GridCtrl.cpp
上传用户:jzscgs158
上传日期:2022-05-25
资源大小:8709k
文件大小:208k
源码类别:

百货/超市行业

开发平台:

Visual C++

  1.             int startCol = nCols - addedCols;
  2.             for (int col = startCol; col < nCols; col++)
  3.                 m_arColWidths[col] = m_cellFixedColDef.GetWidth();
  4.         
  5.             // initialise column data
  6.             if (!GetVirtualMode())
  7.             {
  8.                 for (int row = 0; row < m_nRows; row++)
  9.                     for (col = startCol; col < nCols; col++)
  10.                     {
  11.                         GRID_ROW* pRow = m_RowData[row];
  12.                         if (pRow)
  13.                             pRow->SetAt(col, CreateCell(row, col));
  14.                     }
  15.             }
  16.         }
  17.         // else    // check for selected cell ranges
  18.         //    ResetSelectedRange();
  19.     }
  20.     CATCH (CMemoryException, e)
  21.     {
  22.         e->ReportError();
  23.         bResult = FALSE;
  24.     }
  25.     END_CATCH
  26.     m_nCols = nCols;
  27.     SetModified();
  28.     ResetScrollBars();
  29.     Refresh();
  30.     return bResult;
  31. }
  32. // Insert a column at a given position, or add to end of columns (if nColumn = -1)
  33. int CGridCtrl::InsertColumn(LPCTSTR strHeading,
  34.                             UINT nFormat /* = DT_CENTER|DT_VCENTER|DT_SINGLELINE */,
  35.                             int nColumn  /* = -1 */)
  36. {
  37.     if (nColumn >= 0 && nColumn < m_nFixedCols)
  38.     {
  39.         // TODO: Fix it so column insertion works for in the fixed column area
  40.         ASSERT(FALSE);
  41.         return -1;
  42.     }
  43.     // If the insertion is for a specific column, check it's within range.
  44.     if (nColumn >= 0 && nColumn > GetColumnCount())
  45.         return -1;
  46.     // Force recalculation
  47.     m_idTopLeftCell.col = -1;
  48.     ResetSelectedRange();
  49.     // Gotta be able to at least _see_ some of the column.
  50.     if (m_nRows < 1)
  51.         SetRowCount(1);
  52.     // Allow the user to insert after the last of the columns, but process it as a
  53.     // "-1" column, meaning it gets flaged as being the last column, and not a regular
  54.     // "insert" routine.
  55.     if (nColumn == GetColumnCount())
  56.         nColumn = -1;
  57.     TRY
  58.     {
  59.         if (nColumn < 0)
  60.         {
  61.             nColumn = m_nCols;
  62.             m_arColWidths.Add(0);
  63.             if (!GetVirtualMode())
  64.             {
  65.                 for (int row = 0; row < m_nRows; row++)
  66.                 {
  67.                     GRID_ROW* pRow = m_RowData[row];
  68.                     if (!pRow)
  69.                         return -1;
  70.                     pRow->Add(CreateCell(row, nColumn));
  71.                 }
  72.             }
  73.         } 
  74.         else
  75.         {
  76.             m_arColWidths.InsertAt(nColumn, (int)0);
  77.             if (!GetVirtualMode())
  78.             {
  79.                 for (int row = 0; row < m_nRows; row++) 
  80.                 {
  81.                     GRID_ROW* pRow = m_RowData[row];
  82.                     if (!pRow)
  83.                         return -1;
  84.                     pRow->InsertAt(nColumn, CreateCell(row, nColumn));
  85.                 }
  86.             }
  87.         }
  88.     }
  89.     CATCH (CMemoryException, e)
  90.     {
  91.         e->ReportError();
  92.         return FALSE;
  93.     }
  94.     END_CATCH
  95.     m_nCols++;
  96.     
  97.     // Initialise column data
  98.     SetItemText(0, nColumn, strHeading);
  99.     for (int row = 0; row < m_nRows; row++) 
  100.         SetItemFormat(row, nColumn, nFormat);
  101.     
  102.     // initialized column width
  103.     m_arColWidths[nColumn] = GetTextExtent(0, nColumn, strHeading).cx;
  104.     
  105.     if (m_idCurrentCell.col != -1 && nColumn < m_idCurrentCell.col)
  106.         m_idCurrentCell.col++;
  107.     
  108.     ResetScrollBars();
  109.     SetModified();
  110.     
  111.     return nColumn;
  112. }
  113. // Insert a row at a given position, or add to end of rows (if nRow = -1)
  114. int CGridCtrl::InsertRow(LPCTSTR strHeading, int nRow /* = -1 */)
  115. {
  116.     if (nRow >= 0 && nRow < m_nFixedRows)
  117.     {
  118.         // TODO: Fix it so column insertion works for in the fixed row area
  119.         ASSERT(FALSE);
  120.         return -1;
  121.     }
  122.     // If the insertion is for a specific row, check it's within range.
  123.     if (nRow >= 0 && nRow >= GetRowCount())
  124.         return -1;
  125.     // Force recalculation
  126.     m_idTopLeftCell.col = -1;
  127.     ResetSelectedRange();
  128.     // Gotta be able to at least _see_ some of the row.
  129.     if (m_nCols < 1)
  130.         SetColumnCount(1);
  131.     TRY
  132.     {
  133.         // Adding a row to the bottom
  134.         if (nRow < 0)
  135.         {
  136.             nRow = m_nRows;
  137.             m_arRowHeights.Add(0);
  138.             if (!GetVirtualMode())
  139.                 m_RowData.Add(new GRID_ROW);
  140.         }
  141.         else
  142.         {
  143.             m_arRowHeights.InsertAt(nRow, (int)0);
  144.             if (!GetVirtualMode())
  145.                 m_RowData.InsertAt(nRow, new GRID_ROW);
  146.         }
  147.         if (!GetVirtualMode())
  148.             m_RowData[nRow]->SetSize(m_nCols);
  149.     }
  150.     CATCH (CMemoryException, e)
  151.     {
  152.         e->ReportError();
  153.         return FALSE;
  154.     }
  155.     END_CATCH
  156.     m_nRows++;
  157.     // Initialise cell data
  158.     if (!GetVirtualMode())
  159.     {
  160.         for (int col = 0; col < m_nCols; col++)
  161.         {
  162.             GRID_ROW* pRow = m_RowData[nRow];
  163.             if (!pRow)
  164.                 return -1;
  165.             pRow->SetAt(col, CreateCell(nRow, col));
  166.         }
  167.     }
  168.     // Set row title
  169.     SetItemText(nRow, 0, strHeading);
  170.     // initialized row height
  171.     if (strHeading && strHeading[0])
  172.         m_arRowHeights[nRow] = GetTextExtent(nRow, 0, strHeading).cy;
  173.     else
  174.         m_arRowHeights[nRow] = m_cellFixedRowDef.GetHeight();
  175.     if (m_idCurrentCell.row != -1 && nRow < m_idCurrentCell.row)
  176.         m_idCurrentCell.row++;
  177.     ResetScrollBars();
  178.     SetModified();
  179.     return nRow;
  180. }
  181. ///////////////////////////////////////////////////////////////////////////////
  182. // Cell creation stuff
  183. BOOL CGridCtrl::SetCellType(int nRow, int nCol, CRuntimeClass* pRuntimeClass)
  184. {
  185.     if (GetVirtualMode())
  186.         return FALSE;
  187.     ASSERT(IsValid(nRow, nCol));
  188.     if (!IsValid(nRow, nCol))
  189.         return FALSE;
  190.     if (!pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CGridCellBase)))
  191.     {
  192.         ASSERT( FALSE);
  193.         return FALSE;
  194.     }
  195.     CGridCellBase* pNewCell = (CGridCellBase*) pRuntimeClass->CreateObject();
  196.     CGridCellBase* pCurrCell = GetCell(nRow, nCol);
  197.     if (pCurrCell)
  198.         *pNewCell = *pCurrCell;
  199.     SetCell(nRow, nCol, pNewCell);
  200.     delete pCurrCell;
  201.     return TRUE;
  202. }
  203. BOOL CGridCtrl::SetDefaultCellType( CRuntimeClass* pRuntimeClass)
  204. {
  205.     ASSERT( pRuntimeClass != NULL );
  206.     if (!pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CGridCellBase)))
  207.     {
  208.         ASSERT( FALSE);
  209.         return FALSE;
  210.     }
  211.     m_pRtcDefault = pRuntimeClass;
  212.     return TRUE;
  213. }
  214. // Creates a new grid cell and performs any necessary initialisation
  215. /*virtual*/ CGridCellBase* CGridCtrl::CreateCell(int nRow, int nCol)
  216. {
  217.     ASSERT(!GetVirtualMode());
  218.     if (!m_pRtcDefault || !m_pRtcDefault->IsDerivedFrom(RUNTIME_CLASS(CGridCellBase)))
  219.     {
  220.         ASSERT( FALSE);
  221.         return NULL;
  222.     }
  223.     CGridCellBase* pCell = (CGridCellBase*) m_pRtcDefault->CreateObject();
  224.     if (!pCell)
  225.         return NULL;
  226.     pCell->SetGrid(this);
  227.     pCell->SetCoords(nRow, nCol); 
  228.     // Make format same as cell above
  229.     if (nRow > 0 && nCol >= 0 && nCol < m_nCols)
  230.         pCell->SetFormat(GetItemFormat(nRow - 1, nCol));
  231.     if (nCol < m_nFixedCols)
  232.         pCell->SetState(pCell->GetState() | GVIS_FIXED | GVIS_FIXEDCOL);
  233.     if (nRow < m_nFixedRows)
  234.         pCell->SetState(pCell->GetState() | GVIS_FIXED | GVIS_FIXEDROW);
  235.     
  236.     return pCell;
  237. }
  238. // Performs any cell cleanup necessary to maintain grid integrity
  239. /*virtual*/ void CGridCtrl::DestroyCell(int nRow, int nCol)
  240. {
  241.     // Should NEVER get here in virtual mode.
  242.     ASSERT(!GetVirtualMode());
  243.     // Set the cells state to 0. If the cell is selected, this
  244.     // will remove the cell from the selected list.
  245.     SetItemState(nRow, nCol, 0);
  246.     delete GetCell(nRow, nCol);
  247. }
  248. BOOL CGridCtrl::DeleteColumn(int nColumn)
  249. {
  250.     if (nColumn < 0 || nColumn >= GetColumnCount())
  251.         return FALSE;
  252.     ResetSelectedRange();
  253.     if (!GetVirtualMode())
  254.     {
  255.         for (int row = 0; row < GetRowCount(); row++)
  256.         {
  257.             GRID_ROW* pRow = m_RowData[row];
  258.             if (!pRow)
  259.                 return FALSE;
  260.             DestroyCell(row, nColumn);
  261.         
  262.             pRow->RemoveAt(nColumn);
  263.         }
  264.     }
  265.     m_arColWidths.RemoveAt(nColumn);
  266.     m_nCols--;
  267.     if (nColumn < m_nFixedCols)
  268.         m_nFixedCols--;
  269.     
  270.     if (nColumn == m_idCurrentCell.col)
  271.         m_idCurrentCell.row = m_idCurrentCell.col = -1;
  272.     else if (nColumn < m_idCurrentCell.col)
  273.         m_idCurrentCell.col--;
  274.     
  275.     ResetScrollBars();
  276.     SetModified();
  277.     return TRUE;
  278. }
  279. BOOL CGridCtrl::DeleteRow(int nRow)
  280. {
  281.     if (nRow < 0 || nRow >= GetRowCount())
  282.         return FALSE;
  283.     ResetSelectedRange();
  284.     if (!GetVirtualMode())
  285.     {
  286.         GRID_ROW* pRow = m_RowData[nRow];
  287.         if (!pRow)
  288.             return FALSE;
  289.         for (int col = 0; col < GetColumnCount(); col++)
  290.             DestroyCell(nRow, col);
  291.         delete pRow;
  292.         m_RowData.RemoveAt(nRow);
  293.     }
  294.     m_arRowHeights.RemoveAt(nRow);
  295.     m_nRows--;
  296.     if (nRow < m_nFixedRows)
  297.         m_nFixedRows--;
  298.     
  299.     if (nRow == m_idCurrentCell.row)
  300.         m_idCurrentCell.row = m_idCurrentCell.col = -1;
  301.     else if (nRow < m_idCurrentCell.row)
  302.         m_idCurrentCell.row--;
  303.     
  304.     ResetScrollBars();
  305.     SetModified();
  306.     
  307.     return TRUE;
  308. }
  309. // Handy function that removes all non-fixed rows
  310. BOOL CGridCtrl::DeleteNonFixedRows()
  311. {
  312.     ResetSelectedRange();
  313.     int nFixed = GetFixedRowCount();
  314.     int nCount = GetRowCount();
  315.     // Delete all data rows
  316.     for (int nRow = nCount; nRow >= nFixed; nRow--)
  317.         DeleteRow(nRow);
  318.     return TRUE;
  319. }
  320. // Removes all rows, columns and data from the grid.
  321. BOOL CGridCtrl::DeleteAllItems()
  322. {
  323.     ResetSelectedRange();
  324.     m_arColWidths.RemoveAll();
  325.     m_arRowHeights.RemoveAll();
  326.     // Delete all cells in the grid
  327.     if (!GetVirtualMode())
  328.     {
  329.         for (int row = 0; row < m_nRows; row++)
  330.         {
  331.             for (int col = 0; col < m_nCols; col++)
  332.                 DestroyCell(row, col);
  333.             GRID_ROW* pRow = m_RowData[row];
  334.             delete pRow;
  335.         }
  336.         // Remove all rows
  337.         m_RowData.RemoveAll();
  338.     }
  339.     m_idCurrentCell.row = m_idCurrentCell.col = -1;
  340.     m_nRows = m_nFixedRows = m_nCols = m_nFixedCols = 0;
  341.     ResetScrollBars();
  342.     SetModified();
  343.     return TRUE;
  344. }
  345. void CGridCtrl::AutoFill()
  346. {
  347.     if (!::IsWindow(m_hWnd))
  348.         return;
  349.     CRect rect;
  350.     GetClientRect(rect);
  351.     SetColumnCount(rect.Width() / m_cellDefault.GetWidth() + 1);
  352.     SetRowCount(rect.Height() / m_cellDefault.GetHeight() + 1);
  353.     SetFixedRowCount(1);
  354.     SetFixedColumnCount(1);
  355.     ExpandToFit();
  356. }
  357. /////////////////////////////////////////////////////////////////////////////
  358. // CGridCtrl data functions
  359. // Set CListCtrl::GetNextItem for details
  360. CCellID CGridCtrl::GetNextItem(CCellID& cell, int nFlags) const
  361. {
  362.     if ((nFlags & GVNI_ALL) == GVNI_ALL)
  363.     { // GVNI_ALL Search whole Grid beginning from cell
  364.         //          First row (cell.row) -- ONLY Columns to the right of cell
  365.         //          following rows       -- ALL  Columns
  366.         int row = cell.row , col = cell.col + 1;
  367.         if (row <= 0)
  368.             row = GetFixedRowCount();
  369.         for (; row < GetRowCount(); row++)
  370.         {
  371.             if (col <= 0)
  372.                 col = GetFixedColumnCount();
  373.             for (; col < GetColumnCount(); col++)
  374.             {
  375.                 int nState = GetItemState(row, col);
  376.                 if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) || 
  377.                     (nFlags & GVNI_FOCUSED     && nState & GVIS_FOCUSED)     ||
  378.                     (nFlags & GVNI_SELECTED    && nState & GVIS_SELECTED)    ||
  379.                     (nFlags & GVNI_READONLY    && nState & GVIS_READONLY)    ||
  380.                     (nFlags & GVNI_FIXED       && nState & GVIS_FIXED)       ||
  381.                     (nFlags & GVNI_MODIFIED    && nState & GVIS_MODIFIED))
  382.                     return CCellID(row, col);
  383.             }
  384.             // go to First Column
  385.             col = GetFixedColumnCount();
  386.         }
  387.     }
  388.     else if ((nFlags & GVNI_BELOW) == GVNI_BELOW && 
  389.              (nFlags & GVNI_TORIGHT) == GVNI_TORIGHT)
  390.     {   // GVNI_AREA Search Grid beginning from cell to Lower-Right of Grid
  391.         //           Only rows starting with  cell.row and below
  392.         //           All rows   -- ONLY Columns to the right of cell
  393.         int row = cell.row;
  394.         if (row <= 0)
  395.             row = GetFixedRowCount();
  396.         for (; row < GetRowCount(); row++)
  397.         {
  398.             int col = cell.col + 1;
  399.             if (col <= 0)
  400.                 col = GetFixedColumnCount();
  401.             for (; col < GetColumnCount(); col++) 
  402.             {
  403.                 int nState = GetItemState(row, col);
  404.                 if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) || 
  405.                     (nFlags & GVNI_FOCUSED     && nState & GVIS_FOCUSED)     ||
  406.                     (nFlags & GVNI_SELECTED    && nState & GVIS_SELECTED)    ||
  407.                     (nFlags & GVNI_READONLY    && nState & GVIS_READONLY)    ||
  408.                     (nFlags & GVNI_FIXED       && nState & GVIS_FIXED)       ||
  409.                     (nFlags & GVNI_MODIFIED    && nState & GVIS_MODIFIED))
  410.                     return CCellID(row, col);
  411.             }
  412.         }
  413.     }
  414.     else if ((nFlags & GVNI_ABOVE) == GVNI_ABOVE) 
  415.     {
  416.         for (int row = cell.row - 1; row >= GetFixedRowCount(); row--) 
  417.         {
  418.             int nState = GetItemState(row, cell.col);
  419.             if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) || 
  420.                 (nFlags & GVNI_FOCUSED     && nState & GVIS_FOCUSED)     ||
  421.                 (nFlags & GVNI_SELECTED    && nState & GVIS_SELECTED)    ||
  422.                 (nFlags & GVNI_READONLY    && nState & GVIS_READONLY)    ||
  423.                 (nFlags & GVNI_FIXED       && nState & GVIS_FIXED)       ||
  424.                 (nFlags & GVNI_MODIFIED    && nState & GVIS_MODIFIED))
  425.                 return CCellID(row, cell.col);
  426.         }
  427.     }
  428.     else if ((nFlags & GVNI_BELOW) == GVNI_BELOW)
  429.     {
  430.         for (int row = cell.row + 1; row < GetRowCount(); row++) 
  431.         {
  432.             int nState = GetItemState(row, cell.col);
  433.             if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) || 
  434.                 (nFlags & GVNI_FOCUSED     && nState & GVIS_FOCUSED)     ||
  435.                 (nFlags & GVNI_SELECTED    && nState & GVIS_SELECTED)    ||
  436.                 (nFlags & GVNI_READONLY    && nState & GVIS_READONLY)    ||
  437.                 (nFlags & GVNI_FIXED       && nState & GVIS_FIXED)       ||
  438.                 (nFlags & GVNI_MODIFIED    && nState & GVIS_MODIFIED))
  439.                 return CCellID(row, cell.col);
  440.         }
  441.     } 
  442.     else if ((nFlags & GVNI_TOLEFT) == GVNI_TOLEFT)
  443.     {
  444.         for (int col = cell.col - 1; col >= GetFixedColumnCount(); col--) 
  445.         {
  446.             int nState = GetItemState(cell.row, col);
  447.             if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) || 
  448.                 (nFlags & GVNI_FOCUSED     && nState & GVIS_FOCUSED)     ||
  449.                 (nFlags & GVNI_SELECTED    && nState & GVIS_SELECTED)    ||
  450.                 (nFlags & GVNI_READONLY    && nState & GVIS_READONLY)    ||
  451.                 (nFlags & GVNI_FIXED       && nState & GVIS_FIXED)       ||
  452.                 (nFlags & GVNI_MODIFIED    && nState & GVIS_MODIFIED))
  453.                 return CCellID(cell.row, col);
  454.         }
  455.     }
  456.     else if ((nFlags & GVNI_TORIGHT) == GVNI_TORIGHT)
  457.     {
  458.         for (int col = cell.col + 1; col < GetColumnCount(); col++) 
  459.         {
  460.             int nState = GetItemState(cell.row, col);
  461.             if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) || 
  462.                 (nFlags & GVNI_FOCUSED     && nState & GVIS_FOCUSED)     ||
  463.                 (nFlags & GVNI_SELECTED    && nState & GVIS_SELECTED)    ||
  464.                 (nFlags & GVNI_READONLY    && nState & GVIS_READONLY)    ||
  465.                 (nFlags & GVNI_FIXED       && nState & GVIS_FIXED)       ||
  466.                 (nFlags & GVNI_MODIFIED    && nState & GVIS_MODIFIED))
  467.                 return CCellID(cell.row, col);
  468.         }
  469.     }
  470.     
  471.     return CCellID(-1, -1);
  472. }
  473. // Sorts on a given column using the cell text
  474. BOOL CGridCtrl::SortTextItems(int nCol, BOOL bAscending)
  475. {
  476.     SetSortColumn(nCol);
  477.     SetSortAscending(bAscending);
  478.     ResetSelectedRange();
  479.     SetFocusCell(-1, - 1);
  480.     return SortTextItems(nCol, bAscending, GetFixedRowCount(), - 1);
  481. }
  482. // recursive sort implementation
  483. BOOL CGridCtrl::SortTextItems(int nCol, BOOL bAscending, int low, int high)
  484. {
  485.     if (nCol >= GetColumnCount())
  486.         return FALSE;
  487.     if (high == -1)
  488.         high = GetRowCount() - 1;
  489.     int lo = low;
  490.     int hi = high;
  491.     if (hi <= lo)
  492.         return FALSE;
  493.     
  494.     CString midItem = GetItemText((lo + hi)/2, nCol);
  495.     
  496.     // loop through the list until indices cross
  497.     while (lo <= hi)
  498.     {
  499.         // Find the first element that is greater than or equal to the partition 
  500.         // element starting from the left Index.
  501.         if (bAscending)
  502.             while (lo < high  && GetItemText(lo, nCol) < midItem)
  503.                 ++lo;
  504.             else
  505.                 while (lo < high && GetItemText(lo, nCol) > midItem)
  506.                     ++lo;
  507.                 
  508.                 // Find an element that is smaller than or equal to  the partition 
  509.                 // element starting from the right Index.
  510.                 if (bAscending)
  511.                     while (hi > low && GetItemText(hi, nCol) > midItem)
  512.                         --hi;
  513.                     else
  514.                         while (hi > low && GetItemText(hi, nCol) < midItem)
  515.                             --hi;
  516.                         
  517.                         // If the indexes have not crossed, swap if the items are not equal
  518.                         if (lo <= hi)
  519.                         {
  520.                             // swap only if the items are not equal
  521.                             if (GetItemText(lo, nCol) != GetItemText(hi, nCol))
  522.                             {
  523.                                 for (int col = 0; col < GetColumnCount(); col++)
  524.                                 {
  525.                                     CGridCellBase *pCell = GetCell(lo, col);
  526.                                     SetCell(lo, col, GetCell(hi, col));
  527.                                     SetCell(hi, col, pCell);
  528.                                 }
  529.                                 UINT nRowHeight = m_arRowHeights[lo];
  530.                                 m_arRowHeights[lo] = m_arRowHeights[hi];
  531.                                 m_arRowHeights[hi] = nRowHeight;
  532.                             }
  533.                             
  534.                             ++lo;
  535.                             --hi;
  536.                         }
  537.     }
  538.     
  539.     // If the right index has not reached the left side of array
  540.     // must now sort the left partition.
  541.     if (low < hi)
  542.         SortTextItems(nCol, bAscending, low, hi);
  543.     
  544.     // If the left index has not reached the right side of array
  545.     // must now sort the right partition.
  546.     if (lo < high)
  547.         SortTextItems(nCol, bAscending, lo, high);
  548.     
  549.     return TRUE;
  550. }
  551. // Sorts on a given column using the supplied compare function (see CListCtrl::SortItems)
  552. BOOL CGridCtrl::SortItems(PFNLVCOMPARE pfnCompare, int nCol, BOOL bAscending,
  553.                           LPARAM data /* = 0 */)
  554. {
  555.     SetSortColumn(nCol);
  556.     SetSortAscending(bAscending);
  557.     ResetSelectedRange();
  558.     SetFocusCell(-1, - 1);
  559.     return SortItems(pfnCompare, nCol, bAscending, data, GetFixedRowCount(), -1);
  560. }
  561. // recursive sort implementation
  562. BOOL CGridCtrl::SortItems(PFNLVCOMPARE pfnCompare, int nCol, BOOL bAscending, LPARAM data,
  563.                           int low, int high)
  564. {
  565.     if (nCol >= GetColumnCount())
  566.         return FALSE;
  567.     if (high == -1)
  568.         high = GetRowCount() - 1;
  569.     int lo = low;
  570.     int hi = high;
  571.     
  572.     if (hi <= lo)
  573.         return FALSE;
  574.     
  575.     LPARAM midItem = GetItemData((lo + hi)/2, nCol);
  576.     
  577.     // loop through the list until indices cross
  578.     while (lo <= hi)
  579.     {
  580.         // Find the first element that is greater than or equal to the partition 
  581.         // element starting from the left Index.
  582.         if (bAscending)
  583.             while (lo < high  && pfnCompare(GetItemData(lo, nCol), midItem, data) < 0)
  584.                 ++lo;
  585.             else
  586.                 while (lo < high && pfnCompare(GetItemData(lo, nCol), midItem, data) > 0)
  587.                     ++lo;
  588.                 
  589.                 // Find an element that is smaller than or equal to  the partition 
  590.                 // element starting from the right Index.
  591.                 if (bAscending)
  592.                     while (hi > low && pfnCompare(GetItemData(hi, nCol), midItem, data) > 0)
  593.                         --hi;
  594.                     else
  595.                         while (hi > low && pfnCompare(GetItemData(hi, nCol), midItem, data) < 0)
  596.                             --hi;
  597.                         
  598.                         // If the indexes have not crossed, swap if the items are not equal
  599.                         if (lo <= hi)
  600.                         {
  601.                             // swap only if the items are not equal
  602.                             if (pfnCompare(GetItemData(lo, nCol), GetItemData(hi, nCol), data) != 0)
  603.                             {
  604.                                 for (int col = 0; col < GetColumnCount(); col++)
  605.                                 {
  606.                                     CGridCellBase *pCell = GetCell(lo, col);
  607.                                     SetCell(lo, col, GetCell(hi, col));
  608.                                     SetCell(hi, col, pCell);
  609.                                 }
  610.                                 UINT nRowHeight = m_arRowHeights[lo];
  611.                                 m_arRowHeights[lo] = m_arRowHeights[hi];
  612.                                 m_arRowHeights[hi] = nRowHeight;
  613.                             }
  614.                             
  615.                             ++lo;
  616.                             --hi;
  617.                         }
  618.     }
  619.     
  620.     // If the right index has not reached the left side of array
  621.     // must now sort the left partition.
  622.     if (low < hi)
  623.         SortItems(pfnCompare, nCol, bAscending, data, low, hi);
  624.     
  625.     // If the left index has not reached the right side of array
  626.     // must now sort the right partition.
  627.     if (lo < high)
  628.         SortItems(pfnCompare, nCol, bAscending, data, lo, high);
  629.     
  630.     return TRUE;
  631. }
  632. /////////////////////////////////////////////////////////////////////////////
  633. // CGridCtrl data functions
  634. BOOL CGridCtrl::SetItem(const GV_ITEM* pItem)
  635. {
  636.     if (!pItem || GetVirtualMode())
  637.         return FALSE;
  638.     CGridCellBase* pCell = GetCell(pItem->row, pItem->col);
  639.     if (!pCell)
  640.         return FALSE;
  641.     SetModified(pItem->row, pItem->col);
  642.     if (pItem->mask & GVIF_TEXT)
  643.         pCell->SetText(pItem->strText);
  644.     if (pItem->mask & GVIF_PARAM)
  645.         pCell->SetData(pItem->lParam);
  646.     if (pItem->mask & GVIF_IMAGE)
  647.         pCell->SetImage(pItem->iImage);
  648.     if (pItem->mask & GVIF_STATE)
  649.         pCell->SetState(pItem->nState);
  650.     if (pItem->mask & GVIF_FORMAT)
  651.         pCell->SetFormat(pItem->nFormat);
  652.     if (pItem->mask & GVIF_BKCLR)
  653.         pCell->SetBackClr(pItem->crBkClr);
  654.     if (pItem->mask & GVIF_FGCLR)
  655.         pCell->SetTextClr(pItem->crFgClr);
  656.     if (pItem->mask & GVIF_FONT)
  657.         pCell->SetFont(&(pItem->lfFont));
  658.     if( pItem->mask & GVIF_MARGIN)
  659.         pCell->SetMargin( pItem->nMargin);
  660.     
  661.     return TRUE;
  662. }
  663. BOOL CGridCtrl::GetItem(GV_ITEM* pItem)
  664. {
  665.     if (!pItem)
  666.         return FALSE;
  667.     CGridCellBase* pCell = GetCell(pItem->row, pItem->col);
  668.     if (!pCell)
  669.         return FALSE;
  670.     if (pItem->mask & GVIF_TEXT)
  671.         pItem->strText = GetItemText(pItem->row, pItem->col);
  672.     if (pItem->mask & GVIF_PARAM)
  673.         pItem->lParam  = pCell->GetData();;
  674.     if (pItem->mask & GVIF_IMAGE)
  675.         pItem->iImage  = pCell->GetImage();
  676.     if (pItem->mask & GVIF_STATE)
  677.         pItem->nState  = pCell->GetState();
  678.     if (pItem->mask & GVIF_FORMAT)
  679.         pItem->nFormat = pCell->GetFormat();
  680.     if (pItem->mask & GVIF_BKCLR)
  681.         pItem->crBkClr = pCell->GetBackClr();
  682.     if (pItem->mask & GVIF_FGCLR)
  683.         pItem->crFgClr = pCell->GetTextClr();
  684.     if (pItem->mask & GVIF_FONT)
  685.         memcpy(&(pItem->lfFont), pCell->GetFont(), sizeof(LOGFONT));
  686.     if( pItem->mask & GVIF_MARGIN)
  687.         pItem->nMargin = pCell->GetMargin();
  688.     return TRUE;
  689. }
  690. BOOL CGridCtrl::SetItemText(int nRow, int nCol, LPCTSTR str)
  691. {
  692.     if (GetVirtualMode())
  693.         return FALSE;
  694.     CGridCellBase* pCell = GetCell(nRow, nCol);
  695.     if (!pCell)
  696.         return FALSE;
  697.     pCell->SetText(str);
  698.     SetModified(TRUE, nRow, nCol);
  699.     return TRUE;
  700. }
  701. #if (_WIN32_WCE >= 210)
  702. // EFW - 06/13/99 - Added to support printf-style formatting codes
  703. BOOL CGridCtrl::SetItemTextFmt(int nRow, int nCol, LPCTSTR szFmt, ...)
  704. {
  705.     if (GetVirtualMode())
  706.         return FALSE;
  707.     CString strText;
  708.     va_list argptr;
  709.     CGridCellBase* pCell = GetCell(nRow, nCol);
  710.     if (!pCell)
  711.         return FALSE;
  712.     // Format the message text
  713.     va_start(argptr, szFmt);
  714.     strText.FormatV(szFmt, argptr);
  715.     va_end(argptr);
  716.     pCell->SetText(strText);
  717.     SetModified(TRUE, nRow, nCol);
  718.     return TRUE;
  719. }
  720. // EFW - 06/13/99 - Added to support string resource ID.  Supports
  721. // a variable argument list too.
  722. BOOL CGridCtrl::SetItemTextFmtID(int nRow, int nCol, UINT nID, ...)
  723. {
  724.     if (GetVirtualMode())
  725.         return FALSE;
  726.     CString strFmt, strText;
  727.     va_list argptr;
  728.     CGridCellBase* pCell = GetCell(nRow, nCol);
  729.     if (!pCell)
  730.         return FALSE;
  731.     // Format the message text
  732.     va_start(argptr, nID);
  733.     VERIFY(strFmt.LoadString(nID));
  734.     strText.FormatV(strFmt, argptr);
  735.     va_end(argptr);
  736.     pCell->SetText(strText);
  737.     SetModified(TRUE, nRow, nCol);
  738.     return TRUE;
  739. }
  740. #endif
  741. BOOL CGridCtrl::SetItemData(int nRow, int nCol, LPARAM lParam)
  742. {
  743.     if (GetVirtualMode())
  744.         return FALSE;
  745.     CGridCellBase* pCell = GetCell(nRow, nCol);
  746.     if (!pCell)
  747.         return FALSE;
  748.     pCell->SetData(lParam);
  749.     SetModified(TRUE, nRow, nCol);
  750.     return TRUE;
  751. }
  752. LPARAM CGridCtrl::GetItemData(int nRow, int nCol) const
  753. {
  754.     CGridCellBase* pCell = GetCell(nRow, nCol);
  755.     if (!pCell)
  756.         return (LPARAM) 0;
  757.     return pCell->GetData();
  758. }
  759. BOOL CGridCtrl::SetItemImage(int nRow, int nCol, int iImage)
  760. {
  761.     if (GetVirtualMode())
  762.         return FALSE;
  763.     CGridCellBase* pCell = GetCell(nRow, nCol);
  764.     if (!pCell)
  765.         return FALSE;
  766.     pCell->SetImage(iImage);
  767.     SetModified(TRUE, nRow, nCol);
  768.     return TRUE;
  769. }
  770. int CGridCtrl::GetItemImage(int nRow, int nCol) const
  771. {
  772.     CGridCellBase* pCell = GetCell(nRow, nCol);
  773.     ASSERT(pCell);
  774.     if (!pCell)
  775.         return -1;
  776.     return pCell->GetImage();
  777. }
  778. BOOL CGridCtrl::SetItemState(int nRow, int nCol, UINT state)
  779. {
  780.     BOOL bSelected = IsCellSelected(nRow, nCol);
  781.     // If the cell is being unselected, remove it from the selected list
  782.     if (bSelected && !(state & GVIS_SELECTED))
  783.     {
  784.         CCellID cell;
  785.         DWORD key = MAKELONG(nRow, nCol);
  786.         if (m_SelectedCellMap.Lookup(key, (CCellID&)cell))
  787.             m_SelectedCellMap.RemoveKey(key);
  788.     }
  789.     // If cell is being selected, add it to the list of selected cells
  790.     else if (!bSelected && (state & GVIS_SELECTED))
  791.     {
  792.         CCellID cell(nRow, nCol);
  793.         m_SelectedCellMap.SetAt(MAKELONG(nRow, nCol), cell);
  794.     }
  795.     if (GetVirtualMode())
  796.         return FALSE;
  797.     CGridCellBase* pCell = GetCell(nRow, nCol);
  798.     ASSERT(pCell);
  799.     if (!pCell)
  800.         return FALSE;
  801.     // Set the cell's state
  802.     pCell->SetState(state);
  803.     return TRUE;
  804. }
  805. UINT CGridCtrl::GetItemState(int nRow, int nCol) const
  806. {
  807.     CGridCellBase* pCell = GetCell(nRow, nCol);
  808.     ASSERT(pCell);
  809.     if (!pCell)
  810.         return 0;
  811.     return pCell->GetState();
  812. }
  813. BOOL CGridCtrl::SetItemFormat(int nRow, int nCol, UINT nFormat)
  814. {
  815.     if (GetVirtualMode())
  816.         return FALSE;
  817.     CGridCellBase* pCell = GetCell(nRow, nCol);
  818.     ASSERT(pCell);
  819.     if (!pCell)
  820.         return FALSE;
  821.     pCell->SetFormat(nFormat);
  822.     return TRUE;
  823. }
  824. UINT CGridCtrl::GetItemFormat(int nRow, int nCol) const
  825. {
  826.     CGridCellBase* pCell = GetCell(nRow, nCol);
  827.     ASSERT(pCell);
  828.     if (!pCell)
  829.         return 0;
  830.     return pCell->GetFormat();
  831. }
  832. BOOL CGridCtrl::SetItemBkColour(int nRow, int nCol, COLORREF cr /* = CLR_DEFAULT */)
  833. {
  834.     if (GetVirtualMode())
  835.         return FALSE;
  836.     CGridCellBase* pCell = GetCell(nRow, nCol);
  837.     ASSERT(pCell);
  838.     if (!pCell)
  839.         return FALSE;
  840.     pCell->SetBackClr(cr);
  841.     return TRUE;
  842. }
  843. COLORREF CGridCtrl::GetItemBkColour(int nRow, int nCol) const
  844. {
  845.     CGridCellBase* pCell = GetCell(nRow, nCol);
  846.     ASSERT(pCell);
  847.     if (!pCell)
  848.         return 0;
  849.     return pCell->GetBackClr();
  850. }
  851. BOOL CGridCtrl::SetItemFgColour(int nRow, int nCol, COLORREF cr /* = CLR_DEFAULT */)
  852. {
  853.     if (GetVirtualMode())
  854.         return FALSE;
  855.     CGridCellBase* pCell = GetCell(nRow, nCol);
  856.     ASSERT(pCell);
  857.     if (!pCell)
  858.         return FALSE;
  859.     
  860.     pCell->SetTextClr(cr);
  861.     return TRUE;
  862. }
  863. COLORREF CGridCtrl::GetItemFgColour(int nRow, int nCol) const
  864. {
  865.     CGridCellBase* pCell = GetCell(nRow, nCol);
  866.     ASSERT(pCell);
  867.     if (!pCell)
  868.         return 0;
  869.     
  870.     return pCell->GetTextClr();
  871. }
  872. BOOL CGridCtrl::SetItemFont(int nRow, int nCol, const LOGFONT* plf)
  873. {
  874.     if (GetVirtualMode())
  875.         return FALSE;
  876.     CGridCellBase* pCell = GetCell(nRow, nCol);
  877.     ASSERT(pCell);
  878.     if (!pCell)
  879.         return FALSE;
  880.     
  881.     pCell->SetFont(plf);
  882.     
  883.     return TRUE;
  884. }
  885. const LOGFONT* CGridCtrl::GetItemFont(int nRow, int nCol)
  886. {
  887.     CGridCellBase* pCell = GetCell(nRow, nCol);
  888.     ASSERT(pCell);
  889.     if (!pCell) 
  890.         return GetDefaultCell(nRow < GetFixedRowCount(), nCol < GetFixedColumnCount())->GetFont();
  891.     
  892.     return pCell->GetFont();
  893. }
  894. BOOL CGridCtrl::IsItemEditing(int nRow, int nCol)
  895. {
  896.     CGridCellBase* pCell = GetCell(nRow, nCol);
  897.     ASSERT(pCell);
  898.     if (!pCell)
  899.         return FALSE;
  900.     return pCell->IsEditing();
  901. }
  902. ////////////////////////////////////////////////////////////////////////////////////
  903. // Row/Column size functions
  904. long CGridCtrl::GetVirtualWidth() const
  905. {
  906.     long lVirtualWidth = 0;
  907.     int iColCount = GetColumnCount();
  908.     for (int i = 0; i < iColCount; i++)
  909.         lVirtualWidth += m_arColWidths[i];
  910.     return lVirtualWidth;
  911. }
  912. long CGridCtrl::GetVirtualHeight() const
  913. {
  914.     long lVirtualHeight = 0;
  915.     int iRowCount = GetRowCount();
  916.     for (int i = 0; i < iRowCount; i++)
  917.         lVirtualHeight += m_arRowHeights[i];
  918.     return lVirtualHeight;
  919. }
  920. int CGridCtrl::GetRowHeight(int nRow) const
  921. {
  922.     ASSERT(nRow >= 0 && nRow < m_nRows);
  923.     if (nRow < 0 || nRow >= m_nRows)
  924.         return -1;
  925.     return m_arRowHeights[nRow];
  926. }
  927. int CGridCtrl::GetColumnWidth(int nCol) const
  928. {
  929.     ASSERT(nCol >= 0 && nCol < m_nCols);
  930.     if (nCol < 0 || nCol >= m_nCols)
  931.         return -1;
  932.     return m_arColWidths[nCol];
  933. }
  934. BOOL CGridCtrl::SetRowHeight(int nRow, int height)
  935. {
  936.     ASSERT(nRow >= 0 && nRow < m_nRows && height >= 0);
  937.     if (nRow < 0 || nRow >= m_nRows || height < 0)
  938.         return FALSE;
  939.     m_arRowHeights[nRow] = height;
  940.     ResetScrollBars();
  941.     return TRUE;
  942. }
  943. BOOL CGridCtrl::SetColumnWidth(int nCol, int width)
  944. {
  945.     ASSERT(nCol >= 0 && nCol < m_nCols && width >= 0);
  946.     if (nCol < 0 || nCol >= m_nCols || width < 0)
  947.         return FALSE;
  948.     m_arColWidths[nCol] = width;
  949.     ResetScrollBars();
  950.     return TRUE;
  951. }
  952. int CGridCtrl::GetFixedRowHeight() const
  953. {
  954.     int nHeight = 0;
  955.     for (int i = 0; i < m_nFixedRows; i++)
  956.         nHeight += GetRowHeight(i);
  957.     return nHeight;
  958. }
  959. int CGridCtrl::GetFixedColumnWidth() const
  960. {
  961.     int nWidth = 0;
  962.     for (int i = 0; i < m_nFixedCols; i++)
  963.         nWidth += GetColumnWidth(i);
  964.     return nWidth;
  965. }
  966. BOOL CGridCtrl::AutoSizeColumn(int nCol, UINT nAutoSizeStyle /*=GVS_DEFAULT*/, 
  967.                                BOOL bResetScroll /*=TRUE*/)
  968. {
  969.     ASSERT(nCol >= 0 && nCol < m_nCols);
  970.     if (nCol < 0 || nCol >= m_nCols)
  971.         return FALSE;
  972.     //  Skip hidden columns when autosizing
  973.     if( GetColumnWidth( nCol) <=0 )
  974.         return FALSE;
  975.     CSize size;
  976.     CDC* pDC = GetDC();
  977.     if (!pDC)
  978.         return FALSE;
  979.     int nWidth = 0;
  980.     ASSERT(GVS_DEFAULT <= nAutoSizeStyle && nAutoSizeStyle <= GVS_BOTH);
  981.     if (nAutoSizeStyle == GVS_DEFAULT)
  982.         nAutoSizeStyle = GetAutoSizeStyle();
  983.     int nStartRow = (nAutoSizeStyle & GVS_HEADER)? 0 : GetFixedRowCount();
  984.     int nEndRow   = (nAutoSizeStyle & GVS_DATA)? GetRowCount()-1 : GetFixedRowCount()-1;
  985.     for (int nRow = nStartRow; nRow <= nEndRow; nRow++)
  986.     {
  987.         CGridCellBase* pCell = GetCell(nRow, nCol);
  988.         if (pCell)
  989.             size = pCell->GetCellExtent(pDC);
  990.         if (size.cx > nWidth)
  991.             nWidth = size.cx;
  992.     }
  993.     m_arColWidths[nCol] = nWidth;
  994.     ReleaseDC(pDC);
  995.     if (bResetScroll)
  996.         ResetScrollBars();
  997.     return TRUE;
  998. }
  999. BOOL CGridCtrl::AutoSizeRow(int nRow, BOOL bResetScroll /*=TRUE*/)
  1000. {
  1001.     ASSERT(nRow >= 0 && nRow < m_nRows);
  1002.     if (nRow < 0 || nRow >= m_nRows)
  1003.         return FALSE;
  1004.     //  Skip hidden rows when autosizing
  1005.     if( GetRowHeight( nRow) <=0 )
  1006.         return FALSE;
  1007.     CSize size;
  1008.     CDC* pDC = GetDC();
  1009.     if (!pDC)
  1010.         return FALSE;
  1011.     int nHeight = 0;
  1012.     int nNumColumns = GetColumnCount();
  1013.     for (int nCol = 0; nCol < nNumColumns; nCol++)
  1014.     {
  1015.         CGridCellBase* pCell = GetCell(nRow, nCol);
  1016.         if (pCell)
  1017.             size = pCell->GetCellExtent(pDC);
  1018.         if (size.cy > nHeight)
  1019.             nHeight = size.cy;
  1020.     }
  1021.     m_arRowHeights[nRow] = nHeight;
  1022.     ReleaseDC(pDC);
  1023.     if (bResetScroll)
  1024.         ResetScrollBars();
  1025.     return TRUE;
  1026. }
  1027. void CGridCtrl::AutoSizeColumns(UINT nAutoSizeStyle /*=GVS_DEFAULT*/)
  1028. {
  1029.     int nNumColumns = GetColumnCount();
  1030.     for (int nCol = 0; nCol < nNumColumns; nCol++)
  1031.     {
  1032.         //  Skip hidden columns when autosizing
  1033.         if( GetColumnWidth( nCol) > 0 )
  1034.             AutoSizeColumn(nCol, nAutoSizeStyle, FALSE);
  1035.     }
  1036.     ResetScrollBars();
  1037. }
  1038. void CGridCtrl::AutoSizeRows()
  1039. {
  1040.     int nNumRows = GetRowCount();
  1041.     for (int nRow = 0; nRow < nNumRows; nRow++)
  1042.     {
  1043.         //  Skip hidden rows when autosizing
  1044.         if( GetRowHeight( nRow) > 0 )
  1045.             AutoSizeRow(nRow, FALSE);
  1046.     }
  1047.     ResetScrollBars();
  1048. }
  1049. // sizes all rows and columns
  1050. // faster than calling both AutoSizeColumns() and AutoSizeRows()
  1051. void CGridCtrl::AutoSize(UINT nAutoSizeStyle /*=GVS_DEFAULT*/)
  1052. {
  1053.     CDC* pDC = GetDC();
  1054.     if (!pDC)
  1055.         return;
  1056.     int nNumColumns = GetColumnCount();
  1057.     int nCol, nRow;
  1058.     ASSERT(GVS_DEFAULT <= nAutoSizeStyle && nAutoSizeStyle <= GVS_BOTH);
  1059.     if (nAutoSizeStyle == GVS_DEFAULT)
  1060.         nAutoSizeStyle = GetAutoSizeStyle();
  1061.     int nStartRow = (nAutoSizeStyle & GVS_HEADER)? 0 : GetFixedRowCount();
  1062.     int nEndRow   = (nAutoSizeStyle & GVS_DATA)? GetRowCount()-1 : GetFixedRowCount()-1;
  1063.     // Row initialisation - only work on rows whose height is > 0
  1064.     for (nRow = nStartRow; nRow <= nEndRow; nRow++)
  1065.     {
  1066.         if( GetRowHeight( nRow) > 0 )
  1067.             m_arRowHeights[nRow] = 1;
  1068.     }
  1069.     CSize size;
  1070.     for (nCol = 0; nCol < nNumColumns; nCol++)
  1071.     {
  1072.         //  Don't size hidden columns or rows
  1073.         if( GetColumnWidth( nCol) > 0 )
  1074.         {
  1075.             // Skip columns that are hidden, but now initialize
  1076.             m_arColWidths[nCol] = 0;
  1077.             for (nRow = nStartRow; nRow <= nEndRow; nRow++)
  1078.             {
  1079.                 if( GetRowHeight( nRow) > 0 )
  1080.                 {
  1081.                     CGridCellBase* pCell = GetCell(nRow, nCol);
  1082.                     if (pCell)
  1083.                         size = pCell->GetCellExtent(pDC);
  1084.                     if (size.cx >(int) m_arColWidths[nCol])
  1085.                         m_arColWidths[nCol] = size.cx;
  1086.                     if (size.cy >(int) m_arRowHeights[nRow])
  1087.                         m_arRowHeights[nRow] = size.cy;
  1088.                 }
  1089.             }
  1090.         }
  1091.     }
  1092.     ReleaseDC(pDC);
  1093.     ResetScrollBars();
  1094.     Refresh();
  1095. }
  1096. void CGridCtrl::ExpandColumnsToFit()
  1097. {
  1098.     if (GetColumnCount() <= 0)
  1099.         return;
  1100.     EnableScrollBars(SB_BOTH, FALSE);
  1101.     CRect rect;
  1102.     GetClientRect(rect);
  1103.     long virtualWidth = GetVirtualWidth();
  1104.     int nDifference = rect.Width() -(int) virtualWidth;
  1105.     int nColumnAdjustment = nDifference / GetColumnCount();
  1106.     for (int i = 0; i < GetColumnCount(); i++)
  1107.         m_arColWidths[i] += nColumnAdjustment;
  1108.     if (nDifference > 0)
  1109.     {
  1110.         int leftOver = nDifference % GetColumnCount();
  1111.         for (i = 0; i < leftOver; i++)
  1112.             m_arColWidths[i] += 1;
  1113.     }
  1114.     else 
  1115.     {
  1116.         int leftOver = (-nDifference) % GetColumnCount();
  1117.         for (i = 0; i < leftOver; i++)
  1118.             m_arColWidths[i] -= 1;
  1119.     }
  1120.     Refresh();
  1121.     ResetScrollBars();
  1122. }
  1123. void CGridCtrl::ExpandLastColumn()
  1124. {
  1125.     if (GetColumnCount() <= 0)
  1126.         return;
  1127.     EnableScrollBars(SB_BOTH, FALSE);
  1128.     CRect rect;
  1129.     GetClientRect(rect);
  1130.     long virtualWidth = GetVirtualWidth();
  1131.     int nDifference = rect.Width() -(int) virtualWidth;
  1132.     if (nDifference > 0)
  1133.     {
  1134.         if (GetVirtualHeight() > rect.Height())
  1135.             nDifference -= GetSystemMetrics(SM_CYHSCROLL);
  1136.         m_arColWidths[ GetColumnCount()-1 ] += nDifference;
  1137.         Refresh();
  1138.     }
  1139.     ResetScrollBars();
  1140. }
  1141. void CGridCtrl::ExpandRowsToFit()
  1142. {
  1143.     if (GetRowCount() <= 0)
  1144.         return;
  1145.     EnableScrollBars(SB_BOTH, FALSE); 
  1146.     CRect rect;
  1147.     GetClientRect(rect);
  1148.     
  1149.     long virtualHeight = GetVirtualHeight();
  1150.     int nDifference = rect.Height() -(int) virtualHeight;
  1151.     int nRowAdjustment = nDifference / GetRowCount();
  1152.     
  1153.     for (int i = 0; i < GetRowCount(); i++)
  1154.         m_arRowHeights[i] += nRowAdjustment;
  1155.     
  1156.     if (nDifference > 0)
  1157.     {
  1158.         int leftOver = nDifference % GetRowCount();
  1159.         for (i = 0; i < leftOver; i++)
  1160.             m_arRowHeights[i] += 1;
  1161.     } 
  1162.     else 
  1163.     {
  1164.         int leftOver = (-nDifference) % GetRowCount();
  1165.         for (i = 0; i < leftOver; i++)
  1166.             m_arRowHeights[i] -= 1;
  1167.     }
  1168.     Refresh();
  1169.     ResetScrollBars();
  1170. }
  1171. void CGridCtrl::ExpandToFit()
  1172. {
  1173.     ExpandColumnsToFit();   // This will remove any existing horz scrollbar
  1174.     ExpandRowsToFit();      // This will remove any existing vert scrollbar
  1175.     ExpandColumnsToFit();   // Just in case the first adjustment was with a vert
  1176.                             // scrollbar in place
  1177.     Refresh();
  1178. }
  1179. /////////////////////////////////////////////////////////////////////////////////////
  1180. // Attributes
  1181. void CGridCtrl::SetVirtualMode(BOOL bVirtual)
  1182. {
  1183.     DeleteAllItems();
  1184.     m_bVirtualMode = bVirtual;
  1185.     // Force some defaults here.
  1186.     if (m_bVirtualMode)
  1187.     {
  1188.         SetEditable(FALSE);
  1189.         SetHeaderSort(FALSE);
  1190.         SetAutoSizeStyle(GVS_HEADER);
  1191.         SetFixedColumnSelection(FALSE);
  1192.         SetFixedRowSelection(FALSE);
  1193.     }
  1194. }
  1195. void CGridCtrl::SetGridLines(int nWhichLines /*=GVL_BOTH*/) 
  1196. {
  1197.     m_nGridLines = nWhichLines;
  1198.     Refresh();
  1199. }
  1200. void CGridCtrl::SetListMode(BOOL bEnableListMode /*=TRUE*/)
  1201. {
  1202.     ResetSelectedRange();
  1203.     SetSortColumn(-1);
  1204.     m_bListMode = bEnableListMode;
  1205.     SetFixedRowSelection(FALSE);
  1206.     Refresh();
  1207. }
  1208. void CGridCtrl::SetSortColumn(int nCol)
  1209. {
  1210.     if (m_nSortColumn >= 0)
  1211.         InvalidateCellRect(0, m_nSortColumn);
  1212.     m_nSortColumn = nCol;
  1213.     if (nCol >= 0)
  1214.         InvalidateCellRect(0, nCol);
  1215. }
  1216. BOOL CGridCtrl::IsCellFixed(int nRow, int nCol)
  1217. {
  1218.     return (nRow < GetFixedRowCount() || nCol < GetFixedColumnCount());
  1219. }
  1220. void CGridCtrl::SetModified(BOOL bModified /*=TRUE*/, int nRow /*=-1*/, int nCol /*=-1*/)
  1221. {
  1222.     // Cannot guarantee sorting anymore...
  1223.     if (nCol < 0 || nCol == GetSortColumn())
  1224.         SetSortColumn(-1);
  1225.     if (nRow > 0 && nCol > 0)
  1226.     {
  1227.         if (bModified)
  1228.         {
  1229.             SetItemState(nRow, nCol, GetItemState(nRow, nCol) | GVIS_MODIFIED);
  1230.             m_bModified = TRUE;
  1231.         }
  1232.         else
  1233.             SetItemState(nRow, nCol, GetItemState(nRow, nCol) & ~GVIS_MODIFIED);
  1234.     }
  1235.     else
  1236.         m_bModified = bModified;
  1237.     if (!m_bModified)
  1238.     {
  1239.         for (int row = 0; row < GetRowCount(); row++)
  1240.             for (int col = 0; col < GetColumnCount(); col++)
  1241.                 SetItemState(row, col, GetItemState(row, col) & ~GVIS_MODIFIED);
  1242.     }
  1243. }
  1244. BOOL CGridCtrl::GetModified(int nRow /*=-1*/, int nCol /*=-1*/)
  1245. {
  1246.     if (nRow > 0 && nCol > 0)
  1247.         return ( (GetItemState(nRow, nCol) & GVIS_MODIFIED) == GVIS_MODIFIED );
  1248.     else
  1249.         return m_bModified;
  1250. }
  1251. /////////////////////////////////////////////////////////////////////////////////////
  1252. // GridCtrl cell visibility tests and invalidation/redraw functions
  1253. void CGridCtrl::Refresh()
  1254. {
  1255.     if (GetSafeHwnd() && m_bAllowDraw)
  1256.         Invalidate();
  1257. }
  1258. // EnsureVisible supplied by Roelf Werkman
  1259. void CGridCtrl::EnsureVisible(int nRow, int nCol)
  1260. {
  1261.     CCellRange VisibleCells = GetVisibleNonFixedCellRange();
  1262.     int right = nCol - VisibleCells.GetMaxCol();
  1263.     int left  = VisibleCells.GetMinCol() - nCol;
  1264.     int down  = nRow - VisibleCells.GetMaxRow();
  1265.     int up    = VisibleCells.GetMinRow() - nRow;
  1266.     int iColumnStart;
  1267.     int iRowStart;
  1268.     iColumnStart = VisibleCells.GetMaxCol() + 1;
  1269.     while( right > 0 )
  1270.     {
  1271.         if( GetColumnWidth( iColumnStart ) > 0 )
  1272.             SendMessage( WM_HSCROLL, SB_LINERIGHT, 0 );
  1273.         right--;
  1274.         iColumnStart++;
  1275.     }
  1276.     iColumnStart = VisibleCells.GetMinCol() - 1;
  1277.     while( left > 0 )
  1278.     {
  1279.         if( GetColumnWidth( iColumnStart ) > 0 )
  1280.             SendMessage( WM_HSCROLL, SB_LINELEFT, 0 );
  1281.         left--;
  1282.         iColumnStart--;
  1283.     }
  1284.     iRowStart = VisibleCells.GetMaxRow() + 1;
  1285.     while( down > 0 )
  1286.     {
  1287.         if( GetRowHeight( iRowStart ) > 0 )
  1288.             SendMessage( WM_VSCROLL, SB_LINEDOWN, 0 );
  1289.         down--;
  1290.         iRowStart++;
  1291.     }
  1292.     iRowStart = VisibleCells.GetMinRow() - 1;
  1293.     while( up > 0 )
  1294.     {
  1295.         if( GetRowHeight( iRowStart ) > 0 )
  1296.             SendMessage( WM_VSCROLL, SB_LINEUP, 0 );
  1297.         up--;
  1298.         iRowStart--;
  1299.     }
  1300.     // Move one more if we only see a snall bit of the cell
  1301.     CRect rectCell, rectWindow;
  1302.     if (!GetCellRect(nRow, nCol, rectCell))
  1303.         return;
  1304.     GetClientRect(rectWindow);
  1305.     // The previous fix was fixed properly by Martin Richter <martin.richter@grutzeck.de>
  1306.     while (rectCell.right > rectWindow.right
  1307.            && rectCell.left > GetFixedColumnWidth())
  1308.     {
  1309.         SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
  1310.         if (!GetCellRect(nRow, nCol, rectCell))
  1311.             return;
  1312.     }
  1313.     while (rectCell.bottom > rectWindow.bottom
  1314.            && rectCell.top > GetFixedRowHeight())
  1315.     {
  1316.         SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  1317.         if (!GetCellRect(nRow, nCol, rectCell))
  1318.             return;
  1319.     }
  1320. }
  1321. BOOL CGridCtrl::IsCellEditable(CCellID &cell) const
  1322. {
  1323.     return IsCellEditable(cell.row, cell.col);
  1324. }
  1325. BOOL CGridCtrl::IsCellEditable(int nRow, int nCol) const
  1326. {
  1327.     return IsEditable() && ((GetItemState(nRow, nCol) & GVIS_READONLY) != GVIS_READONLY);
  1328. }
  1329. BOOL CGridCtrl::IsCellSelected(CCellID &cell) const
  1330. {
  1331.     return IsCellSelected(cell.row, cell.col);
  1332. }
  1333. BOOL CGridCtrl::IsCellSelected(int nRow, int nCol) const
  1334. {
  1335.     if (GetVirtualMode())
  1336.     {   
  1337.         if (!IsSelectable())
  1338.             return FALSE;
  1339.         CCellID cell;
  1340.         DWORD key = MAKELONG(nRow, nCol);
  1341.         return (m_SelectedCellMap.Lookup(key, (CCellID&)cell));       
  1342.     }
  1343.     else
  1344.         return IsSelectable() && ((GetItemState(nRow, nCol) & GVIS_SELECTED) == GVIS_SELECTED);
  1345. }
  1346. BOOL CGridCtrl::IsCellVisible(CCellID cell) 
  1347. {
  1348.     return IsCellVisible(cell.row, cell.col);
  1349. }
  1350. BOOL CGridCtrl::IsCellVisible(int nRow, int nCol)
  1351. {
  1352.     if (!IsWindow(m_hWnd))
  1353.         return FALSE;
  1354.     int x, y;
  1355.     CCellID TopLeft;
  1356.     if (nCol >= GetFixedColumnCount() || nRow >= GetFixedRowCount())
  1357.     {
  1358.         TopLeft = GetTopleftNonFixedCell();
  1359.         if (nCol >= GetFixedColumnCount() && nCol < TopLeft.col)
  1360.             return FALSE;
  1361.         if (nRow >= GetFixedRowCount() && nRow < TopLeft.row)
  1362.             return FALSE;
  1363.     }
  1364.     
  1365.     CRect rect;
  1366.     GetClientRect(rect);
  1367.     if (nCol < GetFixedColumnCount())
  1368.     {
  1369.         x = 0;
  1370.         for (int i = 0; i <= nCol; i++) 
  1371.         {
  1372.             if (x >= rect.right)
  1373.                 return FALSE;
  1374.             x += GetColumnWidth(i);    
  1375.         }
  1376.     } 
  1377.     else 
  1378.     {
  1379.         x = GetFixedColumnWidth();
  1380.         for (int i = TopLeft.col; i <= nCol; i++) 
  1381.         {
  1382.             if (x >= rect.right)
  1383.                 return FALSE;
  1384.             x += GetColumnWidth(i);    
  1385.         }
  1386.     }
  1387.     
  1388.     if (nRow < GetFixedRowCount())
  1389.     {
  1390.         y = 0;
  1391.         for (int i = 0; i <= nRow; i++) 
  1392.         {
  1393.             if (y >= rect.bottom)
  1394.                 return FALSE;
  1395.             y += GetRowHeight(i);    
  1396.         }
  1397.     } 
  1398.     else 
  1399.     {
  1400.         if (nRow < TopLeft.row)
  1401.             return FALSE;
  1402.         y = GetFixedRowHeight();
  1403.         for (int i = TopLeft.row; i <= nRow; i++) 
  1404.         {
  1405.             if (y >= rect.bottom)
  1406.                 return FALSE;
  1407.             y += GetRowHeight(i);    
  1408.         }
  1409.     }
  1410.     
  1411.     return TRUE;
  1412. }
  1413. BOOL CGridCtrl::InvalidateCellRect(const CCellID& cell)
  1414. {
  1415.     return InvalidateCellRect(cell.row, cell.col);
  1416. }
  1417. BOOL CGridCtrl::InvalidateCellRect(const int row, const int col)
  1418. {
  1419.     if (!::IsWindow(GetSafeHwnd()) || !m_bAllowDraw)
  1420.         return FALSE;
  1421.     if (!IsValid(row, col))
  1422.         return FALSE;
  1423.     if (!IsCellVisible(row, col))
  1424.         return FALSE;
  1425.     CRect rect;
  1426.     if (!GetCellRect(row, col, rect))
  1427.         return FALSE;
  1428.     rect.right++;
  1429.     rect.bottom++;
  1430.     InvalidateRect(rect, TRUE);
  1431.     return TRUE;
  1432. }
  1433. BOOL CGridCtrl::InvalidateCellRect(const CCellRange& cellRange)
  1434. {
  1435.     ASSERT(IsValid(cellRange));
  1436.     if (!::IsWindow(GetSafeHwnd()) || !m_bAllowDraw)
  1437.         return FALSE;
  1438.     CCellRange visibleCellRange = GetVisibleNonFixedCellRange().Intersect(cellRange);
  1439.     CRect rect;
  1440.     if (!GetCellRangeRect(visibleCellRange, rect))
  1441.         return FALSE;
  1442.     rect.right++;
  1443.     rect.bottom++;
  1444.     InvalidateRect(rect, TRUE);
  1445.     return TRUE;
  1446. }
  1447. /////////////////////////////////////////////////////////////////////////////
  1448. // CGridCtrl Mouse stuff
  1449. // Handles mouse wheel notifications
  1450. // Note - if this doesn't work for win95 then use OnRegisteredMouseWheel instead
  1451. #if !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
  1452. BOOL CGridCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
  1453. {
  1454.     // A m_nRowsPerWheelNotch value less than 0 indicates that the mouse
  1455.     // wheel scrolls whole pages, not just lines.
  1456.     if (m_nRowsPerWheelNotch == -1)
  1457.     {
  1458.         int nPagesScrolled = zDelta / 120;
  1459.         if (nPagesScrolled > 0)
  1460.             for (int i = 0; i < nPagesScrolled; i++)
  1461.                 PostMessage(WM_VSCROLL, SB_PAGEUP, 0);
  1462.         else
  1463.             for (int i = 0; i > nPagesScrolled; i--)
  1464.                 PostMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
  1465.     }
  1466.     else
  1467.     {
  1468.         int nRowsScrolled = m_nRowsPerWheelNotch * zDelta / 120;
  1469.         if (nRowsScrolled > 0)
  1470.             for (int i = 0; i < nRowsScrolled; i++)
  1471.                 PostMessage(WM_VSCROLL, SB_LINEUP, 0);
  1472.         else
  1473.             for (int i = 0; i > nRowsScrolled; i--)
  1474.                 PostMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  1475.     }
  1476.     return CWnd::OnMouseWheel(nFlags, zDelta, pt);
  1477. }
  1478. #endif // !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
  1479. void CGridCtrl::OnMouseMove(UINT /*nFlags*/, CPoint point)
  1480. {
  1481.     CRect rect;
  1482.     GetClientRect(rect);
  1483. #ifndef GRIDCONTROL_NO_DRAGDROP
  1484.     // If outside client area, return (unless we are drag n dropping)
  1485.     if (m_MouseMode != MOUSE_DRAGGING && !rect.PtInRect(point))
  1486.         return;
  1487. #endif
  1488.     // Sometimes a MOUSEMOVE message can come after the left buttons
  1489.     // has been let go, but before the BUTTONUP message hs been processed.
  1490.     // We'll keep track of mouse buttons manually to avoid this.
  1491.     // All bMouseButtonDown's have been replaced with the member m_bMouseButtonDown
  1492.     // BOOL bMouseButtonDown = ((nFlags & MK_LBUTTON) == MK_LBUTTON);
  1493.     // If the left mouse button is up, then test to see if row/column sizing is imminent
  1494.     if (!m_bMouseButtonDown ||
  1495.         (m_bMouseButtonDown && m_MouseMode == MOUSE_NOTHING))
  1496.     {
  1497.         if (m_bAllowColumnResize && MouseOverColumnResizeArea(point))
  1498.         {
  1499.             if (m_MouseMode != MOUSE_OVER_COL_DIVIDE)
  1500.             {
  1501. #ifndef _WIN32_WCE_NO_CURSOR
  1502.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
  1503. #endif
  1504.                 m_MouseMode = MOUSE_OVER_COL_DIVIDE;
  1505.             }
  1506.         }
  1507.         else if (m_bAllowRowResize && MouseOverRowResizeArea(point))
  1508.         {
  1509.             if (m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
  1510.             {
  1511. #ifndef _WIN32_WCE_NO_CURSOR
  1512.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
  1513. #endif
  1514.                 m_MouseMode = MOUSE_OVER_ROW_DIVIDE;
  1515.             }
  1516.         }
  1517.         else if (m_MouseMode != MOUSE_NOTHING)
  1518.         {
  1519. #ifndef _WIN32_WCE_NO_CURSOR
  1520.             SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1521. #endif
  1522.             m_MouseMode = MOUSE_NOTHING;
  1523.         }
  1524.         if (m_MouseMode == MOUSE_NOTHING)
  1525.         {
  1526.             CGridCellBase* pCell = NULL;
  1527.             CCellID idCurrentCell;
  1528.             if (!GetVirtualMode() || m_bTitleTips)
  1529.             {
  1530.                 // Let the cell know that a big fat cursor is currently hovering
  1531.                 // over it.
  1532.                 idCurrentCell = GetCellFromPt(point);
  1533.                 pCell = GetCell(idCurrentCell.row, idCurrentCell.col);
  1534.                 if (pCell)
  1535.                     pCell->OnMouseOver();
  1536.             }
  1537. #ifndef GRIDCONTROL_NO_TITLETIPS
  1538.             // Titletips anyone? anyone?
  1539.             if (m_bTitleTips)
  1540.             {
  1541.                 CRect TextRect, CellRect;
  1542.                 if (pCell)
  1543.                 {
  1544.                     LPCTSTR szTipText = pCell->GetTipText();
  1545.                     if (szTipText && szTipText[0]
  1546.                         && !pCell->IsEditing()
  1547.                         && GetCellRect( idCurrentCell.row, idCurrentCell.col, &TextRect)
  1548.                         && pCell->GetTipTextRect( &TextRect)
  1549.                         && GetCellRect(idCurrentCell.row, idCurrentCell.col, CellRect) )
  1550.                     {
  1551.                         m_TitleTip.Show(TextRect, pCell->GetTipText(),  0, CellRect,
  1552.                                         pCell->GetFont(),  GetTitleTipTextClr(), GetTitleTipBackClr());
  1553.                     }
  1554.                 }
  1555.             }
  1556. #endif
  1557.         }
  1558.         m_LastMousePoint = point;
  1559.         return;
  1560.     }
  1561.     if (!IsValid(m_LeftClickDownCell))
  1562.     {
  1563.         m_LastMousePoint = point;
  1564.         return;
  1565.     }
  1566.     // If the left mouse button is down, then process appropriately
  1567.     if (m_bMouseButtonDown)
  1568.     {
  1569.         switch (m_MouseMode)
  1570.         {
  1571.         case MOUSE_SELECT_ALL:
  1572.             break;
  1573.         case MOUSE_SELECT_COL:
  1574.         case MOUSE_SELECT_ROW:
  1575.         case MOUSE_SELECT_CELLS:
  1576.             {
  1577.                 CCellID idCurrentCell = GetCellFromPt(point);
  1578.                 if (!IsValid(idCurrentCell))
  1579.                     return;
  1580.                 if (idCurrentCell != GetFocusCell())
  1581.                 {
  1582.                     OnSelecting(idCurrentCell);
  1583.                     // EFW - BUG FIX - Keep the appropriate cell row and/or
  1584.                     // column focused.  A fix in SetFocusCell() will place
  1585.                     // the cursor in a non-fixed cell as needed.
  1586.                     if((idCurrentCell.row >= m_nFixedRows &&
  1587.                       idCurrentCell.col >= m_nFixedCols) ||
  1588.                       m_MouseMode==MOUSE_SELECT_COL ||
  1589.                       m_MouseMode==MOUSE_SELECT_ROW)
  1590.                     {
  1591.                         SetFocusCell(idCurrentCell);
  1592.                     }
  1593.                 }
  1594.                 break;
  1595.             }
  1596.         case MOUSE_SIZING_COL:
  1597.             {
  1598.                 CDC* pDC = GetDC();
  1599.                 if (!pDC)
  1600.                     break;
  1601.                 CRect oldInvertedRect(m_LastMousePoint.x, rect.top,
  1602.                     m_LastMousePoint.x + 2, rect.bottom);
  1603.                 pDC->InvertRect(&oldInvertedRect);
  1604.                 CRect newInvertedRect(point.x, rect.top, 
  1605.                     point.x + 2, rect.bottom);
  1606.                 pDC->InvertRect(&newInvertedRect);
  1607.                 ReleaseDC(pDC);
  1608.             }
  1609.             break;
  1610.             
  1611.         case MOUSE_SIZING_ROW:        
  1612.             {
  1613.                 CDC* pDC = GetDC();
  1614.                 if (!pDC)
  1615.                     break;
  1616.                 
  1617.                 CRect oldInvertedRect(rect.left, m_LastMousePoint.y,
  1618.                     rect.right, m_LastMousePoint.y + 2);
  1619.                 pDC->InvertRect(&oldInvertedRect);
  1620.                 CRect newInvertedRect(rect.left, point.y, 
  1621.                     rect.right, point.y + 2);
  1622.                 pDC->InvertRect(&newInvertedRect);
  1623.                 ReleaseDC(pDC);
  1624.             }
  1625.             break;
  1626.             
  1627. #ifndef GRIDCONTROL_NO_DRAGDROP
  1628.         case MOUSE_PREPARE_EDIT:
  1629.         case MOUSE_PREPARE_DRAG:
  1630.             m_MouseMode = MOUSE_PREPARE_DRAG;
  1631.             OnBeginDrag();    
  1632.             break;
  1633. #endif
  1634.         }    
  1635.     }
  1636.     m_LastMousePoint = point;
  1637. }
  1638. CPoint CGridCtrl::GetPointClicked(int nRow, int nCol, const CPoint& point)
  1639. {
  1640.     CPoint PointCellOrigin;
  1641.     if( !GetCellOrigin( nRow, nCol, &PointCellOrigin)  )
  1642.         return CPoint( 0, 0);
  1643.     CPoint PointClickedCellRelative( point);
  1644.     PointClickedCellRelative -= PointCellOrigin;
  1645.     return PointClickedCellRelative;
  1646. }
  1647. void CGridCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
  1648. {
  1649.     TRACE0("CGridCtrl::OnLButtonDblClkn");
  1650.     CCellID cell = GetCellFromPt(point);
  1651.     if( !IsValid( cell) )
  1652.     {
  1653.         //ASSERT(FALSE);
  1654.         return;
  1655.     }
  1656. #ifdef _WIN32_WCE
  1657.     if (MouseOverColumnResizeArea(point))
  1658. #else
  1659.     if (m_MouseMode == MOUSE_OVER_COL_DIVIDE)
  1660. #endif
  1661.     {
  1662.         CPoint start;
  1663.         if (!GetCellOrigin(0, cell.col, &start))
  1664.             return;
  1665.         if (point.x - start.x <= m_nResizeCaptureRange)     // Clicked right of border
  1666.             cell.col--;
  1667.         //  ignore columns that are hidden and look left towards first visible column
  1668.         BOOL bFoundVisible = FALSE;
  1669.         while( cell.col >= 0)
  1670.         {
  1671.             if( GetColumnWidth( cell.col) > 0)
  1672.             {
  1673.                 bFoundVisible = TRUE;
  1674.                 break;
  1675.             }
  1676.             cell.col--;
  1677.         }
  1678.         if( !bFoundVisible)
  1679.             return;
  1680.         AutoSizeColumn(cell.col, GetAutoSizeStyle());
  1681.         Invalidate();
  1682.     }
  1683. #ifdef _WIN32_WCE
  1684.     else if (MouseOverRowResizeArea(point))
  1685. #else
  1686.     else if (m_MouseMode == MOUSE_OVER_ROW_DIVIDE)
  1687. #endif
  1688.     {
  1689.         CPoint start;
  1690.         if (!GetCellOrigin(0, cell.col, &start))
  1691.             return;
  1692.         if (point.y - start.y <= m_nResizeCaptureRange)     // Clicked below border
  1693.             cell.row--;
  1694. //  ignore rows that are hidden and look upt towards first visible row
  1695.         BOOL bFoundVisible = FALSE;
  1696.         while( cell.row >= 0)
  1697.         {
  1698.             if( GetRowHeight( cell.row) > 0)
  1699.             {
  1700.                 bFoundVisible = TRUE;
  1701.                 break;
  1702.             }
  1703.             cell.row--;
  1704.         }
  1705.         if( !bFoundVisible)
  1706.             return;
  1707.         AutoSizeRow(cell.row);
  1708.         Invalidate();
  1709.     }
  1710.     else if (m_MouseMode == MOUSE_NOTHING)
  1711.     {
  1712.         CPoint pointClickedRel;
  1713.         pointClickedRel = GetPointClicked( cell.row, cell.col, point);
  1714.         if (cell.row >= m_nFixedRows && IsValid(m_LeftClickDownCell) && cell.col >= m_nFixedCols)
  1715.         {
  1716.             OnEditCell(cell.row, cell.col, pointClickedRel, VK_LBUTTON);
  1717.         }
  1718.         else if (m_bListMode)
  1719.         {
  1720.             if (!IsValid(cell))
  1721.                 return;
  1722.             if (cell.row >= m_nFixedRows && cell.col < GetFixedColumnCount())
  1723.                 OnEditCell(cell.row, cell.col, pointClickedRel, VK_LBUTTON);
  1724.         }
  1725.         if (IsValid(cell))
  1726.         {
  1727.             CGridCellBase* pCell = GetCell(cell.row, cell.col);
  1728.             if (pCell)
  1729.                 pCell->OnDblClick(pointClickedRel);
  1730.             SendMessageToParent(cell.row, cell.col, NM_DBLCLK);
  1731.         }
  1732.     }
  1733.     CWnd::OnLButtonDblClk(nFlags, point);
  1734. }
  1735. void CGridCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  1736. {
  1737. #ifdef GRIDCONTROL_USE_TITLETIPS
  1738.     // EFW - Bug Fix
  1739.     m_TitleTip.Hide();  // hide any titletips
  1740. #endif
  1741.     // TRACE0("CGridCtrl::OnLButtonDownn");
  1742.     // CWnd::OnLButtonDown(nFlags, point);
  1743.     SetFocus();
  1744.     m_bMouseButtonDown   = TRUE;
  1745.     m_LeftClickDownPoint = point;
  1746.     m_LeftClickDownCell  = GetCellFromPt(point);
  1747.     if (!IsValid(m_LeftClickDownCell))
  1748.         return;
  1749.     // If the SHIFT key is not down, then the start of the selection area should be the 
  1750.     // cell just clicked. Otherwise, keep the previous selection-start-cell so the user
  1751.     // can add to their previous cell selections in an intuitive way. If no selection-
  1752.     // start-cell has been specified, then set it's value here and now.
  1753.     if ((nFlags & MK_SHIFT) != MK_SHIFT)
  1754.         m_SelectionStartCell = m_LeftClickDownCell;
  1755.     else
  1756.     {
  1757.         if (!IsValid(m_SelectionStartCell))
  1758.             m_SelectionStartCell = m_idCurrentCell;
  1759.     }
  1760.     EndEditing();
  1761.     // tell the cell about it -- only things like a btn embedded in a cell
  1762.     //  would look at these msgs
  1763.     CGridCellBase* pCell = GetCell(m_LeftClickDownCell.row, m_LeftClickDownCell.col);
  1764.     if (pCell)
  1765.         pCell->OnClickDown( GetPointClicked( m_LeftClickDownCell.row, m_LeftClickDownCell.col, point));
  1766.     // If the user clicks on the current cell, then prepare to edit it.
  1767.     // (If the user moves the mouse, then dragging occurs)
  1768.     if (m_LeftClickDownCell == m_idCurrentCell)
  1769.     {
  1770.         if (IsCellEditable(m_LeftClickDownCell))
  1771.             m_MouseMode = MOUSE_PREPARE_EDIT;
  1772.         return;
  1773.     }
  1774.     // If the user clicks on a selected cell, then prepare to drag it.
  1775.     // (If the user moves the mouse, then dragging occurs)
  1776.     else if (IsCellSelected(m_LeftClickDownCell))
  1777.     {
  1778.         // If control is pressed then unselect the cell or row (depending on the list mode)
  1779.         if (nFlags & MK_CONTROL)
  1780.         {
  1781.             SetFocusCell(m_LeftClickDownCell);
  1782.             if (GetListMode())
  1783.                 SelectRows(m_LeftClickDownCell, TRUE, FALSE);
  1784.             else
  1785.                 SelectCells(m_LeftClickDownCell, TRUE, FALSE);
  1786.             return;
  1787.         }
  1788. #ifndef GRIDCONTROL_NO_DRAGDROP
  1789.         else if (m_bAllowDragAndDrop)
  1790.             m_MouseMode = MOUSE_PREPARE_DRAG;
  1791. #endif
  1792.     }
  1793.     else if (m_MouseMode != MOUSE_OVER_COL_DIVIDE &&
  1794.              m_MouseMode != MOUSE_OVER_ROW_DIVIDE)        
  1795.     {
  1796.         SetFocusCell(-1, - 1);
  1797.         if (GetRowCount() > GetFixedRowCount() && 
  1798.             GetColumnCount() > GetFixedColumnCount())
  1799.             SetFocusCell(max(m_LeftClickDownCell.row, m_nFixedRows),
  1800.                          max(m_LeftClickDownCell.col, m_nFixedCols));
  1801.     }
  1802.     
  1803.     SetCapture();
  1804.     
  1805.     if (m_MouseMode == MOUSE_NOTHING)
  1806.     {
  1807.         if (m_bAllowColumnResize && MouseOverColumnResizeArea(point))
  1808.         {
  1809.             if (m_MouseMode != MOUSE_OVER_COL_DIVIDE)
  1810.             {
  1811. #ifndef _WIN32_WCE_NO_CURSOR
  1812.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
  1813. #endif
  1814.                 m_MouseMode = MOUSE_OVER_COL_DIVIDE;
  1815.             }
  1816.         }
  1817.         else if (m_bAllowRowResize && MouseOverRowResizeArea(point))
  1818.         {
  1819.             if (m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
  1820.             {
  1821. #ifndef _WIN32_WCE_NO_CURSOR
  1822.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
  1823. #endif
  1824.                 m_MouseMode = MOUSE_OVER_ROW_DIVIDE;
  1825.             }
  1826.         }
  1827.         // else if (m_MouseMode != MOUSE_NOTHING)
  1828.         //{
  1829.         //    SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1830.         //    m_MouseMode = MOUSE_NOTHING;
  1831.         //}
  1832.     }
  1833.     
  1834.     if (m_MouseMode == MOUSE_OVER_COL_DIVIDE) // sizing column
  1835.     {
  1836.         m_MouseMode = MOUSE_SIZING_COL;
  1837.         CPoint start;
  1838.         if (!GetCellOrigin(0, m_LeftClickDownCell.col, &start))
  1839.             return;
  1840.         if( !m_bHiddenColUnhide)
  1841.         {
  1842.             //  ignore columns that are hidden and look left towards first visible column
  1843.             BOOL bLookForVisible = TRUE;
  1844.             BOOL bIsCellRightBorder = point.x - start.x > m_nResizeCaptureRange;
  1845.             if( bIsCellRightBorder
  1846.                 && m_LeftClickDownCell.col + 1 >= GetColumnCount() )
  1847.             {
  1848.                 // clicked on last column's right border
  1849.                 // if last column is visible, don't do anything
  1850.                 if( m_LeftClickDownCell.col >= 0)
  1851.                     bLookForVisible = FALSE;
  1852.             }
  1853.             if( bLookForVisible)
  1854.             {
  1855.                 // clicked on column divider other than last right border
  1856.                 BOOL bFoundVisible = FALSE;
  1857.                 int iOffset = 1;
  1858.                 if( bIsCellRightBorder)
  1859.                     iOffset = 0;
  1860.                 while( m_LeftClickDownCell.col - iOffset >= 0)
  1861.                 {
  1862.                     if( GetColumnWidth( m_LeftClickDownCell.col - iOffset) > 0)
  1863.                     {
  1864.                         bFoundVisible = TRUE;
  1865.                         break;
  1866.                     }
  1867.                     m_LeftClickDownCell.col--;
  1868.                 }
  1869.                 if( !bFoundVisible)
  1870.                     return;
  1871.             }
  1872.         }
  1873.         CRect rect;
  1874.         GetClientRect(rect);
  1875.         CRect invertedRect(point.x, rect.top, point.x + 2, rect.bottom);
  1876.         CDC* pDC = GetDC();
  1877.         if (pDC)
  1878.         {
  1879.             pDC->InvertRect(&invertedRect);
  1880.             ReleaseDC(pDC);
  1881.         }
  1882.         if (point.x - start.x <= m_nResizeCaptureRange)        // clicked right of border
  1883.             if (!GetCellOrigin(0, --m_LeftClickDownCell.col, &start))
  1884.                 return;
  1885.             rect.left = start.x;
  1886.             ClientToScreen(rect);
  1887. #ifndef _WIN32_WCE_NO_CURSOR
  1888.             ClipCursor(rect);
  1889. #endif
  1890.     }
  1891.     else if (m_MouseMode == MOUSE_OVER_ROW_DIVIDE) // sizing row
  1892.     {
  1893.         m_MouseMode = MOUSE_SIZING_ROW;
  1894.         CPoint start;
  1895.         if (!GetCellOrigin(m_LeftClickDownCell, &start))
  1896.             return;
  1897.         if( !m_bHiddenRowUnhide)
  1898.         {
  1899.             //  ignore rows that are hidden and look up towards first visible row
  1900.             BOOL bLookForVisible = TRUE;
  1901.             BOOL bIsCellBottomBorder = point.y - start.y > m_nResizeCaptureRange;
  1902.             if( bIsCellBottomBorder
  1903.                 && m_LeftClickDownCell.row + 1 >= GetRowCount() )
  1904.             {
  1905.                 // clicked on last row's bottom border
  1906.                 // if last row is visible, don't do anything
  1907.                 if( m_LeftClickDownCell.row >= 0)
  1908.                     bLookForVisible = FALSE;
  1909.             }
  1910.             if( bLookForVisible)
  1911.             {
  1912.                 // clicked on row divider other than last bottom border
  1913.                 BOOL bFoundVisible = FALSE;
  1914.                 int iOffset = 1;
  1915.                 if( bIsCellBottomBorder)
  1916.                     iOffset = 0;
  1917.                 while( m_LeftClickDownCell.row - iOffset >= 0)
  1918.                 {
  1919.                     if( GetRowHeight( m_LeftClickDownCell.row - iOffset) > 0)
  1920.                     {
  1921.                         bFoundVisible = TRUE;
  1922.                         break;
  1923.                     }
  1924.                     m_LeftClickDownCell.row--;
  1925.                 }
  1926.                 if( !bFoundVisible)
  1927.                     return;
  1928.             }
  1929.         }
  1930.         CRect rect;
  1931.         GetClientRect(rect);
  1932.         CRect invertedRect(rect.left, point.y, rect.right, point.y + 2);
  1933.         CDC* pDC = GetDC();
  1934.         if (pDC)
  1935.         {
  1936.             pDC->InvertRect(&invertedRect);
  1937.             ReleaseDC(pDC);
  1938.         }
  1939.         if (point.y - start.y <= m_nResizeCaptureRange)            // clicked below border
  1940.             if (!GetCellOrigin(--m_LeftClickDownCell.row, 0, &start))
  1941.                 return;
  1942.             rect.top = start.y;
  1943.             ClientToScreen(rect);
  1944. #ifndef _WIN32_WCE_NO_CURSOR
  1945.             ClipCursor(rect);
  1946. #endif
  1947.     }
  1948.     else
  1949. #ifndef GRIDCONTROL_NO_DRAGDROP
  1950.     if (m_MouseMode != MOUSE_PREPARE_DRAG) // not sizing or editing -- selecting
  1951. #endif
  1952.     {
  1953.         // If Ctrl pressed, save the current cell selection. This will get added
  1954.         // to the new cell selection at the end of the cell selection process
  1955.         m_PrevSelectedCellMap.RemoveAll();
  1956.         if (nFlags & MK_CONTROL)
  1957.         {
  1958.             for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  1959.             {
  1960.                 DWORD key;
  1961.                 CCellID cell;
  1962.                 m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1963.                 m_PrevSelectedCellMap.SetAt(key, cell);
  1964.             }
  1965.         }
  1966.         
  1967.         if (m_LeftClickDownCell.row < GetFixedRowCount())
  1968.             OnFixedRowClick(m_LeftClickDownCell);
  1969.         else if (m_LeftClickDownCell.col < GetFixedColumnCount())
  1970.             OnFixedColumnClick(m_LeftClickDownCell);
  1971.         else
  1972.         {
  1973.             m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
  1974.             OnSelecting(m_LeftClickDownCell);
  1975.             m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);
  1976.         }
  1977.     }   
  1978.     m_LastMousePoint = point;
  1979. }
  1980. void CGridCtrl::OnLButtonUp(UINT nFlags, CPoint point)
  1981. {
  1982.     // TRACE0("CGridCtrl::OnLButtonUpn");
  1983.     CWnd::OnLButtonUp(nFlags, point);
  1984.     m_bMouseButtonDown = FALSE;
  1985. #ifndef _WIN32_WCE_NO_CURSOR
  1986.     ClipCursor(NULL);
  1987. #endif
  1988.     if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
  1989.     {
  1990.         ReleaseCapture();
  1991.         KillTimer(m_nTimerID);
  1992.         m_nTimerID = 0;
  1993.     }
  1994.     CPoint pointClickedRel;
  1995.     pointClickedRel = GetPointClicked( m_idCurrentCell.row, m_idCurrentCell.col, point);
  1996.     // m_MouseMode == MOUSE_PREPARE_EDIT only if user clicked down on current cell
  1997.     // and then didn't move mouse before clicking up (releasing button)
  1998.     if (m_MouseMode == MOUSE_PREPARE_EDIT)
  1999.     {
  2000.         OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, pointClickedRel, VK_LBUTTON);
  2001.     }
  2002. #ifndef GRIDCONTROL_NO_DRAGDROP
  2003.     // m_MouseMode == MOUSE_PREPARE_DRAG only if user clicked down on a selected cell
  2004.     // and then didn't move mouse before clicking up (releasing button)
  2005.     else if (m_MouseMode == MOUSE_PREPARE_DRAG) 
  2006.     {
  2007.         CGridCellBase* pCell = GetCell(m_idCurrentCell.row, m_idCurrentCell.col);
  2008.         if (pCell)
  2009.             pCell->OnClick( GetPointClicked( m_idCurrentCell.row, m_idCurrentCell.col, point) );
  2010.         SendMessageToParent(m_LeftClickDownCell.row, m_LeftClickDownCell.col, NM_CLICK);
  2011.         ResetSelectedRange();
  2012.     }
  2013. #endif
  2014.     else if (m_MouseMode == MOUSE_SIZING_COL)
  2015.     {
  2016.         CRect rect;
  2017.         GetClientRect(rect);
  2018.         CRect invertedRect(m_LastMousePoint.x, rect.top, m_LastMousePoint.x + 2, rect.bottom);
  2019.         
  2020.         CDC* pDC = GetDC();
  2021.         if (pDC)
  2022.         {
  2023.             pDC->InvertRect(&invertedRect);
  2024.             ReleaseDC(pDC);
  2025.         }
  2026.         
  2027.         if (m_LeftClickDownPoint != point && (point.x != 0 || point.y != 0)) // 0 pt fix by email1@bierling.net
  2028.         {   
  2029.             CPoint start;
  2030.             if (!GetCellOrigin(m_LeftClickDownCell, &start))
  2031.                 return;
  2032.             int nColumnWidth = point.x - start.x;
  2033.             if (!m_bAllowColHide)
  2034.                 nColumnWidth = max(nColumnWidth,1);
  2035.             SetColumnWidth(m_LeftClickDownCell.col, nColumnWidth);
  2036.             ResetScrollBars();
  2037.             Invalidate();
  2038.         }
  2039.     }
  2040.     else if (m_MouseMode == MOUSE_SIZING_ROW)
  2041.     {
  2042.         CRect rect;
  2043.         GetClientRect(rect);
  2044.         CRect invertedRect(rect.left, m_LastMousePoint.y, rect.right, m_LastMousePoint.y + 2);
  2045.         CDC* pDC = GetDC();
  2046.         if (pDC)
  2047.         {
  2048.             pDC->InvertRect(&invertedRect);
  2049.             ReleaseDC(pDC);
  2050.         }
  2051.         
  2052.         if (m_LeftClickDownPoint != point  && (point.x != 0 || point.y != 0)) // 0 pt fix by email1@bierling.net
  2053.         {
  2054.             CPoint start;
  2055.             if (!GetCellOrigin(m_LeftClickDownCell, &start))
  2056.                 return;
  2057.             
  2058.             int nRowHeight = point.y - start.y;
  2059.             if (!m_bAllowRowHide)
  2060.                 nRowHeight = max(nRowHeight,1);
  2061.             SetRowHeight(m_LeftClickDownCell.row, nRowHeight);
  2062.             ResetScrollBars();
  2063.             Invalidate();
  2064.         }
  2065.     }
  2066.     else
  2067.     {
  2068.         CGridCellBase* pCell = GetCell(m_idCurrentCell.row, m_idCurrentCell.col);
  2069.         if (pCell)
  2070.             pCell->OnClick( GetPointClicked( m_idCurrentCell.row, m_idCurrentCell.col, point) );
  2071.         SendMessageToParent(m_LeftClickDownCell.row, m_LeftClickDownCell.col, NM_CLICK);
  2072.     }
  2073.     
  2074.     m_MouseMode = MOUSE_NOTHING;
  2075.     
  2076. #ifndef _WIN32_WCE_NO_CURSOR
  2077.     SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  2078. #endif
  2079.     
  2080.     if (!IsValid(m_LeftClickDownCell))
  2081.         return;
  2082.     
  2083.     CWnd *pOwner = GetOwner();
  2084.     if (pOwner && IsWindow(pOwner->m_hWnd))
  2085.         pOwner->PostMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED),
  2086.         (LPARAM) GetSafeHwnd());
  2087. }
  2088. #ifndef _WIN32_WCE
  2089. // EFW - Added to forward right click to parent so that a context
  2090. // menu can be shown without deriving a new grid class.
  2091. void CGridCtrl::OnRButtonUp(UINT nFlags, CPoint point)
  2092. {
  2093.     CCellID FocusCell;
  2094.     CWnd::OnRButtonUp(nFlags, point);
  2095.     FocusCell = GetCellFromPt(point);
  2096.     EndEditing();        // Auto-destroy any InPlaceEdit's
  2097.     // If not a valid cell, pass -1 for row and column
  2098.     if(!IsValid(FocusCell))
  2099.         SendMessageToParent(-1, -1, NM_RCLICK);
  2100.     else
  2101.     {
  2102.         SetFocusCell(-1,-1);
  2103.         SetFocusCell(max(FocusCell.row, m_nFixedRows),
  2104.             max(FocusCell.col, m_nFixedCols));
  2105.         // tell the cell about it
  2106.         CGridCellBase* pCell = GetCell(m_idCurrentCell.row, m_idCurrentCell.col);
  2107.         if (pCell)
  2108.             pCell->OnRClick( GetPointClicked( m_idCurrentCell.row, m_idCurrentCell.col, point) );
  2109.         SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, NM_RCLICK);
  2110.     }
  2111. }
  2112. #endif
  2113. #if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  2114. /////////////////////////////////////////////////////////////////////////////
  2115. // CGridCtrl printing
  2116. // EFW - New print margin support functions
  2117. void CGridCtrl::SetPrintMarginInfo(int nHeaderHeight, int nFooterHeight,
  2118.     int nLeftMargin, int nRightMargin, int nTopMargin,
  2119.     int nBottomMargin, int nGap)
  2120. {
  2121.     // If any parameter is -1, keep the existing setting
  2122.     if(nHeaderHeight > -1)
  2123.         m_nHeaderHeight = nHeaderHeight;
  2124.     if(nFooterHeight > -1)
  2125.         m_nFooterHeight = nFooterHeight;
  2126.     if(nLeftMargin > -1)
  2127.         m_nLeftMargin = nLeftMargin;
  2128.     if(nRightMargin > -1)
  2129.         m_nRightMargin = nRightMargin;
  2130.     if(nTopMargin > -1)
  2131.         m_nTopMargin = nTopMargin;
  2132.     if(nBottomMargin > -1)
  2133.         m_nBottomMargin = nBottomMargin;
  2134.     if(nGap > -1)
  2135.         m_nGap = nGap;
  2136. }
  2137. void CGridCtrl::GetPrintMarginInfo(int &nHeaderHeight, int &nFooterHeight,
  2138.     int &nLeftMargin, int &nRightMargin, int &nTopMargin,
  2139.     int &nBottomMargin, int &nGap)
  2140. {
  2141.     nHeaderHeight = m_nHeaderHeight;
  2142.     nFooterHeight = m_nFooterHeight;
  2143.     nLeftMargin = m_nLeftMargin;
  2144.     nRightMargin = m_nRightMargin;
  2145.     nTopMargin = m_nTopMargin;
  2146.     nBottomMargin = m_nBottomMargin;
  2147.     nGap = m_nGap;
  2148. }
  2149. void CGridCtrl::Print()
  2150. {
  2151.     CDC dc;
  2152.     CPrintDialog printDlg(FALSE);
  2153.     if (printDlg.DoModal() != IDOK)             // Get printer settings from user
  2154.         return;
  2155.     dc.Attach(printDlg.GetPrinterDC());         // attach a printer DC
  2156.     dc.m_bPrinting = TRUE;
  2157.     CString strTitle;
  2158.     strTitle.LoadString(AFX_IDS_APP_TITLE);
  2159.     if( strTitle.IsEmpty() )
  2160.     {
  2161.         CWnd *pParentWnd = GetParent();
  2162.         while (pParentWnd)
  2163.         {
  2164.             pParentWnd->GetWindowText(strTitle);
  2165.             if (strTitle.GetLength())  // can happen if it is a CView, CChildFrm has the title
  2166.                 break;
  2167.             pParentWnd = pParentWnd->GetParent();
  2168.         }
  2169.     }
  2170.     DOCINFO di;                                 // Initialise print doc details
  2171.     memset(&di, 0, sizeof (DOCINFO));
  2172.     di.cbSize = sizeof (DOCINFO);
  2173.     di.lpszDocName = strTitle;
  2174.     BOOL bPrintingOK = dc.StartDoc(&di);        // Begin a new print job
  2175.     CPrintInfo Info;
  2176.     Info.m_rectDraw.SetRect(0,0, dc.GetDeviceCaps(HORZRES), dc.GetDeviceCaps(VERTRES));
  2177.     OnBeginPrinting(&dc, &Info);                // Initialise printing
  2178.     for (UINT page = Info.GetMinPage(); page <= Info.GetMaxPage() && bPrintingOK; page++)
  2179.     {
  2180.         dc.StartPage();                         // begin new page
  2181.         Info.m_nCurPage = page;
  2182.         OnPrint(&dc, &Info);                    // Print page
  2183.         bPrintingOK = (dc.EndPage() > 0);       // end page
  2184.     }
  2185.     OnEndPrinting(&dc, &Info);                  // Clean up after printing
  2186.     if (bPrintingOK)
  2187.         dc.EndDoc();                            // end a print job
  2188.     else
  2189.         dc.AbortDoc();                          // abort job.
  2190.     dc.Detach();                                // detach the printer DC
  2191. }
  2192. /////////////////////////////////////////////////////////////////////////////
  2193. // CGridCtrl printing overridables - for Doc/View print/print preview framework
  2194. // EFW - Various changes in the next few functions to support the
  2195. // new print margins and a few other adjustments.
  2196. void CGridCtrl::OnBeginPrinting(CDC *pDC, CPrintInfo *pInfo)
  2197. {
  2198.     // OnBeginPrinting() is called after the user has committed to
  2199.     // printing by OK'ing the Print dialog, and after the framework
  2200.     // has created a CDC object for the printer or the preview view.
  2201.     // This is the right opportunity to set up the page range.
  2202.     // Given the CDC object, we can determine how many rows will
  2203.     // fit on a page, so we can in turn determine how many printed
  2204.     // pages represent the entire document.
  2205.     ASSERT(pDC && pInfo);
  2206.     if (!pDC || !pInfo) return;
  2207.     // Get a DC for the current window (will be a screen DC for print previewing)
  2208.     CDC *pCurrentDC = GetDC();        // will have dimensions of the client area
  2209.     if (!pCurrentDC) return;
  2210.     CSize PaperPixelsPerInch(pDC->GetDeviceCaps(LOGPIXELSX), pDC->GetDeviceCaps(LOGPIXELSY));
  2211.     CSize ScreenPixelsPerInch(pCurrentDC->GetDeviceCaps(LOGPIXELSX), pCurrentDC->GetDeviceCaps(LOGPIXELSY));
  2212.     // Create the printer font
  2213.     int nFontSize = -10;
  2214.     CString strFontName = "Arial";
  2215.     m_PrinterFont.CreateFont(nFontSize, 0,0,0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,
  2216.                              OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
  2217.                              DEFAULT_PITCH | FF_DONTCARE, strFontName);
  2218.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  2219.     // Get the average character width (in GridCtrl units) and hence the margins
  2220.     m_CharSize = pDC->GetTextExtent(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSATUVWXYZ"),52);
  2221.     m_CharSize.cx /= 52;
  2222.     int nMargins = (m_nLeftMargin+m_nRightMargin)*m_CharSize.cx;
  2223.     // Get the page sizes (physical and logical)
  2224.     m_PaperSize = CSize(pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
  2225.     if( m_bWysiwygPrinting)
  2226.     {
  2227.         m_LogicalPageSize.cx = ScreenPixelsPerInch.cx * m_PaperSize.cx / PaperPixelsPerInch.cx * 3 / 4;
  2228.         m_LogicalPageSize.cy = ScreenPixelsPerInch.cy * m_PaperSize.cy / PaperPixelsPerInch.cy * 3 / 4;
  2229.     }
  2230.     else
  2231.     {
  2232.         m_PaperSize = CSize(pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
  2233.         m_LogicalPageSize.cx = GetVirtualWidth()+nMargins;
  2234. #ifdef _WIN32_WCE
  2235.         m_LogicalPageSize.cy = (m_LogicalPageSize.cx * m_PaperSize.cy) / m_PaperSize.cx;
  2236. #else
  2237.         m_LogicalPageSize.cy = MulDiv(m_LogicalPageSize.cx, m_PaperSize.cy, m_PaperSize.cx);
  2238. #endif
  2239.     }
  2240.     m_nPageHeight = m_LogicalPageSize.cy - GetFixedRowHeight()
  2241.                        - (m_nHeaderHeight+m_nFooterHeight + 2*m_nGap)*m_CharSize.cy;
  2242.     // Get the number of pages. Assumes no row is bigger than the page size.
  2243.     int nTotalRowHeight = 0;
  2244.     m_nNumPages = 1;
  2245.     for (int row = GetFixedRowCount(); row < GetRowCount(); row++)
  2246.     {
  2247.         nTotalRowHeight += GetRowHeight(row);
  2248.         if (nTotalRowHeight > m_nPageHeight) {
  2249.             m_nNumPages++;
  2250.             nTotalRowHeight = GetRowHeight(row);
  2251.         }
  2252.     }
  2253.     // now, figure out how many additional pages must print out if rows ARE bigger
  2254.     //  than page size
  2255.     int iColumnOffset = 0;
  2256.     int i1;
  2257.     for( i1=0; i1 < GetFixedColumnCount(); i1++)
  2258.     {
  2259.         iColumnOffset += GetColumnWidth( i1);
  2260.     }
  2261.     m_nPageWidth = m_LogicalPageSize.cx - iColumnOffset
  2262.                     - nMargins;
  2263.     m_nPageMultiplier = 1;
  2264.     if( m_bWysiwygPrinting)
  2265.     {
  2266.         int iTotalRowWidth = 0;
  2267.         for( i1 = GetFixedColumnCount(); i1 < GetColumnCount(); i1++)
  2268.         {
  2269.             iTotalRowWidth += GetColumnWidth( i1);
  2270.             if( iTotalRowWidth > m_nPageWidth)
  2271.             {
  2272.                 m_nPageMultiplier++;
  2273.                 iTotalRowWidth = GetColumnWidth( i1);
  2274.             }
  2275.         }
  2276.         m_nNumPages *= m_nPageMultiplier;
  2277.     }
  2278.     // Set up the print info
  2279.     pInfo->SetMaxPage(m_nNumPages);
  2280.     pInfo->m_nCurPage = 1;                        // start printing at page# 1
  2281.     ReleaseDC(pCurrentDC);
  2282.     pDC->SelectObject(pOldFont);
  2283. }
  2284. void CGridCtrl::OnPrint(CDC *pDC, CPrintInfo *pInfo)
  2285. {
  2286.     if (!pDC || !pInfo)
  2287.         return;
  2288.     //CRect rcPage(pInfo->m_rectDraw);
  2289.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  2290.     // Set the page map mode to use GridCtrl units, and setup margin
  2291.     pDC->SetMapMode(MM_ANISOTROPIC);
  2292.     pDC->SetWindowExt(m_LogicalPageSize);
  2293.     pDC->SetViewportExt(m_PaperSize);
  2294.     pDC->SetWindowOrg(-m_nLeftMargin * m_CharSize.cx, 0);
  2295.     // Header
  2296.     pInfo->m_rectDraw.top    = 0;
  2297.     pInfo->m_rectDraw.left   = 0;
  2298.     pInfo->m_rectDraw.right  = m_LogicalPageSize.cx - (m_nLeftMargin + m_nRightMargin) * m_CharSize.cx;
  2299.     pInfo->m_rectDraw.bottom = m_nHeaderHeight * m_CharSize.cy;
  2300.     PrintHeader(pDC, pInfo);
  2301.     pDC->OffsetWindowOrg(0, -m_nHeaderHeight * m_CharSize.cy);
  2302.     // Gap between header and column headings
  2303.     pDC->OffsetWindowOrg(0, -m_nGap * m_CharSize.cy);
  2304.     pDC->OffsetWindowOrg(0, -GetFixedRowHeight());
  2305.     // We need to find out which row to start printing for this page.
  2306.     int nTotalRowHeight = 0;
  2307.     UINT nNumPages = 1;
  2308.     m_nCurrPrintRow = GetFixedRowCount();
  2309.     // Not only the row, but we need to figure out column, too
  2310.     // Can print 4 pages, where page 1 and 2 represent the same rows but
  2311.     // with different WIDE columns.
  2312.     //
  2313.     // .......
  2314.     // .1 .2 .  If representing page 3  -->    iPageIfIgnoredWideCols = 2
  2315.     // .......                                 iWideColPageOffset = 0
  2316.     // .3 .4 .  If representing page 2  -->    iPageIfIgnoredWideCols = 1
  2317.     // .......                                 iWideColPageOffset = 1
  2318.     int iPageIfIgnoredWideCols = pInfo->m_nCurPage / m_nPageMultiplier;
  2319.     int iWideColPageOffset = pInfo->m_nCurPage - ( iPageIfIgnoredWideCols * m_nPageMultiplier);
  2320.     if( iWideColPageOffset > 0)
  2321.         iPageIfIgnoredWideCols++;
  2322.     if( iWideColPageOffset == 0)
  2323.         iWideColPageOffset = m_nPageMultiplier;
  2324.     iWideColPageOffset--;
  2325.     // calculate current print row based on iPageIfIgnoredWideCols
  2326.     while(  m_nCurrPrintRow < GetRowCount()
  2327.             && (int)nNumPages < iPageIfIgnoredWideCols)
  2328.     {
  2329.         nTotalRowHeight += GetRowHeight(m_nCurrPrintRow);
  2330.         if (nTotalRowHeight > m_nPageHeight) {
  2331.             nNumPages++;
  2332.             if ((int)nNumPages == iPageIfIgnoredWideCols) break;
  2333.             nTotalRowHeight = GetRowHeight(m_nCurrPrintRow);
  2334.         }
  2335.         m_nCurrPrintRow++;
  2336.     }
  2337.     m_nPrintColumn = GetFixedColumnCount();
  2338.     int iTotalRowWidth = 0;
  2339.     int i1, i2;
  2340.     // now, calculate which print column to start displaying
  2341.     for( i1 = 0; i1 < iWideColPageOffset; i1++)
  2342.     {
  2343.         for( i2 = m_nPrintColumn; i2 < GetColumnCount(); i2++)
  2344.         {
  2345.             iTotalRowWidth += GetColumnWidth( i2);
  2346.             if( iTotalRowWidth > m_nPageWidth)
  2347.             {
  2348.                 m_nPrintColumn = i2;
  2349.                 iTotalRowWidth = 0;
  2350.                 break;
  2351.             }
  2352.         }
  2353.     }
  2354.     PrintRowButtons( pDC, pInfo);   // print row buttons on each page
  2355.     int iColumnOffset = 0;
  2356.     for( i1=0; i1 < GetFixedColumnCount(); i1++)
  2357.     {
  2358.         iColumnOffset += GetColumnWidth( i1);
  2359.     }
  2360.     // Print the column headings
  2361.     pInfo->m_rectDraw.bottom = GetFixedRowHeight();
  2362.     if( m_nPrintColumn == GetFixedColumnCount())
  2363.     {
  2364.         // have the column headings fcn draw the upper left fixed cells
  2365.         //  for the very first columns, only
  2366.         pDC->OffsetWindowOrg( 0, +GetFixedRowHeight());
  2367.         m_nPageWidth += iColumnOffset;
  2368.         m_nPrintColumn = 0;
  2369.         PrintColumnHeadings(pDC, pInfo);
  2370.         m_nPageWidth -= iColumnOffset;
  2371.         m_nPrintColumn = GetFixedColumnCount();
  2372.         pDC->OffsetWindowOrg( -iColumnOffset, -GetFixedRowHeight());
  2373.     }
  2374.     else
  2375.     {
  2376.         pDC->OffsetWindowOrg( -iColumnOffset, +GetFixedRowHeight());
  2377.         PrintColumnHeadings(pDC, pInfo);
  2378.         pDC->OffsetWindowOrg( 0, -GetFixedRowHeight());
  2379.     }
  2380.     if (m_nCurrPrintRow >= GetRowCount()) return;
  2381.     // Draw as many rows as will fit on the printed page.
  2382.     // Clip the printed page so that there is no partially shown
  2383.     // row at the bottom of the page (the same row which will be fully
  2384.     // shown at the top of the next page).
  2385.     BOOL bFirstPrintedRow = TRUE;
  2386.     CRect rect;
  2387.     rect.bottom = -1;
  2388.     while (m_nCurrPrintRow < GetRowCount())
  2389.     {
  2390.         rect.top = rect.bottom+1;
  2391.         rect.bottom = rect.top + GetRowHeight(m_nCurrPrintRow) - 1;
  2392.         if (rect.bottom > m_nPageHeight) break;            // Gone past end of page
  2393.         rect.right = -1;
  2394.         // modified to allow printing of wide grids on multiple pages
  2395.         for (int col = m_nPrintColumn; col < GetColumnCount(); col++)
  2396.         {
  2397.             rect.left = rect.right+1;
  2398.             rect.right =    rect.left
  2399.                             + GetColumnWidth( col)
  2400.                             - 1;
  2401.             if( rect.right > m_nPageWidth)
  2402.                 break;
  2403.             CGridCellBase* pCell = GetCell(m_nCurrPrintRow, col);
  2404.             if (pCell)
  2405.                 pCell->PrintCell(pDC, m_nCurrPrintRow, col, rect);
  2406.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
  2407.             {
  2408.                 int Overlap = (col == 0)? 0:1;
  2409.                 pDC->MoveTo(rect.left-Overlap, rect.bottom);
  2410.                 pDC->LineTo(rect.right, rect.bottom);
  2411.                 if (m_nCurrPrintRow == 0) {
  2412.                     pDC->MoveTo(rect.left-Overlap, rect.top);
  2413.                     pDC->LineTo(rect.right, rect.top);
  2414.                 }
  2415.             }
  2416.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
  2417.             {
  2418.                 int Overlap = (bFirstPrintedRow)? 0:1;
  2419.                 pDC->MoveTo(rect.right, rect.top-Overlap);
  2420.                 pDC->LineTo(rect.right, rect.bottom);
  2421.                 if (col == 0) {
  2422.                     pDC->MoveTo(rect.left, rect.top-Overlap);
  2423.                     pDC->LineTo(rect.left, rect.bottom);
  2424.                 }
  2425.             }
  2426.         }
  2427.         m_nCurrPrintRow++;
  2428.         bFirstPrintedRow = FALSE;
  2429.     }
  2430.     // Footer
  2431.     pInfo->m_rectDraw.bottom = m_nFooterHeight * m_CharSize.cy;
  2432.     pDC->SetWindowOrg( -m_nLeftMargin * m_CharSize.cx,
  2433.         -m_LogicalPageSize.cy + m_nFooterHeight * m_CharSize.cy);
  2434.     PrintFooter(pDC, pInfo);
  2435.     // SetWindowOrg back for next page
  2436.     pDC->SetWindowOrg(0,0);
  2437.     pDC->SelectObject(pOldFont);
  2438. }
  2439. void CGridCtrl::PrintColumnHeadings(CDC *pDC, CPrintInfo* /*pInfo*/)
  2440. {
  2441.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  2442.     CRect rect;
  2443.     rect.bottom = -1;
  2444.     BOOL bFirst = TRUE;
  2445.     // modified to allow column hdr printing of multi-page wide grids
  2446.     for (int row = 0; row < GetFixedRowCount(); row++)
  2447.     {
  2448.         rect.top = rect.bottom+1;
  2449.         rect.bottom = rect.top + GetRowHeight(row) - 1;
  2450.         rect.right = -1;
  2451.         for (int col = m_nPrintColumn; col < GetColumnCount(); col++)
  2452.         {
  2453.             rect.left = rect.right+1;
  2454.             rect.right =    rect.left
  2455.                             + GetColumnWidth( col)
  2456.                             - 1;
  2457.             if( rect.right > m_nPageWidth)
  2458.                 break;
  2459.             CGridCellBase* pCell = GetCell(row, col);
  2460.             if (pCell)
  2461.                 pCell->PrintCell(pDC, row, col, rect);
  2462.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
  2463.             {
  2464.                 int Overlap = (col == 0)? 0:1;
  2465.                 pDC->MoveTo(rect.left-Overlap, rect.bottom);
  2466.                 pDC->LineTo(rect.right, rect.bottom);
  2467.                 if (row == 0) {
  2468.                     pDC->MoveTo(rect.left-Overlap, rect.top);
  2469.                     pDC->LineTo(rect.right, rect.top);
  2470.                 }
  2471.             }
  2472.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
  2473.             {
  2474.                 int Overlap = (row == 0)? 0:1;
  2475.                 pDC->MoveTo(rect.right, rect.top-Overlap);
  2476.                 pDC->LineTo(rect.right, rect.bottom);
  2477.                 if( bFirst) {
  2478.                     pDC->MoveTo(rect.left-1, rect.top-Overlap);
  2479.                     pDC->LineTo(rect.left-1, rect.bottom);
  2480.                     bFirst = FALSE;
  2481.                 }
  2482.             }
  2483.         }
  2484.     }
  2485.     pDC->SelectObject(pOldFont);
  2486. }
  2487. /*****************************************************************************
  2488. Prints line of row buttons on each page of the printout.  Assumes that
  2489. the window origin is setup before calling
  2490. *****************************************************************************/
  2491. void CGridCtrl::PrintRowButtons(CDC *pDC, CPrintInfo* /*pInfo*/)
  2492. {
  2493.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  2494.     CRect rect;
  2495.     rect.right = -1;
  2496.     BOOL bFirst = TRUE;
  2497.     for( int iCol = 0; iCol < GetFixedColumnCount(); iCol++)
  2498.     {
  2499.         rect.left = rect.right+1;
  2500.         rect.right =    rect.left
  2501.                         + GetColumnWidth( iCol)
  2502.                         - 1;
  2503.         rect.bottom = -1;
  2504.         for( int iRow = m_nCurrPrintRow; iRow < GetRowCount(); iRow++)
  2505.         {
  2506.             rect.top = rect.bottom+1;
  2507.             rect.bottom = rect.top + GetRowHeight( iRow) - 1;
  2508.             if( rect.bottom > m_nPageHeight)
  2509.                 break;
  2510.             CGridCellBase* pCell = GetCell(iRow, iCol);
  2511.             if (pCell)
  2512.                 pCell->PrintCell(pDC, iRow, iCol, rect);
  2513.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
  2514.             {
  2515.                 int Overlap = (iCol == 0)? 0:1;
  2516.                 pDC->MoveTo(rect.left-Overlap, rect.bottom);
  2517.                 pDC->LineTo(rect.right, rect.bottom);
  2518.                 if( bFirst) {
  2519.                     pDC->MoveTo(rect.left-Overlap, rect.top-1);
  2520.                     pDC->LineTo(rect.right, rect.top-1);
  2521.                     bFirst = FALSE;
  2522.                 }
  2523.             }
  2524.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
  2525.             {
  2526.                 int Overlap = (iRow == 0)? 0:1;
  2527.                 pDC->MoveTo(rect.right, rect.top-Overlap);
  2528.                 pDC->LineTo(rect.right, rect.bottom);
  2529.                 if (iCol == 0) {
  2530.                     pDC->MoveTo(rect.left, rect.top-Overlap);
  2531.                     pDC->LineTo(rect.left, rect.bottom);
  2532.                 }
  2533.             }
  2534.         }
  2535.     }
  2536.     pDC->SelectObject(pOldFont);
  2537. }
  2538. void CGridCtrl::PrintHeader(CDC *pDC, CPrintInfo *pInfo)
  2539. {
  2540.     // print App title on top right margin
  2541.     CString strRight;
  2542.     strRight.LoadString(AFX_IDS_APP_TITLE);
  2543.     // print parent window title in the centre (Gert Rijs)
  2544.     CString strCenter;
  2545.     CWnd *pParentWnd = GetParent();
  2546.     while (pParentWnd)
  2547.     {
  2548.         pParentWnd->GetWindowText(strCenter);
  2549.         if (strCenter.GetLength())  // can happen if it is a CView, CChildFrm has the title
  2550.             break;
  2551.         pParentWnd = pParentWnd->GetParent();
  2552.     }
  2553.     CFont   BoldFont;
  2554.     LOGFONT lf;
  2555.     //create bold font for header and footer
  2556.     VERIFY(m_PrinterFont.GetLogFont(&lf));
  2557.     lf.lfWeight = FW_BOLD;
  2558. lf.lfWidth=18;
  2559. lf.lfHeight=18;
  2560.     VERIFY(BoldFont.CreateFontIndirect(&lf));
  2561.     CFont *pNormalFont = pDC->SelectObject(&BoldFont);
  2562.     int nPrevBkMode = pDC->SetBkMode(TRANSPARENT);
  2563.     CRect   rc(pInfo->m_rectDraw);
  2564.     if( !strCenter.IsEmpty() )
  2565.         pDC->DrawText( strCenter, &rc, DT_CENTER | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
  2566.     if( !strRight.IsEmpty() )
  2567.         pDC->DrawText( strRight, &rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
  2568.     pDC->SetBkMode(nPrevBkMode);
  2569.     pDC->SelectObject(pNormalFont);
  2570.     BoldFont.DeleteObject();
  2571.     // draw ruled-line across top
  2572.     pDC->SelectStockObject(BLACK_PEN);
  2573.     pDC->MoveTo(rc.left, rc.bottom);
  2574.     pDC->LineTo(rc.right, rc.bottom);
  2575. }
  2576. //print footer with a line and date, and page number
  2577. void CGridCtrl::PrintFooter(CDC *pDC, CPrintInfo *pInfo)
  2578. {
  2579.     // page numbering on left
  2580.     CString strLeft;
  2581.     strLeft.Format(_T("ҳ: %d of %d"), pInfo->m_nCurPage, pInfo->GetMaxPage() );
  2582.     // date and time on the right
  2583.     CString strRight;
  2584.     CTime t = CTime::GetCurrentTime();
  2585.     strRight = t.Format(_T("%c"));
  2586.     
  2587.     CRect rc(pInfo->m_rectDraw);
  2588.     // draw ruled line on bottom
  2589.     pDC->SelectStockObject(BLACK_PEN);
  2590.     pDC->MoveTo(rc.left, rc.top);
  2591.     pDC->LineTo(rc.right, rc.top);
  2592.     CFont BoldFont;
  2593.     LOGFONT lf;
  2594.     //create bold font for header and footer
  2595.     m_PrinterFont.GetLogFont(&lf);
  2596.     lf.lfWeight = FW_BOLD;
  2597.     BoldFont.CreateFontIndirect(&lf);
  2598.     CFont *pNormalFont = pDC->SelectObject(&BoldFont);
  2599.     int nPrevBkMode = pDC->SetBkMode(TRANSPARENT);
  2600.     // EFW - Bug fix - Force text color to black.  It doesn't always
  2601.     // get set to a printable color when it gets here.
  2602.     pDC->SetTextColor(RGB(0, 0, 0));
  2603.     if( !strLeft.IsEmpty() )
  2604.         pDC->DrawText( strLeft, &rc, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
  2605.     if( !strRight.IsEmpty() )
  2606.         pDC->DrawText( strRight, &rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
  2607.     pDC->SetBkMode(nPrevBkMode);
  2608.     pDC->SelectObject(pNormalFont);
  2609.     BoldFont.DeleteObject();
  2610. }
  2611. void CGridCtrl::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  2612. {
  2613.     m_PrinterFont.DeleteObject();
  2614. }
  2615. #endif  // !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  2616. #ifndef _WIN32_WCE
  2617. /////////////////////////////////////////////////////////////////////////////
  2618. // CGridCtrl persistance
  2619. BOOL CGridCtrl::Save(LPCTSTR filename)
  2620. {
  2621.     CStdioFile File;
  2622.     CFileException ex;
  2623.     if (!File.Open(filename, CFile::modeWrite | CFile::modeCreate| CFile::typeText, &ex))
  2624.     {
  2625.         ex.ReportError();
  2626.         return FALSE;
  2627.     }
  2628.     TRY
  2629.     {
  2630.         int nNumColumns = GetColumnCount();
  2631.         for (int i = 0; i < nNumColumns; i++)
  2632.         {
  2633.             File.WriteString(GetItemText(0,i));
  2634.             File.WriteString((i==(nNumColumns-1))? _T("n"):_T(","));
  2635.         }
  2636.         for (i = 0; i < GetRowCount(); i++)
  2637.         {
  2638.             for (int j = 0; j < nNumColumns; j++)
  2639.             {
  2640.                 File.WriteString(GetItemText(i,j));
  2641.                 File.WriteString((j==(nNumColumns-1))? _T("n"):_T(","));
  2642.             }
  2643.         }
  2644.         File.Close();
  2645.     }
  2646.     CATCH (CFileException, e)
  2647.     {
  2648.         AfxMessageBox(_T("Unable to save grid list"));
  2649.         return FALSE;
  2650.     }
  2651.     END_CATCH
  2652.     return TRUE;
  2653. }
  2654. BOOL CGridCtrl::Load(LPCTSTR filename)
  2655. {
  2656.     if (GetVirtualMode())
  2657.         return FALSE;
  2658.     TCHAR *token, *end;
  2659.     TCHAR buffer[1024];
  2660.     CStdioFile File;
  2661.     CFileException ex;
  2662.     if (!File.Open(filename, CFile::modeRead | CFile::typeText))
  2663.     {
  2664.         ex.ReportError();
  2665.         return FALSE;
  2666.     }
  2667.     DeleteAllItems();
  2668.     TRY
  2669.     {
  2670.         // Read Header off file
  2671.         File.ReadString(buffer, 1024);
  2672.         // Get first token
  2673.         for (token=buffer, end=buffer;
  2674.              *end && (*end != _T(',')) && (*end != _T('n')); 
  2675.              end++)
  2676.             ;
  2677.         if ((*end == _T('')) && (token == end))
  2678.             token = NULL;
  2679.         *end = _T('');
  2680.         while (token)
  2681.         {
  2682.             InsertColumn(token);
  2683.             // Get next token
  2684.             for (token=++end; *end && (*end != _T(',')) && (*end != _T('n'));
  2685.                end++)
  2686.                 ;
  2687.             if ((*end == _T('')) && (token == end))
  2688.                 token = NULL;
  2689.             *end = _T('');
  2690.         }
  2691.         // Read in rest of data
  2692.         int nItem = 0;
  2693.         while (File.ReadString(buffer, 1024))
  2694.         {
  2695.             // Get first token
  2696.             for (token=buffer, end=buffer;
  2697.               *end && (*end != _T(',')) && (*end != _T('n')); end++)
  2698.                 ;
  2699.             if ((*end == _T('')) && (token == end))
  2700.                 token = NULL;
  2701.             *end = _T('');
  2702.             int nSubItem = 0;
  2703.             while (token)
  2704.             {
  2705.                 if (!nSubItem)
  2706.                     InsertRow(token);
  2707.                 else
  2708.                     SetItemText(nItem, nSubItem, token);
  2709.                 // Get next token
  2710.                 for (token=++end; *end && (*end != _T(',')) && (*end != _T('n'));
  2711.                   end++)
  2712.                     ;
  2713.                 if ((*end == _T('')) && (token == end))
  2714.                     token = NULL;
  2715.                 *end = _T('');
  2716.                 nSubItem++;
  2717.             }
  2718.             nItem++;
  2719.         }
  2720.         AutoSizeColumns(GetAutoSizeStyle());
  2721.         File.Close();
  2722.     }
  2723.     CATCH (CFileException, e)
  2724.     {
  2725.         AfxMessageBox(_T("Unable to load grid data"));
  2726.         return FALSE;
  2727.     }
  2728.     END_CATCH
  2729.     return TRUE;
  2730. }
  2731. #endif
  2732. /////////////////////////////////////////////////////////////////////////////
  2733. // CGridCtrl overrideables
  2734. #ifndef GRIDCONTROL_NO_DRAGDROP
  2735. // This is no longer needed since I've changed to OLE drag and drop - but it's
  2736. // still cool code. :)
  2737. CImageList* CGridCtrl::CreateDragImage(CPoint *pHotSpot)
  2738. {
  2739.     CDC* pDC = GetDC();
  2740.     if (!pDC)
  2741.         return NULL;
  2742.     CRect rect;
  2743.     CCellID cell = GetFocusCell();
  2744.     if (!GetCellRect(cell.row, cell.col, rect))
  2745.         return NULL;
  2746.     
  2747.     // Translate coordinate system
  2748.     rect.BottomRight() = CPoint(rect.Width(), rect.Height());
  2749.     rect.TopLeft()     = CPoint(0, 0);
  2750.     *pHotSpot = rect.BottomRight(); 
  2751.     
  2752.     // Create a new imagelist (the caller of this function has responsibility
  2753.     // for deleting this list)
  2754.     CImageList* pList = new CImageList;
  2755.     if (!pList || !pList->Create(rect.Width(), rect.Height(), ILC_MASK, 1, 1))
  2756.     {    
  2757.         if (pList)
  2758.             delete pList;
  2759.         return NULL;
  2760.     }
  2761.     
  2762.     // Create mem DC and bitmap
  2763.     CDC MemDC;
  2764.     CBitmap bm;
  2765.     MemDC.CreateCompatibleDC(pDC);
  2766.     bm.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  2767.     CBitmap* pOldBitmap = MemDC.SelectObject(&bm);
  2768.     MemDC.SetWindowOrg(0, 0);
  2769.     
  2770.     // Draw cell onto bitmap in memDC
  2771.     CGridCellBase* pCell = GetCell(cell.row, cell.col);
  2772.     if (pCell)
  2773.         pCell->Draw(&MemDC, cell.row, cell.col, rect, FALSE);
  2774.     
  2775.     // Clean up
  2776.     MemDC.SelectObject(pOldBitmap);
  2777.     ReleaseDC(pDC);
  2778.     
  2779.     // Add the bitmap we just drew to the image list.
  2780.     pList->Add(&bm, GetDefaultCell(FALSE, FALSE)->GetBackClr());
  2781.     bm.DeleteObject();
  2782.     return pList;
  2783. }
  2784. #endif
  2785. void CGridCtrl::OnFixedRowClick(CCellID& cell)
  2786. {
  2787.     if (!IsValid(cell))
  2788.         return;
  2789.     if (GetHeaderSort())
  2790.     {
  2791.         CWaitCursor waiter;
  2792.         if (cell.col == GetSortColumn())
  2793.             SortTextItems(cell.col, !GetSortAscending());
  2794.         else
  2795.             SortTextItems(cell.col, TRUE);
  2796.         Invalidate();
  2797.     }
  2798.     if (GetFixedRowSelection())
  2799.     {
  2800.         if (cell.col < GetFixedColumnCount())
  2801.         {
  2802.             m_MouseMode = MOUSE_SELECT_ALL;
  2803.             OnSelecting(cell);
  2804.         }
  2805.         else 
  2806.         {
  2807.             m_MouseMode = MOUSE_SELECT_COL;
  2808.             OnSelecting(cell);
  2809.         }
  2810.     }
  2811. }
  2812. void CGridCtrl::OnFixedColumnClick(CCellID& cell)
  2813. {
  2814.     if (!IsValid(cell))
  2815.         return;
  2816. //    if (m_bListMode && (GetItemState(cell.row, m_nFixedCols) & GVNI_SELECTED))
  2817. //    {
  2818. //        OnEditCell(cell.row, cell.col, VK_LBUTTON);
  2819. //        return;
  2820. //    }
  2821.     if (GetFixedColumnSelection())
  2822.     {
  2823.         if (cell.row < GetFixedRowCount())
  2824.         {
  2825.             m_MouseMode = MOUSE_SELECT_ALL;
  2826.             OnSelecting(cell);
  2827.         }
  2828.         else
  2829.         {
  2830.             m_MouseMode = MOUSE_SELECT_ROW;
  2831.             OnSelecting(cell);
  2832.         }
  2833.     }
  2834. }
  2835. // Gets the extent of the text pointed to by str (no CDC needed)
  2836. // By default this uses the selected font (which is a bigger font)
  2837. CSize CGridCtrl::GetTextExtent(int nRow, int nCol, LPCTSTR str)
  2838. {
  2839.     CGridCellBase* pCell = GetCell(nRow, nCol);
  2840.     if (!pCell)
  2841.         return CSize(0, 0);
  2842.     else
  2843.         return pCell->GetTextExtent(str);
  2844. }
  2845. void CGridCtrl::OnEditCell(int nRow, int nCol, CPoint point, UINT nChar)
  2846. {
  2847. #ifndef GRIDCONTROL_NO_TITLETIPS
  2848.     m_TitleTip.Hide();  // hide any titletips
  2849. #endif
  2850.     // Can we do it?
  2851.     CCellID cell(nRow, nCol);
  2852.     if (!IsValid(cell) || !IsCellEditable(nRow, nCol))
  2853.         return;
  2854.     // Can we see what we are doing?
  2855.     EnsureVisible(nRow, nCol);
  2856.     if (!IsCellVisible(nRow, nCol))
  2857.         return;
  2858.     // Where, exactly, are we gonna do it??
  2859.     CRect rect;
  2860.     if (!GetCellRect(cell, rect))
  2861.         return;
  2862.     // Tell Mum and Dad what we are doing
  2863.     SendMessageToParent(nRow, nCol, GVN_BEGINLABELEDIT);
  2864.     // Let's do it...
  2865.     CGridCellBase* pCell = GetCell(nRow, nCol);
  2866.     if (pCell)
  2867.         pCell->Edit(nRow, nCol, rect, point, IDC_INPLACE_CONTROL, nChar);
  2868. }
  2869. void CGridCtrl::EndEditing()
  2870. {
  2871.     CCellID cell = GetFocusCell();
  2872.     if (!IsValid(cell)) return;
  2873.     CGridCellBase *pCell = GetCell(cell.row, cell.col);
  2874.     if (pCell)
  2875.         pCell->EndEdit();
  2876. }
  2877. void CGridCtrl::OnEndEditCell(int nRow, int nCol, CString str)
  2878. {
  2879.     CString strCurrent = GetItemText(nRow, nCol);
  2880.     if (strCurrent != str)
  2881.     {
  2882.         SetModified(TRUE, nRow, nCol);
  2883.         SetItemText(nRow, nCol, str);
  2884.     }
  2885.     CGridCellBase* pCell = GetCell(nRow, nCol);
  2886.     if (pCell)
  2887.         pCell->OnEndEdit();
  2888. }
  2889. CString CGridCtrl::GetItemText(int nRow, int nCol) const
  2890. {
  2891.     if (nRow < 0 || nRow >= m_nRows || nCol < 0 || nCol >= m_nCols)
  2892.         return _T("");
  2893.     CGridCellBase* pCell = GetCell(nRow, nCol);
  2894.     ASSERT(pCell);
  2895.     if (!pCell)
  2896.         return _T("");
  2897.     return pCell->GetText();
  2898. }