GridCtrl.cpp
上传用户:zhanglf88
上传日期:2013-11-19
资源大小:6036k
文件大小:170k
源码类别:

金融证券系统

开发平台:

Visual C++

  1.     if (GetSafeHwnd() && m_bAllowDraw)
  2.         Invalidate();
  3.     return TRUE;
  4. }
  5. BOOL CGridCtrl::SetFixedColumnCount(int nFixedCols)
  6. {
  7.     ASSERT(nFixedCols >= 0);
  8.     if (nFixedCols > GetColumnCount())
  9.         if (!SetColumnCount(nFixedCols)) return FALSE;
  10.     if (m_idCurrentCell.col < nFixedCols)
  11.         SetFocusCell(-1,-1);
  12.     m_nFixedCols = nFixedCols;
  13.     if (GetSafeHwnd() && m_bAllowDraw)
  14.         Invalidate();
  15.     return TRUE;
  16. }
  17. BOOL CGridCtrl::SetRowCount(int nRows)
  18. {
  19.     ASSERT(nRows >= 0);
  20.     if (nRows == GetRowCount()) return TRUE;
  21.     if (nRows < m_nFixedRows) 
  22.         m_nFixedRows = nRows;
  23.     if (m_idCurrentCell.row >= nRows)
  24.         SetFocusCell(-1,-1);
  25.     int addedRows = nRows - GetRowCount();
  26.     // If we are about to lose rows, then we need to delete the GridCell objects 
  27.     // in each column within each row
  28.     if (addedRows < 0) {
  29.         for (int row = nRows; row < m_nRows; row++)
  30.         {
  31.             // Delete cells
  32.             for (int col = 0; col < m_nCols; col++) 
  33.             {
  34.                 CGridCell* pCell = GetCell(row, col);
  35.                 if (pCell) {
  36.                     EmptyCell(pCell, row, col);
  37.                     delete pCell;
  38.                 }
  39.             }
  40.             // Delete rows
  41.             GRID_ROW* pRow = m_RowData[row];
  42.             if (pRow) delete pRow;
  43.         }
  44.     }
  45.     // Change the number of rows.
  46.     m_nRows = nRows;
  47.     m_RowData.SetSize(m_nRows);
  48.     m_arRowHeights.SetSize(nRows);
  49.     // If we have just added rows, we need to construct new elements for each cell
  50.     // and set the default row height
  51.     if (addedRows > 0) {
  52.         // initialize row heights and data
  53.         int startRow = nRows - addedRows;
  54.         for (int row = startRow; row < GetRowCount(); row++) {
  55.             m_arRowHeights[row] = m_nDefCellHeight;
  56.             m_RowData[row] = new GRID_ROW;
  57.             m_RowData[row]->SetSize(m_nCols);
  58.             for (int col = 0; col < m_nCols; col++)
  59.             {
  60.                 GRID_ROW* pRow = m_RowData[row];
  61.                 if (pRow) pRow->SetAt(col, CreateCell(row, col));
  62.             }
  63.         }
  64.     }
  65.     //else
  66.     //    ResetSelectedRange();
  67.     
  68.     if (GetSafeHwnd() && m_bAllowDraw)
  69.     {
  70.         ResetScrollBars();
  71.         Invalidate();
  72.     }
  73.     return TRUE;
  74. }
  75. BOOL CGridCtrl::SetColumnCount(int nCols)
  76. {
  77.     ASSERT(nCols >= 0);
  78.     if (nCols == GetColumnCount()) return TRUE;
  79.     if (nCols < m_nFixedCols) 
  80.         m_nFixedCols = nCols;
  81.     if (m_idCurrentCell.col >= nCols)
  82.         SetFocusCell(-1,-1);
  83.     int addedCols = nCols - GetColumnCount();
  84.     // If we are about to lose columns, then we need to delete the GridCell objects 
  85.     // within each column
  86.     if (addedCols < 0) {
  87.         for (int row = 0; row < m_nRows; row++)
  88.             for (int col = nCols; col < GetColumnCount(); col++)
  89.             {
  90.                 CGridCell* pCell = GetCell(row, col);
  91.                 if (pCell) {
  92.                     EmptyCell(pCell, row, col);
  93.                     delete pCell;
  94.                 }
  95.             }
  96.     }
  97.     // Change the number of columns.
  98.     m_nCols = nCols;
  99.     m_arColWidths.SetSize(nCols);
  100.     // Change the number of columns in each row.
  101.     for (int i = 0; i < m_nRows; i++)
  102.         if (m_RowData[i]) m_RowData[i]->SetSize(nCols);
  103.     // If we have just added columns, we need to construct new elements for each cell
  104.     // and set the default column width
  105.     if (addedCols > 0)
  106.     {
  107.         // initialized column widths
  108.         int startCol = nCols - addedCols;
  109.         for (int col = startCol; col < GetColumnCount(); col++)
  110.             m_arColWidths[col] = m_nDefCellWidth;
  111.         // initialise column data
  112.         for (int row = 0; row < m_nRows; row++)
  113.             for (col = startCol; col < GetColumnCount(); col++)
  114.             {
  115.                 GRID_ROW* pRow = m_RowData[row];
  116.                 if (pRow) pRow->SetAt(col, CreateCell(row,col));
  117.             }
  118.     }
  119.     //else    // check for selected cell ranges
  120.     //    ResetSelectedRange();
  121.     
  122.     if (GetSafeHwnd() && m_bAllowDraw)
  123.     {
  124.         ResetScrollBars();
  125.         Invalidate();
  126.     }
  127.     return TRUE;
  128. }
  129. // Insert a column at a given position, or add to end of columns (if nColumn = -1)
  130. int CGridCtrl::InsertColumn(LPCTSTR strHeading, 
  131.                             UINT nFormat /* = DT_CENTER|DT_VCENTER|DT_SINGLELINE */,
  132.                             int nColumn  /* = -1 */)
  133. {
  134.     // If the insertion is for a specific column, check it's within range.
  135.     if (nColumn >= 0 && nColumn >= GetColumnCount())
  136.         return -1;
  137.     ResetSelectedRange();
  138.     // Gotta be able to at least _see_ some of the column.
  139.     if (m_nRows < 1)
  140.         SetRowCount(1);    
  141.     if (nColumn < 0)
  142.     {
  143.         nColumn = m_nCols;
  144.         m_arColWidths.Add(0);
  145.         for (int row = 0; row < m_nRows; row++) 
  146.         {
  147.             GRID_ROW* pRow = m_RowData[row];
  148.             if (!pRow) return -1;
  149.             pRow->Add(CreateCell(row, nColumn));
  150.         }
  151.     } 
  152.     else
  153.     {
  154.         m_arColWidths.InsertAt(nColumn, (UINT)0);
  155.         for (int row = 0; row < m_nRows; row++) 
  156.         {
  157.             GRID_ROW* pRow = m_RowData[row];
  158.             if (!pRow) return -1;
  159.             pRow->InsertAt(nColumn, CreateCell(row, nColumn));
  160.         }
  161.     }
  162.     m_nCols++;
  163.     // Initialise column data
  164.     SetItemText(0, nColumn, strHeading);
  165.     for (int row = 0; row < m_nRows; row++) 
  166.     {
  167.         SetItemFormat(row, nColumn, nFormat);
  168.     }
  169.     // initialized column width
  170.     m_arColWidths[nColumn] = GetTextExtent(strHeading).cx;
  171.     if (m_idCurrentCell.col != -1 && nColumn < m_idCurrentCell.col)
  172.         m_idCurrentCell.col++;
  173.     
  174.     ResetScrollBars();
  175.     return nColumn;
  176. }
  177. // Insert a row at a given position, or add to end of rows (if nRow = -1)
  178. int CGridCtrl::InsertRow(LPCTSTR strHeading, int nRow /* = -1 */)
  179. {
  180.     // If the insertion is for a specific row, check it's within range.
  181.     if (nRow >= 0 && nRow >= GetRowCount())
  182.         return -1;
  183.     ResetSelectedRange();
  184.     // Gotta be able to at least _see_ some of the row.
  185.     if (m_nCols < 1) 
  186.         SetColumnCount(1);    
  187.     // Adding a row to the bottom
  188.     if (nRow < 0) 
  189.     {
  190.         nRow = m_nRows;
  191.         m_arRowHeights.Add(0);
  192.         m_RowData.Add(new GRID_ROW);
  193.     } 
  194.     else 
  195.     {
  196.         m_arRowHeights.InsertAt(nRow, (UINT)0);
  197.         m_RowData.InsertAt(nRow, new GRID_ROW);
  198.     }
  199.     
  200.     m_nRows++;
  201.     m_RowData[nRow]->SetSize(m_nCols);
  202.     // Initialise cell data
  203.     for (int col = 0; col < m_nCols; col++)
  204.     {
  205.         GRID_ROW* pRow = m_RowData[nRow];
  206.         if (!pRow) return -1;
  207.         pRow->SetAt(col, CreateCell(nRow, col));
  208.     }
  209.     // Set row title
  210.     SetItemText(nRow, 0, strHeading);
  211.     // initialized row height
  212.     m_arRowHeights[nRow] = GetTextExtent(strHeading).cy;
  213.     if (m_idCurrentCell.row != -1 && nRow < m_idCurrentCell.row)
  214.         m_idCurrentCell.row++;
  215.     
  216.     ResetScrollBars();
  217.     return nRow;
  218. }
  219. // Creates a new grid cell and performs any necessary initialisation
  220. CGridCell* CGridCtrl::CreateCell(int nRow, int nCol)
  221. {
  222.     CGridCell* pCell = new CGridCell;
  223.     if (!pCell)
  224.         return NULL;
  225.     
  226.     // Make format same as cell above
  227.     if (nRow > 0 && nCol >= 0 && nCol < m_nCols)
  228.         pCell->nFormat = GetItemFormat(nRow-1, nCol);
  229.  
  230.     // Make font default grid font
  231.     memcpy(&(pCell->lfFont), &m_Logfont, sizeof(LOGFONT));
  232.     
  233.     return pCell;
  234. }
  235. // Performs any cell cleanup necessary to maintain grid integrity
  236. void CGridCtrl::EmptyCell(CGridCell* pCell, int nRow, int nCol)
  237. {
  238.     // Set the cells state to 0. If the cell is selected, this
  239.     // will remove the cell from the selected list.
  240.     SetItemState(nRow, nCol, 0);
  241.     // Empty strings
  242.     pCell->szText.Empty();
  243. }
  244. BOOL CGridCtrl::DeleteColumn(int nColumn)
  245. {
  246.     if (nColumn < 0 || nColumn >= GetColumnCount())
  247.         return FALSE;
  248.     ResetSelectedRange();
  249.     for (int row = 0; row < GetRowCount(); row++) 
  250.     {
  251.         GRID_ROW* pRow = m_RowData[row];
  252.         if (!pRow) return FALSE;
  253.         CGridCell* pCell = pRow->GetAt(nColumn);
  254.         if (pCell) {
  255.             EmptyCell(pCell, row, nColumn);
  256.             delete pCell;
  257.         }
  258.         pRow->RemoveAt(nColumn);
  259.     }
  260.     m_arColWidths.RemoveAt(nColumn);
  261.     m_nCols--;
  262.     if (nColumn < m_nFixedCols) m_nFixedCols--;
  263.     if (nColumn == m_idCurrentCell.col)
  264.         m_idCurrentCell.row = m_idCurrentCell.col = -1;
  265.     else if (nColumn < m_idCurrentCell.col)
  266.         m_idCurrentCell.col--;
  267.     ResetScrollBars();
  268.     return TRUE;
  269. }
  270. BOOL CGridCtrl::DeleteRow(int nRow)
  271. {
  272.     if ( nRow < 0 || nRow >= GetRowCount())
  273.         return FALSE;
  274.     GRID_ROW* pRow = m_RowData[nRow];
  275.     if (!pRow) return FALSE;
  276.     ResetSelectedRange();
  277.     for (int col = 0; col < GetColumnCount(); col++) 
  278.     {
  279.         CGridCell* pCell = pRow->GetAt(col);
  280.         if (pCell) {
  281.             EmptyCell(pCell, nRow, col);
  282.             delete pCell;
  283.         }
  284.     }
  285.     delete pRow;
  286.     m_RowData.RemoveAt(nRow);
  287.     m_arRowHeights.RemoveAt(nRow);
  288.     m_nRows--;
  289.     if (nRow < m_nFixedRows) m_nFixedRows--;
  290.     if (nRow == m_idCurrentCell.row)
  291.         m_idCurrentCell.row = m_idCurrentCell.col = -1;
  292.     else if (nRow < m_idCurrentCell.row)
  293.         m_idCurrentCell.row--;
  294.     ResetScrollBars();
  295.     return TRUE;
  296. }
  297. // Handy function that removes all non-fixed rows
  298. BOOL CGridCtrl::DeleteNonFixedRows()
  299. {
  300.     int nFixed = GetFixedRowCount();
  301.     int nCount = GetRowCount();
  302.     // Delete all data rows
  303.     for (int nRow = nCount; nRow >= nFixed; nRow--) 
  304.         DeleteRow(nRow);
  305.     return TRUE;
  306. }
  307. // Removes all rows, columns and data from the grid.
  308. BOOL CGridCtrl::DeleteAllItems()
  309. {
  310.     ResetSelectedRange();
  311.     m_arColWidths.RemoveAll();
  312.     m_arRowHeights.RemoveAll();
  313.     // Delete all cells in the grid
  314.     for (int row = 0; row < m_nRows; row++) 
  315.     {
  316.         GRID_ROW* pRow = m_RowData[row];
  317.         if (!pRow) continue;
  318.         for (int col = 0; col < m_nCols; col++)
  319.         {
  320.             CGridCell* pCell = pRow->GetAt(col);
  321.             if (pCell) {
  322.                 EmptyCell(pCell, row, col);  // TODO - this is a bit of a performance hit.
  323.                 delete pCell;                // better to call m_SelectedCells.RemoveAll()
  324.             }                                // instead. This is safer for changes though.
  325.         }
  326.         delete pRow;
  327.     }
  328.     // Remove all rows
  329.     m_RowData.RemoveAll();
  330.     m_idCurrentCell.row = m_idCurrentCell.col = -1;
  331.     m_nRows = m_nFixedRows = m_nCols = m_nFixedCols = 0;
  332.     ResetScrollBars();
  333.     return TRUE;
  334. }
  335. /////////////////////////////////////////////////////////////////////////////
  336. // CGridCtrl data functions
  337. // Set CListCtrl::GetNextItem for details
  338. CCellID CGridCtrl::GetNextItem(CCellID& cell, int nFlags) const
  339. {
  340.     if (nFlags & GVNI_ALL)
  341.     { // GVNI_ALL Search whole Grid beginning from cell
  342. //          First row (cell.row) -- ONLY Columns to the right of cell
  343. //          following rows       -- ALL  Columns
  344.         int row = cell.row , col = cell.col + 1; 
  345.         if (row <= 0) row = GetFixedRowCount();
  346.         for (; row < GetRowCount(); row++)
  347.         {
  348.             if (col <= 0 ) col = GetFixedColumnCount();
  349.             for (; col < GetColumnCount(); col++) 
  350.             {
  351.                 int nState = GetItemState(row, col);
  352.                 if (nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) 
  353.                     return CCellID(row, col);
  354.                 if (nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED)     
  355.                     return CCellID(row, col);
  356.                 if (nFlags & GVNI_SELECTED && nState & GVIS_SELECTED)   
  357.                     return CCellID(row, col);
  358.                 if (nFlags & GVNI_READONLY && nState & GVIS_READONLY)   
  359.                     return CCellID(row, col);    
  360. if (nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED)   
  361. return CCellID(row, col);
  362.             }
  363. // go to First Column
  364. col=GetFixedColumnCount();
  365.         }
  366.     }
  367. else if ((nFlags & GVNI_BELOW) && (nFlags & GVNI_TORIGHT))
  368.     {   // GVNI_AREA Search Grid beginning from cell to Lower-Right of Grid
  369. //           Only rows starting with  cell.row and below
  370. //           All rows   -- ONLY Columns to the right of cell
  371.         int row = cell.row;
  372.         if (row <= 0) row = GetFixedRowCount();
  373.         for (; row < GetRowCount(); row++)
  374.         {
  375.             int col = cell.col + 1;
  376.             if (col <= 0) col = GetFixedColumnCount();
  377.             for (; col < GetColumnCount(); col++) 
  378.             {
  379.                 int nState = GetItemState(row, col);
  380.                 if (nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) 
  381.                     return CCellID(row, col);
  382.                 if (nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED)     
  383.                     return CCellID(row, col);
  384.                 if (nFlags & GVNI_SELECTED && nState & GVIS_SELECTED)   
  385.                     return CCellID(row, col);
  386.                 if (nFlags & GVNI_READONLY && nState & GVIS_READONLY)   
  387.                     return CCellID(row, col);    
  388. if (nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED)   
  389. return CCellID(row, col);
  390.             }
  391.         }
  392.     }
  393.     else if (nFlags & GVNI_ABOVE) 
  394.     {
  395.         for (int row = cell.row-1; row >= GetFixedRowCount(); row--) 
  396.         {
  397.             int nState = GetItemState(row, cell.col);
  398.             if (nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED)
  399.                 return CCellID(row, cell.col);
  400.             if (nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED)    
  401.                 return CCellID(row, cell.col);
  402.             if (nFlags & GVNI_SELECTED && nState & GVIS_SELECTED)  
  403.                 return CCellID(row, cell.col);
  404.             if (nFlags & GVNI_READONLY && nState & GVIS_READONLY)  
  405.                 return CCellID(row, cell.col);
  406.             if (nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED)   
  407.                 return CCellID(row, cell.col);
  408.         }
  409.     }
  410.     else if (nFlags & GVNI_BELOW)
  411.     {
  412.         for (int row = cell.row+1; row < GetRowCount(); row++) 
  413.         {
  414.             int nState = GetItemState(row, cell.col);
  415.             if (nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) 
  416.                 return CCellID(row, cell.col);
  417.             if (nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED)   
  418.                 return CCellID(row, cell.col);
  419.             if (nFlags & GVNI_SELECTED && nState & GVIS_SELECTED)  
  420.                 return CCellID(row, cell.col);
  421.             if (nFlags & GVNI_READONLY && nState & GVIS_READONLY) 
  422.                 return CCellID(row, cell.col);
  423.             if (nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED)   
  424.                 return CCellID(row, cell.col);
  425.         }
  426.     } 
  427.     else if (nFlags & GVNI_TOLEFT)
  428.     {
  429.         for (int col = cell.col-1; col >= GetFixedColumnCount(); col--) 
  430.         {
  431.             int nState = GetItemState(cell.row, col);
  432.             if (nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) 
  433.                 return CCellID(cell.row, col);
  434.             if (nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED)    
  435.                 return CCellID(cell.row, col);
  436.             if (nFlags & GVNI_SELECTED && nState & GVIS_SELECTED)  
  437.                 return CCellID(cell.row, col);
  438.             if (nFlags & GVNI_READONLY && nState & GVIS_READONLY) 
  439.                 return CCellID(cell.row, col);
  440.             if (nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED)   
  441.                 return CCellID(cell.row, col);
  442.        }
  443.     }
  444.     else if (nFlags & GVNI_TORIGHT) 
  445.     {
  446.         for (int col = cell.col+1; col < GetColumnCount(); col++) 
  447.         {
  448.             int nState = GetItemState(cell.row, col);
  449.             if (nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) 
  450.                 return CCellID(cell.row, col);
  451.             if (nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED)  
  452.                 return CCellID(cell.row, col);
  453.             if (nFlags & GVNI_SELECTED && nState & GVIS_SELECTED) 
  454.                 return CCellID(cell.row, col);
  455.             if (nFlags & GVNI_READONLY && nState & GVIS_READONLY) 
  456.                 return CCellID(cell.row, col);
  457.             if (nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED)   
  458.                 return CCellID(cell.row, col);
  459.         }
  460.     }
  461.     return CCellID(-1, -1);
  462. }
  463. // Sorts on a given column using the cell text
  464. BOOL CGridCtrl::SortTextItems(int nCol, BOOL bAscending)
  465. {
  466.     ResetSelectedRange();
  467.     SetFocusCell(-1,-1);
  468.     return SortTextItems(nCol, bAscending, GetFixedRowCount(),-1);
  469. }
  470. // recursive sort implementation
  471. BOOL CGridCtrl::SortTextItems(int nCol, BOOL bAscending, int low, int high)
  472. {
  473.     if (nCol >= GetColumnCount()) return FALSE;
  474.     if (high == -1) high = GetRowCount() - 1;
  475.     int lo = low;
  476.     int hi = high;
  477.     if( hi <= lo ) return FALSE;
  478.     CString midItem = GetItemText( (lo+hi)/2, nCol );
  479.     // loop through the list until indices cross
  480.     while( lo <= hi )
  481.     {
  482.         // Find the first element that is greater than or equal to the partition 
  483.         // element starting from the left Index.
  484.         if( bAscending )
  485.             while (lo < high  && GetItemText(lo, nCol) < midItem)
  486.                 ++lo;
  487.         else
  488.             while (lo < high && GetItemText(lo, nCol) > midItem)
  489.                 ++lo;
  490.         // Find an element that is smaller than or equal to  the partition 
  491.         // element starting from the right Index.
  492.         if( bAscending )
  493.             while (hi > low && GetItemText(hi, nCol) > midItem)
  494.                 --hi;
  495.         else
  496.             while (hi > low && GetItemText(hi, nCol) < midItem)
  497.                 --hi;
  498.         // If the indexes have not crossed, swap if the items are not equal
  499.         if (lo <= hi)
  500.         {
  501.             // swap only if the items are not equal
  502.             if (GetItemText(lo, nCol) != GetItemText(hi, nCol))
  503.             {
  504.                 for (int col = 0; col < GetColumnCount(); col++)
  505.                 {
  506.                     CGridCell *pCell = GetCell(lo, col);
  507.                     SetCell(lo, col, GetCell(hi, col));
  508.                     SetCell(hi, col, pCell);
  509.                 }
  510.                 UINT nRowHeight = m_arRowHeights[lo];
  511.                 m_arRowHeights[lo] = m_arRowHeights[hi];
  512.                 m_arRowHeights[hi] = nRowHeight;
  513.             }
  514.             ++lo;
  515.             --hi;
  516.         }
  517.     }
  518.     // If the right index has not reached the left side of array
  519.     // must now sort the left partition.
  520.     if( low < hi )
  521.         SortTextItems(nCol, bAscending, low, hi);
  522.     // If the left index has not reached the right side of array
  523.     // must now sort the right partition.
  524.     if( lo < high )
  525.         SortTextItems(nCol, bAscending, lo, high);
  526.     return TRUE;
  527. }
  528. // Sorts on a given column using the supplied compare function (see CListCtrl::SortItems)
  529. BOOL CGridCtrl::SortItems(PFNLVCOMPARE pfnCompare, int nCol, BOOL bAscending, 
  530.                           LPARAM data /* = 0 */)
  531. {
  532.     ResetSelectedRange();
  533.     SetFocusCell(-1,-1);
  534.     return SortItems(pfnCompare, nCol, bAscending, data, GetFixedRowCount(), -1);
  535. }
  536. // recursive sort implementation
  537. BOOL CGridCtrl::SortItems(PFNLVCOMPARE pfnCompare, int nCol, BOOL bAscending, LPARAM data,
  538.                           int low, int high)
  539. {
  540.     if (nCol >= GetColumnCount()) return FALSE;
  541.     if (high == -1) high = GetRowCount() - 1;
  542.     int lo = low;
  543.     int hi = high;
  544.     if( hi <= lo ) return FALSE;
  545.     LPARAM midItem = GetItemData( (lo+hi)/2, nCol );
  546.     // loop through the list until indices cross
  547.     while( lo <= hi )
  548.     {
  549.         // Find the first element that is greater than or equal to the partition 
  550.         // element starting from the left Index.
  551.         if( bAscending )
  552.             while (lo < high  && pfnCompare(GetItemData(lo, nCol), midItem, data) < 0)
  553.                 ++lo;
  554.         else
  555.             while (lo < high && pfnCompare(GetItemData(lo, nCol), midItem, data) > 0)
  556.                 ++lo;
  557.         // Find an element that is smaller than or equal to  the partition 
  558.         // element starting from the right Index.
  559.         if( bAscending )
  560.             while (hi > low && pfnCompare(GetItemData(hi, nCol), midItem, data) > 0)
  561.                 --hi;
  562.         else
  563.             while (hi > low && pfnCompare(GetItemData(hi, nCol), midItem, data) < 0)
  564.                 --hi;
  565.         // If the indexes have not crossed, swap if the items are not equal
  566.         if (lo <= hi)
  567.         {
  568.             // swap only if the items are not equal
  569.             if (pfnCompare(GetItemData(lo, nCol), GetItemData(hi, nCol), data) != 0)
  570.             {
  571.                 for (int col = 0; col < GetColumnCount(); col++)
  572.                 {
  573.                     CGridCell *pCell = GetCell(lo, col);
  574.                     SetCell(lo, col, GetCell(hi, col));
  575.                     SetCell(hi, col, pCell);
  576.                 }
  577.                 UINT nRowHeight = m_arRowHeights[lo];
  578.                 m_arRowHeights[lo] = m_arRowHeights[hi];
  579.                 m_arRowHeights[hi] = nRowHeight;
  580.             }
  581.             ++lo;
  582.             --hi;
  583.         }
  584.     }
  585.     // If the right index has not reached the left side of array
  586.     // must now sort the left partition.
  587.     if( low < hi )
  588.         SortItems(pfnCompare, nCol, bAscending, data, low, hi);
  589.     // If the left index has not reached the right side of array
  590.     // must now sort the right partition.
  591.     if( lo < high )
  592.         SortItems(pfnCompare, nCol, bAscending, data, lo, high);
  593.     return TRUE;
  594. }
  595. /////////////////////////////////////////////////////////////////////////////
  596. // CGridCtrl data functions
  597. BOOL CGridCtrl::SetItem(const GV_ITEM* pItem)
  598. {
  599.     if (!pItem) 
  600.         return FALSE;
  601.     CGridCell* pCell = GetCell(pItem->row, pItem->col);
  602.     if (!pCell) 
  603.         return FALSE;
  604.     if (pItem->mask & GVIF_TEXT)   pCell->szText  = pItem->szText;
  605.     if (pItem->mask & GVIF_PARAM)  pCell->lParam  = pItem->lParam;
  606.     if (pItem->mask & GVIF_IMAGE)  pCell->iImage  = pItem->iImage;
  607.     if (pItem->mask & GVIF_STATE)  pCell->state   = pItem->state;
  608.     if (pItem->mask & GVIF_FORMAT) pCell->nFormat = pItem->nFormat;
  609.     if (pItem->mask & GVIF_BKCLR)  pCell->crBkClr = pItem->crBkClr;
  610.     if (pItem->mask & GVIF_FGCLR)  pCell->crFgClr = pItem->crFgClr;
  611.     if (pItem->mask & GVIF_FONT)   memcpy(&(pCell->lfFont), &(pItem->lfFont), sizeof(LOGFONT));
  612.     return TRUE;
  613. }
  614. BOOL CGridCtrl::GetItem(GV_ITEM* pItem)
  615. {
  616.     if (!pItem)
  617.         return FALSE;
  618.     CGridCell* pCell = GetCell(pItem->row, pItem->col);
  619.     if (!pCell) 
  620.         return FALSE;
  621.     if (pItem->mask & GVIF_TEXT)   pItem->szText  = GetItemText(pItem->row, pItem->col);
  622.     if (pItem->mask & GVIF_PARAM)  pItem->lParam  = pCell->lParam;
  623.     if (pItem->mask & GVIF_IMAGE)  pItem->iImage  = pCell->iImage;
  624.     if (pItem->mask & GVIF_STATE)  pItem->state   = pCell->state;
  625.     if (pItem->mask & GVIF_FORMAT) pItem->nFormat = pCell->nFormat;
  626.     if (pItem->mask & GVIF_BKCLR)  pItem->crBkClr = pCell->crBkClr;
  627.     if (pItem->mask & GVIF_FGCLR)  pItem->crFgClr = pCell->crFgClr;
  628.     if (pItem->mask & GVIF_FONT)   memcpy(&(pItem->lfFont), &(pCell->lfFont), sizeof(LOGFONT));
  629.     return TRUE;
  630. }
  631. BOOL CGridCtrl::SetItemText(int nRow, int nCol, LPCTSTR str)
  632. {
  633.     CGridCell* pCell = GetCell(nRow, nCol);
  634.     if (!pCell)
  635.         return FALSE;
  636.     pCell->szText = str;
  637.     return TRUE;
  638. }
  639. BOOL CGridCtrl::SetItemData(int nRow, int nCol, LPARAM lParam)
  640. {
  641.     CGridCell* pCell = GetCell(nRow, nCol);
  642.     if (!pCell) 
  643.         return FALSE;
  644.     pCell->lParam = lParam;
  645.     return TRUE;
  646. }
  647. LPARAM CGridCtrl::GetItemData(int nRow, int nCol) const
  648. {    
  649.     CGridCell* pCell = GetCell(nRow, nCol);
  650.     if (!pCell) 
  651.         return (LPARAM) 0;
  652.     return pCell->lParam;
  653. }
  654. BOOL CGridCtrl::SetItemImage(int nRow, int nCol, int iImage)
  655. {
  656.     CGridCell* pCell = GetCell(nRow, nCol);
  657.     if (!pCell) 
  658.         return FALSE;
  659.     pCell->iImage = iImage;
  660.     return TRUE;
  661. }
  662. int CGridCtrl::GetItemImage(int nRow, int nCol) const
  663. {
  664.     CGridCell* pCell = GetCell(nRow, nCol);
  665.     ASSERT(pCell);
  666.     if (!pCell) 
  667.         return -1;
  668.     return pCell->iImage;
  669. }
  670. BOOL CGridCtrl::SetItemState(int nRow, int nCol, UINT state)
  671. {
  672.     CGridCell* pCell = GetCell(nRow, nCol);
  673.     ASSERT(pCell);
  674.     if (!pCell)
  675.         return FALSE;
  676.     // If the cell is being unselected, remove it from the selected list
  677.     if ((pCell->state & GVIS_SELECTED) && !(state & GVIS_SELECTED))
  678.     {
  679.         CCellID cell;
  680.         DWORD key = MAKELONG(nRow, nCol);
  681.         if (m_SelectedCellMap.Lookup(key, (CCellID&)cell))
  682.             m_SelectedCellMap.RemoveKey(key);
  683.     }
  684.     // If cell is being selected, add it to the list of selected cells
  685.     else if (!(pCell->state & GVIS_SELECTED) && (state & GVIS_SELECTED))
  686.     {
  687.         CCellID cell(nRow, nCol);
  688.         m_SelectedCellMap.SetAt(MAKELONG(nRow, nCol), cell);
  689.     }
  690.     // Set the cell's state
  691.     pCell->state = state;
  692.     return TRUE;
  693. }
  694. UINT CGridCtrl::GetItemState(int nRow, int nCol) const
  695. {
  696.     CGridCell* pCell = GetCell(nRow, nCol);
  697.     ASSERT(pCell);
  698.     if (!pCell)
  699.         return 0;
  700.     return pCell->state;
  701. }
  702. BOOL CGridCtrl::SetItemFormat(int nRow, int nCol, UINT nFormat)
  703. {
  704.     CGridCell* pCell = GetCell(nRow, nCol);
  705.     ASSERT(pCell);
  706.     if (!pCell) 
  707.         return FALSE;
  708.     pCell->nFormat = nFormat;
  709.     return TRUE;
  710. }
  711. UINT CGridCtrl::GetItemFormat(int nRow, int nCol) const
  712. {
  713.     CGridCell* pCell = GetCell(nRow, nCol);
  714.     ASSERT(pCell);
  715.     if (!pCell)
  716.         return 0;
  717.     return pCell->nFormat;
  718. }
  719. BOOL CGridCtrl::SetItemBkColour(int nRow, int nCol, COLORREF cr /* = CLR_DEFAULT */)
  720. {
  721.     CGridCell* pCell = GetCell(nRow, nCol);
  722.     ASSERT(pCell);
  723.     if (!pCell) 
  724.         return FALSE;
  725.     pCell->crBkClr = cr;
  726.     return TRUE;
  727. }
  728. COLORREF CGridCtrl::GetItemBkColour(int nRow, int nCol) const
  729. {
  730.     CGridCell* pCell = GetCell(nRow, nCol);
  731.     ASSERT(pCell);
  732.     if (!pCell)
  733.         return 0;
  734.     return pCell->crBkClr;
  735. }
  736. BOOL CGridCtrl::SetItemFgColour(int nRow, int nCol, COLORREF cr /* = CLR_DEFAULT */)
  737. {
  738.     CGridCell* pCell = GetCell(nRow, nCol);
  739.     ASSERT(pCell);
  740.     if (!pCell)
  741.         return FALSE;
  742.     pCell->crFgClr = cr;
  743.     return TRUE;
  744. }
  745. COLORREF CGridCtrl::GetItemFgColour(int nRow, int nCol) const
  746. {
  747.     CGridCell* pCell = GetCell(nRow, nCol);
  748.     ASSERT(pCell);
  749.     if (!pCell)
  750.         return 0;
  751.     return pCell->crFgClr;
  752. }
  753. BOOL CGridCtrl::SetItemFont(int nRow, int nCol, LOGFONT* lf)
  754. {
  755.     CGridCell* pCell = GetCell(nRow, nCol);
  756.     ASSERT(pCell);
  757.     if (!pCell)
  758.         return FALSE;
  759.     memcpy(&(pCell->lfFont), lf, sizeof(LOGFONT));
  760.     return TRUE;
  761. }
  762. LOGFONT* CGridCtrl::GetItemFont(int nRow, int nCol)
  763. {
  764.     CGridCell* pCell = GetCell(nRow, nCol);
  765.     ASSERT(pCell);
  766.     if (!pCell) 
  767.         return &m_Logfont;
  768.     return &(pCell->lfFont);
  769. }
  770. ////////////////////////////////////////////////////////////////////////////////////
  771. // Row/Column size functions
  772. long CGridCtrl::GetVirtualWidth() const
  773. {
  774.     long lVirtualWidth = 0;
  775.     int iColCount = GetColumnCount();
  776.     for (int i = 0; i < iColCount; i++)
  777.         lVirtualWidth += m_arColWidths[i];
  778.     return lVirtualWidth;
  779. }
  780. long CGridCtrl::GetVirtualHeight() const
  781. {
  782.     long lVirtualHeight = 0;
  783.     int iRowCount = GetRowCount();
  784.     for (int i = 0; i < iRowCount; i++)
  785.         lVirtualHeight += m_arRowHeights[i];
  786.     return lVirtualHeight;
  787. }
  788. int CGridCtrl::GetRowHeight(int nRow) const
  789. {
  790.     ASSERT(nRow >= 0 && nRow < m_nRows);
  791.     if (nRow < 0 || nRow >= m_nRows) return -1;
  792.     return m_arRowHeights[nRow];
  793. }
  794. int CGridCtrl::GetColumnWidth(int nCol) const
  795. {
  796.     ASSERT(nCol >= 0 && nCol < m_nCols);
  797.     if (nCol < 0 || nCol >= m_nCols) return -1;
  798.     return m_arColWidths[nCol];
  799. }
  800. BOOL CGridCtrl::SetRowHeight(int nRow, int height)
  801. {
  802.     ASSERT(nRow >= 0 && nRow < m_nRows && height >= 0);
  803.     if (nRow < 0 || nRow >= m_nRows || height < 0) return FALSE;
  804.     m_arRowHeights[nRow] = height;    
  805.     return TRUE;
  806. }
  807. BOOL CGridCtrl::SetColumnWidth(int nCol, int width)
  808. {
  809.     ASSERT(nCol >= 0 && nCol < m_nCols && width >= 0);
  810.     if (nCol < 0 || nCol >= m_nCols || width < 0) return FALSE;
  811.     m_arColWidths[nCol] = width;
  812.     return TRUE;
  813. }
  814. int CGridCtrl::GetFixedRowHeight() const
  815. {
  816.     int nHeight = 0;
  817.     for (int i = 0; i < m_nFixedRows; i++)
  818.         nHeight += GetRowHeight(i);
  819.     return nHeight;
  820. }
  821. int CGridCtrl::GetFixedColumnWidth() const
  822. {
  823.     int nWidth = 0;
  824.     for (int i = 0; i < m_nFixedCols; i++)
  825.         nWidth += GetColumnWidth(i);
  826.     return nWidth;
  827. }
  828. #define AUTOSIZE_COLUMN_MIN 20
  829. #define AUTOSIZE_COLUMN_MAX 200
  830. BOOL CGridCtrl::AutoSizeColumn(int nCol)
  831. {
  832.     ASSERT(nCol >= 0 && nCol < m_nCols);
  833.     if (nCol < 0 || nCol >= m_nCols) return FALSE;
  834.     CSize size;
  835.     CDC* pDC = GetDC();
  836.     if (!pDC) return FALSE;
  837.     int nWidth = AUTOSIZE_COLUMN_MIN;
  838.     int nNumRows = GetRowCount();
  839.     for (int nRow = 0; nRow < nNumRows; nRow++)
  840.     {
  841.         size = GetCellExtent(nRow, nCol, pDC);
  842.         if (size.cx > nWidth) nWidth = size.cx;
  843.     }
  844.     if( nWidth > AUTOSIZE_COLUMN_MAX )
  845.         nWidth = AUTOSIZE_COLUMN_MAX;
  846.     m_arColWidths[nCol] = nWidth;
  847.     ReleaseDC(pDC);
  848.     ResetScrollBars();
  849.     return TRUE;
  850. }
  851. BOOL CGridCtrl::AutoSizeRow(int nRow)
  852. {
  853.     ASSERT(nRow >= 0 && nRow < m_nRows);
  854.     if (nRow < 0 || nRow >= m_nRows) return FALSE;
  855.     CSize size;
  856.     CDC* pDC = GetDC();
  857.     if (!pDC) return FALSE;
  858.     int nHeight = 0;
  859.     int nNumColumns = GetColumnCount();
  860.     for (int nCol = 0; nCol < nNumColumns; nCol++)
  861.     {  
  862.         size = GetCellExtent(nRow, nCol, pDC);
  863.         if (size.cy > nHeight) nHeight = size.cy;
  864.     }  
  865.     m_arRowHeights[nRow] = nHeight;
  866.     ReleaseDC(pDC);
  867.     ResetScrollBars();
  868.     return TRUE;
  869. }
  870. void CGridCtrl::AutoSizeColumns()
  871. {
  872.     int nNumColumns = GetColumnCount();
  873.     for (int nCol = 0; nCol < nNumColumns; nCol++)
  874.         AutoSizeColumn(nCol);
  875. }
  876. void CGridCtrl::AutoSizeRows()
  877. {
  878.     int nNumRows = GetRowCount();
  879.     for (int nRow = 0; nRow < nNumRows; nRow++)
  880.         AutoSizeRow(nRow);
  881. }
  882. // sizes all rows and columns
  883. // faster than calling both AutoSizeColumns() and AutoSizeRows()
  884. void CGridCtrl::AutoSize()
  885. {
  886.     CDC* pDC = GetDC();
  887.     if (!pDC) return;
  888.     int nNumColumns = GetColumnCount();
  889.     int nNumRows = GetRowCount();
  890.     
  891.     // initialize column widths to zero
  892.     for (int nCol = 0; nCol < nNumColumns; nCol++)
  893.         m_arColWidths[nCol] = AUTOSIZE_COLUMN_MIN;
  894.     
  895.     // initialize row heights to zero
  896.     for (int nRow = 0; nRow < nNumRows; nRow++)
  897.         m_arRowHeights[nRow] = 0;
  898.     
  899.     CSize size;
  900.     for (nCol = 0; nCol < nNumColumns; nCol++)
  901.         for (nRow = 0; nRow < nNumRows; nRow++)
  902.         {
  903.             size = GetCellExtent(nRow, nCol, pDC);
  904.             if (size.cx > (int) m_arColWidths[nCol])  m_arColWidths[nCol] = size.cx;
  905.             if (size.cy > (int) m_arRowHeights[nRow]) m_arRowHeights[nRow] = size.cy;
  906.         }
  907.         
  908.     for (nCol = 0; nCol < nNumColumns; nCol++)
  909.     {
  910.         if( m_arColWidths[nCol] > AUTOSIZE_COLUMN_MAX )
  911.             m_arColWidths[nCol] = AUTOSIZE_COLUMN_MIN;
  912.     }
  913.     
  914.     ReleaseDC(pDC);
  915.     if (m_bAllowDraw) {
  916.         ResetScrollBars();
  917.         Invalidate();
  918.     }
  919. }
  920. void CGridCtrl::ExpandColumnsToFit()
  921. {
  922.     if (GetColumnCount() <= 0) return;
  923.     CRect rect;
  924.     GetClientRect(rect);
  925.     long virtualWidth = GetVirtualWidth();
  926.     int nDifference = rect.Width() - (int) virtualWidth;
  927.     int nColumnAdjustment = nDifference / GetColumnCount();
  928.     for (int i = 0; i < GetColumnCount(); i++)
  929.        m_arColWidths[i] += nColumnAdjustment;
  930.     if (nDifference > 0)
  931.     {
  932.         int leftOver = nDifference % GetColumnCount();
  933.         for (i = 0; i < leftOver; i++)
  934.             m_arColWidths[i] += 1;
  935.     } 
  936.     else 
  937.     {
  938.         int leftOver = (-nDifference) % GetColumnCount();
  939.         for (i = 0; i < leftOver; i++)
  940.             m_arColWidths[i] -= 1;
  941.     }
  942.     if (m_bAllowDraw) 
  943.         Invalidate();
  944. }
  945. void CGridCtrl::ExpandRowsToFit()
  946. {
  947.     if (GetRowCount() <= 0) return;
  948.     CRect rect;
  949.     GetClientRect(rect);
  950.     long virtualHeight = GetVirtualHeight();
  951.     int nDifference = rect.Height() - (int) virtualHeight;
  952.     int nRowAdjustment = nDifference / GetRowCount();
  953.     for (int i = 0; i < GetRowCount(); i++)
  954.        m_arRowHeights[i] += nRowAdjustment;
  955.     if (nDifference > 0)
  956.     {
  957.         int leftOver = nDifference % GetRowCount();
  958.         for (i = 0; i < leftOver; i++)
  959.             m_arRowHeights[i] += 1;
  960.     } 
  961.     else 
  962.     {
  963.         int leftOver = (-nDifference) % GetRowCount();
  964.         for (i = 0; i < leftOver; i++)
  965.             m_arRowHeights[i] -= 1;
  966.     }
  967.     if (m_bAllowDraw) 
  968.         Invalidate();
  969. }
  970. void CGridCtrl::ExpandToFit()
  971. {
  972.     ExpandColumnsToFit();   // This will remove any existing horz scrollbar
  973.     ExpandRowsToFit();      // This will remove any existing vert scrollbar
  974.     ExpandColumnsToFit();   // Just in case the first adjustment was with a vert
  975.                             // scrollbar in place
  976. }
  977. BOOL CGridCtrl::IsCellFixed(int nRow, int nCol)
  978. {
  979.     return (nRow < GetFixedRowCount() || nCol < GetFixedColumnCount());
  980. }
  981. void CGridCtrl::SetModified(BOOL bModified /*=TRUE*/, int nRow /*=-1*/, int nCol /*=-1*/)
  982. {
  983.     if (nRow > 0 && nCol > 0)
  984.     {
  985.         if (bModified)
  986.         {
  987.         SetItemState(nRow, nCol, GetItemState(nRow, nCol) | GVIS_MODIFIED);
  988.             m_bModified = TRUE; 
  989.         }
  990.         else
  991.         SetItemState(nRow, nCol, GetItemState(nRow, nCol) & ~GVIS_MODIFIED);
  992.     }
  993.     else
  994.         m_bModified = bModified;
  995.     if (!m_bModified)
  996.     {
  997.         for (int row = 0; row < GetRowCount(); row++)
  998.             for (int col = 0; col < GetColumnCount(); col++) 
  999.                 SetItemState(row, col, GetItemState(row, col) & ~GVIS_MODIFIED);
  1000.     }
  1001. }
  1002. BOOL CGridCtrl::GetModified(int nRow /*=-1*/, int nCol /*=-1*/)
  1003. {
  1004.     if (nRow > 0 && nCol > 0)
  1005.         return ( (GetItemState(nRow, nCol) & GVIS_MODIFIED) == GVIS_MODIFIED );
  1006.     else
  1007.         return m_bModified;               
  1008. }
  1009. /////////////////////////////////////////////////////////////////////////////////////
  1010. // GridCtrl cell visibility tests and invalidation/redraw functions
  1011. // EnsureVisible supplied by Roelf Werkman
  1012. void CGridCtrl::EnsureVisible(int nRow, int nCol)
  1013. {
  1014.     CCellRange VisibleCells = GetVisibleNonFixedCellRange();
  1015.     int right = nCol - VisibleCells.GetMaxCol();
  1016.     int left  = VisibleCells.GetMinCol() - nCol;
  1017.     int down  = nRow - VisibleCells.GetMaxRow();
  1018.     int up    = VisibleCells.GetMinRow() - nRow;
  1019.     while (right > 0)
  1020.     {
  1021.         SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
  1022.         right--;
  1023.     }
  1024.     while (left > 0)
  1025.     {
  1026.         SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
  1027.         left--;
  1028.     }
  1029.     while (down > 0)
  1030.     {
  1031.         SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  1032.         down--;
  1033.    }
  1034.    while (up > 0)
  1035.    {
  1036.        SendMessage(WM_VSCROLL, SB_LINEUP, 0);
  1037.        up--;
  1038.    }
  1039.    // Move one more if we only see a snall bit of the cell
  1040.    CRect rectCell, rectWindow;
  1041.    GetCellRect(nRow, nCol, rectCell);
  1042.    GetClientRect(rectWindow);
  1043.    if (rectCell.right > rectWindow.right)
  1044.         SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
  1045.    if (rectCell.bottom > rectWindow.bottom)
  1046.         SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  1047. }
  1048. BOOL CGridCtrl::IsCellEditable(CCellID &cell) const
  1049. {
  1050.     return IsCellEditable(cell.row, cell.col); 
  1051. }
  1052. BOOL CGridCtrl::IsCellEditable(int nRow, int nCol) const
  1053. {
  1054.     return IsEditable() && ((GetItemState(nRow, nCol) & GVIS_READONLY) != GVIS_READONLY);
  1055. }
  1056. BOOL CGridCtrl::IsCellVisible(CCellID cell) const
  1057. {  
  1058.     return IsCellVisible(cell.row, cell.col);
  1059. }
  1060. BOOL CGridCtrl::IsCellVisible(int nRow, int nCol) const
  1061. {  
  1062.     if (!IsWindow(m_hWnd))
  1063.         return FALSE;
  1064.     int x,y;
  1065.     CCellID TopLeft;
  1066.     if (nCol >= GetFixedColumnCount() || nRow >= GetFixedRowCount())
  1067.     {
  1068.         TopLeft = GetTopleftNonFixedCell();
  1069.         if (nCol >= GetFixedColumnCount() && nCol < TopLeft.col) return FALSE;
  1070.         if (nRow >= GetFixedRowCount() && nRow < TopLeft.row) return FALSE;
  1071.     }
  1072.     CRect rect;
  1073.     GetClientRect(rect);
  1074.     if (nCol < GetFixedColumnCount())
  1075.     {
  1076.         x = 0;
  1077.         for (int i = 0; i <= nCol; i++) 
  1078.         {
  1079.             if (x >= rect.right) return FALSE;
  1080.             x += GetColumnWidth(i);    
  1081.         }
  1082.     } 
  1083.     else 
  1084.     {
  1085.         x = GetFixedColumnWidth();
  1086.         for (int i = TopLeft.col; i <= nCol; i++) 
  1087.         {
  1088.             if (x >= rect.right) return FALSE;
  1089.             x += GetColumnWidth(i);    
  1090.         }
  1091.     }
  1092.     if (nRow < GetFixedRowCount())
  1093.     {
  1094.         y = 0;
  1095.         for (int i = 0; i <= nRow; i++) 
  1096.         {
  1097.             if (y >= rect.bottom) return FALSE;
  1098.             y += GetRowHeight(i);    
  1099.         }
  1100.     } 
  1101.     else 
  1102.     {
  1103.         if (nRow < TopLeft.row) return FALSE;
  1104.         y = GetFixedRowHeight();
  1105.         for (int i = TopLeft.row; i <= nRow; i++) 
  1106.         {
  1107.             if (y >= rect.bottom) return FALSE;
  1108.             y += GetRowHeight(i);    
  1109.         }
  1110.     }
  1111.     return TRUE;
  1112. }
  1113. BOOL CGridCtrl::InvalidateCellRect(const CCellID& cell)
  1114. {
  1115.     if (!::IsWindow(GetSafeHwnd()) || !m_bAllowDraw)
  1116.         return FALSE;
  1117.     ASSERT(IsValid(cell));
  1118.     if (!IsCellVisible(cell.row, cell.col)) return FALSE;
  1119.     CRect rect;
  1120.     if (!GetCellRect(cell, rect)) return FALSE;
  1121.     rect.right++; rect.bottom++;
  1122.     InvalidateRect(rect, TRUE);
  1123.     return TRUE;
  1124. }
  1125. BOOL CGridCtrl::InvalidateCellRect(const CCellRange& cellRange)
  1126. {
  1127.     ASSERT(IsValid(cellRange));
  1128.     if (!::IsWindow(GetSafeHwnd()) || !m_bAllowDraw) return FALSE;
  1129.     CCellRange visibleCellRange = GetVisibleNonFixedCellRange().Intersect(cellRange);
  1130.     CRect rect;
  1131.     if (!GetCellRangeRect(visibleCellRange, rect)) return FALSE;
  1132.     rect.right++; rect.bottom++;
  1133.     InvalidateRect(rect, TRUE);
  1134.     return TRUE;
  1135. }
  1136. /////////////////////////////////////////////////////////////////////////////
  1137. // CGridCtrl Mouse stuff
  1138. // Handles mouse wheel notifications
  1139. // Note - if this doesn't work for win95 then use OnRegisteredMouseWheel instead
  1140. #if !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
  1141. BOOL CGridCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
  1142. {
  1143.     // A m_nRowsPerWheelNotch value less than 0 indicates that the mouse
  1144.     // wheel scrolls whole pages, not just lines.
  1145.     if (m_nRowsPerWheelNotch == -1)
  1146.     {
  1147.         int nPagesScrolled = zDelta / 120;
  1148.         if (nPagesScrolled > 0)
  1149.             for (int i = 0; i < nPagesScrolled; i++)
  1150.                 PostMessage(WM_VSCROLL, SB_PAGEUP, 0);
  1151.         else
  1152.             for (int i = 0; i > nPagesScrolled; i--)
  1153.                 PostMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
  1154.     }
  1155.     else
  1156.     {
  1157.         int nRowsScrolled = m_nRowsPerWheelNotch * zDelta / 120;
  1158.         if (nRowsScrolled > 0)
  1159.             for (int i = 0; i < nRowsScrolled; i++)
  1160.                 PostMessage(WM_VSCROLL, SB_LINEUP, 0);
  1161.         else
  1162.             for (int i = 0; i > nRowsScrolled; i--)
  1163.                 PostMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  1164.     }
  1165.     return CWnd::OnMouseWheel(nFlags, zDelta, pt);
  1166. }
  1167. #endif // !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
  1168. void CGridCtrl::OnMouseMove(UINT nFlags, CPoint point)
  1169. {
  1170.     CRect rect;
  1171.     GetClientRect(rect);
  1172. #ifndef GRIDCONTROL_NO_DRAGDROP
  1173.     // If outside client area, return (unless we are drag n dropping)
  1174.     if (m_MouseMode != MOUSE_DRAGGING && !rect.PtInRect(point))
  1175.         return;
  1176. #endif
  1177.     // If the left mouse button is up, then test to see if row/column sizing is imminent
  1178.     if (!(nFlags & MK_LBUTTON) 
  1179.         || (m_MouseMode == MOUSE_NOTHING && (nFlags & MK_LBUTTON)))
  1180.     {
  1181.         if (m_bAllowColumnResize && MouseOverColumnResizeArea(point))
  1182.         {
  1183.             if (m_MouseMode != MOUSE_OVER_COL_DIVIDE)
  1184.             {
  1185.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
  1186.                 m_MouseMode = MOUSE_OVER_COL_DIVIDE;
  1187.             }
  1188.         }
  1189.         else if (m_bAllowRowResize && MouseOverRowResizeArea(point))
  1190.         {
  1191.             if (m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
  1192.             {
  1193.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
  1194.                 m_MouseMode = MOUSE_OVER_ROW_DIVIDE;
  1195.             }
  1196.         }
  1197.         else if (m_MouseMode != MOUSE_NOTHING)
  1198.         {
  1199.             SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1200.             m_MouseMode = MOUSE_NOTHING;
  1201.         }
  1202. #ifndef GRIDCONTROL_NO_TITLETIPS
  1203.         if (m_MouseMode == MOUSE_NOTHING && m_bTitleTips)
  1204.         {
  1205.             CCellID idCurrentCell = GetCellFromPt(point);
  1206.             CRect TextRect, CellRect;
  1207.             if (GetTextRect(idCurrentCell.row, idCurrentCell.col, TextRect) &&
  1208.                 GetCellRect(idCurrentCell.row, idCurrentCell.col, CellRect))
  1209.                 m_TitleTip.Show(TextRect, 
  1210.                                 GetItemText(idCurrentCell.row, idCurrentCell.col), 
  1211.                                 0,
  1212.                                 CellRect,
  1213.                                 GetItemFont(idCurrentCell.row, idCurrentCell.col));
  1214.         }
  1215. #endif
  1216.         m_LastMousePoint = point;
  1217.         return;
  1218.     }
  1219.     if (!IsValid(m_LeftClickDownCell))
  1220.     {
  1221.         m_LastMousePoint = point;
  1222.         return;
  1223.     }
  1224.     // If the left mouse button is down, then process appropriately
  1225.     if (nFlags & MK_LBUTTON) 
  1226.     {
  1227.         switch(m_MouseMode)
  1228.         {
  1229.             case MOUSE_SELECT_ALL:        
  1230.                 break;
  1231.             case MOUSE_SELECT_COL:
  1232.             case MOUSE_SELECT_ROW:    
  1233.             case MOUSE_SELECT_CELLS:    
  1234.                 {
  1235.                     CCellID idCurrentCell = GetCellFromPt(point);
  1236.                     if (!IsValid(idCurrentCell)) 
  1237.                         return;
  1238.                     if (idCurrentCell != GetFocusCell())
  1239.                     {
  1240.                         OnSelecting(idCurrentCell);
  1241.                         //SetFocusCell(max(idCurrentCell.row, m_nFixedRows),
  1242.                         //             max(idCurrentCell.col, m_nFixedCols));
  1243.                         if (idCurrentCell.row >= m_nFixedRows &&
  1244.                             idCurrentCell.col >= m_nFixedCols)
  1245.                         {
  1246.                             SetFocusCell(idCurrentCell);
  1247.                         }
  1248.                     }
  1249.                     break;
  1250.                 }
  1251.             case MOUSE_SIZING_COL:   
  1252.                 {
  1253.                     CDC* pDC = GetDC();
  1254.                     if (!pDC) break;
  1255.                     
  1256.                     CRect oldInvertedRect(m_LastMousePoint.x, rect.top, 
  1257.                         m_LastMousePoint.x + 2, rect.bottom);
  1258.                     pDC->InvertRect(&oldInvertedRect);
  1259.                     CRect newInvertedRect(point.x, rect.top, 
  1260.                         point.x + 2, rect.bottom);
  1261.                     pDC->InvertRect(&newInvertedRect);
  1262.                     ReleaseDC(pDC);
  1263.                 }
  1264.                 break;
  1265.                 
  1266.             case MOUSE_SIZING_ROW:        
  1267.                 {
  1268.                     CDC* pDC = GetDC();
  1269.                     if (!pDC) break;
  1270.                     
  1271.                     CRect oldInvertedRect(rect.left, m_LastMousePoint.y, 
  1272.                         rect.right, m_LastMousePoint.y + 2);
  1273.                     pDC->InvertRect(&oldInvertedRect);
  1274.                     CRect newInvertedRect(rect.left, point.y, 
  1275.                         rect.right, point.y + 2);
  1276.                     pDC->InvertRect(&newInvertedRect);
  1277.                     ReleaseDC(pDC);
  1278.                 }
  1279.                 break;
  1280. #ifndef GRIDCONTROL_NO_DRAGDROP
  1281.             case MOUSE_PREPARE_DRAG:   
  1282.                 OnBeginDrag();    
  1283.                 break;
  1284. #endif
  1285.         }    
  1286.     }
  1287.     m_LastMousePoint = point;
  1288. }
  1289. void CGridCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) 
  1290. {
  1291.     if (m_MouseMode == MOUSE_OVER_COL_DIVIDE) 
  1292.     {
  1293.         CCellID cell = GetCellFromPt(point);
  1294.         ASSERT(IsValid(cell));
  1295.         CPoint start;
  1296.         if (!GetCellOrigin(0, cell.col, &start)) return;
  1297.         if (point.x - start.x <= m_nResizeCaptureRange)     // Clicked right of border
  1298.             cell.col--;
  1299.         AutoSizeColumn(cell.col);
  1300.         Invalidate();
  1301.     } 
  1302.     else if (m_MouseMode == MOUSE_OVER_ROW_DIVIDE)
  1303.     {
  1304.         CCellID cell = GetCellFromPt(point);
  1305.         ASSERT(IsValid(cell));
  1306.         CPoint start;
  1307.         if (!GetCellOrigin(0, cell.col, &start)) return;
  1308.         if (point.y - start.y <= m_nResizeCaptureRange)     // Clicked below border
  1309.             cell.row--;
  1310.         AutoSizeRow(cell.row);
  1311.         Invalidate();
  1312.     }
  1313.     else if (m_MouseMode == MOUSE_NOTHING)
  1314.     {
  1315.         if (m_LeftClickDownCell.row >= m_nFixedRows && 
  1316.             IsValid(m_LeftClickDownCell) &&
  1317.             m_LeftClickDownCell.col >= m_nFixedCols)
  1318.         {
  1319.             OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, VK_LBUTTON);
  1320.         }
  1321.         else if (m_bListMode)
  1322.         {
  1323.             CCellID cell = GetCellFromPt(point);
  1324.             if (!IsValid(cell)) return;
  1325.             if (cell.row >= m_nFixedRows && cell.col < GetFixedColumnCount())
  1326.                 OnEditCell(cell.row, cell.col, VK_LBUTTON);
  1327.         }
  1328.     }
  1329.     CWnd::OnLButtonDblClk(nFlags, point);
  1330. }
  1331. void CGridCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  1332. {
  1333.     HWND hOldFocusWnd = ::GetFocus();
  1334.     m_LeftClickDownPoint = point;
  1335.     m_LeftClickDownCell = GetCellFromPt(point);
  1336.     if (!IsValid(m_LeftClickDownCell)) return;
  1337.     m_SelectionStartCell = (nFlags & MK_SHIFT)? m_idCurrentCell : m_LeftClickDownCell;
  1338.     SetFocus();        // Auto-destroy any InPlaceEdit's
  1339.     // If the user clicks on the current cell, then prepare to edit it.
  1340.     // (If the user moves the mouse, then dragging occurs)
  1341.     if (m_LeftClickDownCell == m_idCurrentCell)
  1342.     {
  1343.         m_MouseMode = MOUSE_PREPARE_EDIT;
  1344.         return;
  1345.     }   
  1346.     else if (m_MouseMode != MOUSE_OVER_COL_DIVIDE &&
  1347.              m_MouseMode != MOUSE_OVER_ROW_DIVIDE)        
  1348.     {
  1349.         SetFocusCell(-1,-1);
  1350.         SetFocusCell(max(m_LeftClickDownCell.row, m_nFixedRows),
  1351.                      max(m_LeftClickDownCell.col, m_nFixedCols));
  1352.     }
  1353. #ifndef GRIDCONTROL_NO_DRAGDROP
  1354.     // If the user clicks on a selected cell, then prepare to drag it.
  1355.     // (If the user moves the mouse, then dragging occurs)
  1356.     if (m_bAllowDragAndDrop && hOldFocusWnd == GetSafeHwnd() && 
  1357.         GetItemState(m_LeftClickDownCell.row, m_LeftClickDownCell.col) & GVNI_SELECTED)
  1358.     {
  1359.         m_MouseMode = MOUSE_PREPARE_DRAG;
  1360.         return;
  1361.     }
  1362. #endif
  1363.     SetCapture();
  1364.     if (m_MouseMode == MOUSE_NOTHING)
  1365.     {
  1366.         if (m_bAllowColumnResize && MouseOverColumnResizeArea(point))
  1367.         {
  1368.             if (m_MouseMode != MOUSE_OVER_COL_DIVIDE)
  1369.             {
  1370.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
  1371.                 m_MouseMode = MOUSE_OVER_COL_DIVIDE;
  1372.             }
  1373.         }
  1374.         else if (m_bAllowRowResize && MouseOverRowResizeArea(point))
  1375.         {
  1376.             if (m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
  1377.             {
  1378.                 SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
  1379.                 m_MouseMode = MOUSE_OVER_ROW_DIVIDE;
  1380.             }
  1381.         }
  1382.         //else if (m_MouseMode != MOUSE_NOTHING)
  1383.         //{
  1384.         //    SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1385.         //    m_MouseMode = MOUSE_NOTHING;
  1386.         //}
  1387.     }
  1388.     if (m_MouseMode == MOUSE_OVER_COL_DIVIDE) // sizing column
  1389.     {
  1390.         m_MouseMode = MOUSE_SIZING_COL;
  1391.         CPoint start;
  1392.         if (!GetCellOrigin(0, m_LeftClickDownCell.col, &start)) return;
  1393.         CRect rect;
  1394.         GetClientRect(rect);
  1395.         CRect invertedRect(point.x, rect.top, point.x + 2, rect.bottom);
  1396.         CDC* pDC = GetDC();
  1397.         if (pDC) {
  1398.             pDC->InvertRect(&invertedRect);
  1399.             ReleaseDC(pDC);
  1400.         }
  1401.         if (point.x - start.x <= m_nResizeCaptureRange)        // clicked right of border
  1402.             if (!GetCellOrigin(0, --m_LeftClickDownCell.col, &start)) return;
  1403.         rect.left = start.x;
  1404.         ClientToScreen(rect);
  1405. #ifndef _WIN32_WCE_NO_CURSOR
  1406.         ClipCursor(rect);
  1407. #endif
  1408.     }
  1409.     else if (m_MouseMode == MOUSE_OVER_ROW_DIVIDE) // sizing row
  1410.     {
  1411.         m_MouseMode = MOUSE_SIZING_ROW;
  1412.         CPoint start;
  1413.         if (!GetCellOrigin(m_LeftClickDownCell, &start)) return;
  1414.         CRect rect;
  1415.         GetClientRect(rect);
  1416.         CRect invertedRect(rect.left, point.y, rect.right, point.y + 2);
  1417.         CDC* pDC = GetDC();
  1418.         if (pDC) {
  1419.             pDC->InvertRect(&invertedRect);
  1420.             ReleaseDC(pDC);
  1421.         }
  1422.         if (point.y - start.y <= m_nResizeCaptureRange)            // clicked below border
  1423.             if (!GetCellOrigin(--m_LeftClickDownCell.row, 0, &start)) return;
  1424.         rect.top = start.y;
  1425.         ClientToScreen(rect);
  1426. #ifndef _WIN32_WCE_NO_CURSOR
  1427.         ClipCursor(rect);
  1428. #endif
  1429.     }
  1430.     else // not sizing or editing -- selecting
  1431.     {    
  1432.         // If Ctrl pressed, save the current cell selection. This will get added
  1433.         // to the new cell selection at the end of the cell selection process
  1434.         m_PrevSelectedCellMap.RemoveAll();
  1435.         if (nFlags & MK_CONTROL) {
  1436.             for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  1437.             {
  1438.                 DWORD key;
  1439.                 CCellID cell;
  1440.                 m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1441.                 m_PrevSelectedCellMap.SetAt(key, cell);
  1442.             }
  1443.         }
  1444.         
  1445.         if (m_LeftClickDownCell.row < GetFixedRowCount())
  1446.             OnFixedRowClick(m_LeftClickDownCell);
  1447.         else if (m_LeftClickDownCell.col < GetFixedColumnCount())
  1448.             OnFixedColumnClick(m_LeftClickDownCell);
  1449.         else
  1450.         {
  1451.             m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
  1452.             OnSelecting(m_LeftClickDownCell);
  1453.         }
  1454.         m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);
  1455.     }   
  1456.     m_LastMousePoint = point;
  1457. }
  1458. void CGridCtrl::OnLButtonUp(UINT nFlags, CPoint point)
  1459. {
  1460.     CWnd::OnLButtonUp(nFlags, point);
  1461. #ifndef _WIN32_WCE_NO_CURSOR
  1462.     ClipCursor(NULL);
  1463. #endif
  1464.     if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
  1465.     {
  1466.         ReleaseCapture();
  1467.         KillTimer(m_nTimerID);
  1468.         m_nTimerID = 0;
  1469.     }
  1470.     // m_MouseMode == MOUSE_PREPARE_EDIT only if user clicked down on current cell
  1471.     // and then didn't move mouse before clicking up (releasing button)
  1472.     if (m_MouseMode == MOUSE_PREPARE_EDIT)    
  1473.     {
  1474.         OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, VK_LBUTTON);
  1475.     }
  1476. #ifndef GRIDCONTROL_NO_DRAGDROP
  1477.     // m_MouseMode == MOUSE_PREPARE_DRAG only if user clicked down on a selected cell
  1478.     // and then didn't move mouse before clicking up (releasing button)
  1479.     else if (m_MouseMode == MOUSE_PREPARE_DRAG) 
  1480.     {
  1481.         ResetSelectedRange();
  1482.     }
  1483. #endif
  1484.     else if (m_MouseMode == MOUSE_SIZING_COL)
  1485.     {
  1486.         CRect rect;
  1487.         GetClientRect(rect);
  1488.         CRect invertedRect(m_LastMousePoint.x, rect.top, m_LastMousePoint.x + 2, rect.bottom);
  1489.         CDC* pDC = GetDC();
  1490.         if (pDC) {
  1491.             pDC->InvertRect(&invertedRect);
  1492.             ReleaseDC(pDC);
  1493.         }
  1494.         if (m_LeftClickDownPoint != point) 
  1495.         {   
  1496.             CPoint start;
  1497.             if (!GetCellOrigin(m_LeftClickDownCell, &start)) return;
  1498.             SetColumnWidth(m_LeftClickDownCell.col, point.x - start.x);
  1499.             ResetScrollBars();
  1500.             Invalidate();
  1501.         }
  1502.     }
  1503.     else if (m_MouseMode == MOUSE_SIZING_ROW)
  1504.     {
  1505.         CRect rect;
  1506.         GetClientRect(rect);
  1507.         CRect invertedRect(rect.left, m_LastMousePoint.y, rect.right, m_LastMousePoint.y + 2);
  1508.     
  1509.         CDC* pDC = GetDC();
  1510.         if (pDC) {
  1511.             pDC->InvertRect(&invertedRect);
  1512.             ReleaseDC(pDC);
  1513.         }
  1514.     
  1515.         if (m_LeftClickDownPoint != point) 
  1516.         {
  1517.             CPoint start;
  1518.             if (!GetCellOrigin(m_LeftClickDownCell, &start)) return;
  1519.             SetRowHeight(m_LeftClickDownCell.row, point.y - start.y);
  1520.             ResetScrollBars();
  1521.             Invalidate();
  1522.         }
  1523.     } 
  1524.     m_MouseMode = MOUSE_NOTHING;
  1525. #ifndef _WIN32_WCE_NO_CURSOR
  1526.     SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1527. #endif
  1528.     if (!IsValid(m_LeftClickDownCell)) return;
  1529.     CWnd *pOwner = GetOwner();
  1530.     if (pOwner && IsWindow(pOwner->m_hWnd))
  1531.         pOwner->PostMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED), 
  1532.                             (LPARAM) GetSafeHwnd());
  1533. }
  1534. #if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  1535. /////////////////////////////////////////////////////////////////////////////
  1536. // CGridCtrl printing 
  1537. void CGridCtrl::Print() 
  1538. {
  1539.     CDC dc;
  1540.     CPrintDialog printDlg(FALSE);
  1541.     if (printDlg.DoModal() != IDOK)             // Get printer settings from user
  1542.         return;
  1543.     dc.Attach(printDlg.GetPrinterDC());         // attach a printer DC
  1544.     dc.m_bPrinting = TRUE;
  1545.     CString strTitle;
  1546.     strTitle.LoadString(AFX_IDS_APP_TITLE);
  1547.     DOCINFO di;                                 // Initialise print doc details
  1548.     memset(&di, 0, sizeof (DOCINFO));
  1549.     di.cbSize = sizeof (DOCINFO);
  1550.     di.lpszDocName = strTitle;
  1551.     BOOL bPrintingOK = dc.StartDoc(&di);        // Begin a new print job
  1552.      CPrintInfo Info;
  1553.     Info.m_rectDraw.SetRect(0,0, dc.GetDeviceCaps(HORZRES), dc.GetDeviceCaps(VERTRES));
  1554.     OnBeginPrinting(&dc, &Info);                // Initialise printing
  1555.     for (UINT page = Info.GetMinPage(); page <= Info.GetMaxPage() && bPrintingOK; page++)
  1556.     {
  1557.         dc.StartPage();                         // begin new page
  1558.         Info.m_nCurPage = page;
  1559.         OnPrint(&dc, &Info);                    // Print page
  1560.         bPrintingOK = (dc.EndPage() > 0);       // end page
  1561.     }
  1562.     OnEndPrinting(&dc, &Info);                  // Clean up after printing
  1563.     if (bPrintingOK)
  1564.         dc.EndDoc();                            // end a print job
  1565.     else
  1566.         dc.AbortDoc();                          // abort job.
  1567.     dc.Detach();                                // detach the printer DC
  1568. }
  1569. /////////////////////////////////////////////////////////////////////////////
  1570. // CGridCtrl printing overridables - for Doc/View print/print preview framework
  1571. void CGridCtrl::OnBeginPrinting(CDC *pDC, CPrintInfo *pInfo)
  1572. {
  1573.     // OnBeginPrinting() is called after the user has committed to
  1574.     // printing by OK'ing the Print dialog, and after the framework
  1575.     // has created a CDC object for the printer or the preview view.
  1576.     // This is the right opportunity to set up the page range.
  1577.     // Given the CDC object, we can determine how many rows will
  1578.     // fit on a page, so we can in turn determine how many printed
  1579.     // pages represent the entire document.
  1580.     ASSERT(pDC && pInfo);
  1581.     if (!pDC || !pInfo) return;
  1582.     int nMaxRowCount = GetRowCount() - GetFixedRowCount();
  1583.     if (!nMaxRowCount) return;
  1584.     // Get a DC for the current window (will be a screen DC for print previewing)
  1585.     CDC *pCurrentDC = GetDC();        // will have dimensions of the client area
  1586.     if (!pCurrentDC) return;
  1587.     CSize PaperPixelsPerInch(pDC->GetDeviceCaps(LOGPIXELSX), pDC->GetDeviceCaps(LOGPIXELSY));
  1588.     CSize ScreenPixelsPerInch(pCurrentDC->GetDeviceCaps(LOGPIXELSX), pCurrentDC->GetDeviceCaps(LOGPIXELSY));
  1589.     // Create the printer font
  1590.     int nFontSize = -9;
  1591.     CString strFontName = "Times New Roman";
  1592.     m_PrinterFont.CreateFont(nFontSize, 0,0,0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,
  1593.                              OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
  1594.                              DEFAULT_PITCH | FF_DONTCARE, strFontName);
  1595.             
  1596.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  1597.     // Get the average character width (in GridCtrl units) and hence the margins
  1598.     m_CharSize = pDC->GetTextExtent(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSATUVWXYZ"),52);
  1599.     m_CharSize.cx /= 52;
  1600.     int nMargins = (LEFT_MARGIN+RIGHT_MARGIN)*m_CharSize.cx;
  1601.     // Get the page sizes (physical and logical)
  1602.     m_PaperSize = CSize(pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
  1603.     m_LogicalPageSize.cx = GetVirtualWidth()+nMargins;
  1604. #ifdef _WIN32_WCE
  1605.     m_LogicalPageSize.cy = (m_LogicalPageSize.cx * m_PaperSize.cy) / m_PaperSize.cx;
  1606. #else
  1607.     m_LogicalPageSize.cy = MulDiv(m_LogicalPageSize.cx, m_PaperSize.cy, m_PaperSize.cx);
  1608. #endif
  1609.     m_nPageHeight = m_LogicalPageSize.cy - GetFixedRowHeight()
  1610.                        - (HEADER_HEIGHT+FOOTER_HEIGHT + 2*GAP)*m_CharSize.cy;
  1611.     // Get the number of pages. Assumes no row is bigger than the page size.
  1612.     int nTotalRowHeight = 0;
  1613.     int nNumPages = 1;
  1614.     for (int row = GetFixedRowCount(); row < GetRowCount(); row++)
  1615.     {
  1616.         nTotalRowHeight += GetRowHeight(row);
  1617.         if (nTotalRowHeight > m_nPageHeight) {
  1618.             nNumPages++;
  1619.             nTotalRowHeight = GetRowHeight(row);
  1620.         }
  1621.     }
  1622.     // Set up the print info
  1623.     pInfo->SetMaxPage(nNumPages);
  1624.     pInfo->m_nCurPage = 1;                        // start printing at page# 1
  1625.     ReleaseDC(pCurrentDC);
  1626.     pDC->SelectObject(pOldFont);
  1627. }
  1628. void CGridCtrl::OnPrint(CDC *pDC, CPrintInfo *pInfo)
  1629. {
  1630.     if (!pDC || !pInfo) return;
  1631.     //CRect rcPage(pInfo->m_rectDraw);
  1632.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  1633.     // Set the page map mode to use GridCtrl units, and setup margin
  1634.     pDC->SetMapMode(MM_ANISOTROPIC);
  1635.     pDC->SetWindowExt(m_LogicalPageSize);
  1636.     pDC->SetViewportExt(m_PaperSize);
  1637.     pDC->SetWindowOrg(-LEFT_MARGIN*m_CharSize.cx, 0);
  1638.     // Header
  1639.     pInfo->m_rectDraw.top    = 0;
  1640.     pInfo->m_rectDraw.left   = 0;
  1641.     pInfo->m_rectDraw.right  = m_LogicalPageSize.cx - (LEFT_MARGIN+RIGHT_MARGIN)*m_CharSize.cx;
  1642.     pInfo->m_rectDraw.bottom = HEADER_HEIGHT*m_CharSize.cy;
  1643.     PrintHeader(pDC, pInfo);
  1644.     pDC->OffsetWindowOrg(0, -HEADER_HEIGHT*m_CharSize.cy);
  1645.     // Gap between header and column headings
  1646.     pDC->OffsetWindowOrg(0, -GAP*m_CharSize.cy);
  1647.     // Print the column headings
  1648.     pInfo->m_rectDraw.bottom = GetFixedRowHeight(); 
  1649.     PrintColumnHeadings(pDC, pInfo);
  1650.     pDC->OffsetWindowOrg(0, -GetFixedRowHeight()); 
  1651.     // We need to find out which row to start printing for this page.
  1652.     int nTotalRowHeight = 0;
  1653.     UINT nNumPages = 1;
  1654.     int nCurrPrintRow = GetFixedRowCount();
  1655.     while (nCurrPrintRow < GetRowCount() && nNumPages < pInfo->m_nCurPage)
  1656.     {
  1657.         nTotalRowHeight += GetRowHeight(nCurrPrintRow);
  1658.         if (nTotalRowHeight > m_nPageHeight) {
  1659.             nNumPages++;
  1660.             if (nNumPages == pInfo->m_nCurPage) break;
  1661.             nTotalRowHeight = GetRowHeight(nCurrPrintRow);
  1662.         }
  1663.         nCurrPrintRow++;
  1664.     }
  1665.     if (nCurrPrintRow >= GetRowCount()) return;
  1666.     // Draw as many rows as will fit on the printed page.
  1667.     // Clip the printed page so that there is no partially shown
  1668.     // row at the bottom of the page (the same row which will be fully
  1669.     // shown at the top of the next page).
  1670.     BOOL bFirstPrintedRow = TRUE;
  1671.     CRect rect;
  1672.     rect.bottom = -1;
  1673.     while (nCurrPrintRow < GetRowCount())
  1674.     {
  1675.         rect.top = rect.bottom+1;
  1676.         rect.bottom = rect.top + GetRowHeight(nCurrPrintRow) - 1;
  1677.         if (rect.bottom > m_nPageHeight) break;            // Gone past end of page
  1678.         rect.right = -1;
  1679.         for (int col = 0; col < GetColumnCount(); col++)
  1680.         {
  1681.             rect.left = rect.right+1;
  1682.             rect.right = rect.left + GetColumnWidth(col) - 1;
  1683.             DrawCell(pDC, nCurrPrintRow, col, rect);
  1684.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ) 
  1685.             {
  1686.                 int Overlap = (col == 0)? 0:1;
  1687.                 pDC->MoveTo(rect.left-Overlap, rect.bottom);
  1688.                 pDC->LineTo(rect.right, rect.bottom);
  1689.                 if (nCurrPrintRow == 0) {
  1690.                     pDC->MoveTo(rect.left-Overlap, rect.top);
  1691.                     pDC->LineTo(rect.right, rect.top);
  1692.                 }
  1693.             }
  1694.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT) 
  1695.             {
  1696.                 int Overlap = (bFirstPrintedRow)? 0:1;
  1697.                 pDC->MoveTo(rect.right, rect.top-Overlap);
  1698.                 pDC->LineTo(rect.right, rect.bottom);    
  1699.                 if (col == 0) {
  1700.                     pDC->MoveTo(rect.left, rect.top-Overlap);
  1701.                     pDC->LineTo(rect.left, rect.bottom);    
  1702.                 }
  1703.             }
  1704.         }
  1705.         nCurrPrintRow++;
  1706.         bFirstPrintedRow = FALSE;
  1707.     }
  1708.     // Footer
  1709.     pInfo->m_rectDraw.bottom = FOOTER_HEIGHT*m_CharSize.cy;
  1710.     pDC->SetWindowOrg(-LEFT_MARGIN*m_CharSize.cx, -m_LogicalPageSize.cy + FOOTER_HEIGHT*m_CharSize.cy);
  1711.     PrintFooter(pDC, pInfo);
  1712.     // SetWindowOrg back for next page
  1713.     pDC->SetWindowOrg(0,0);
  1714.     pDC->SelectObject(pOldFont);
  1715. }
  1716. void CGridCtrl::PrintColumnHeadings(CDC *pDC, CPrintInfo* /*pInfo*/)
  1717. {
  1718.     CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
  1719.     CRect rect;
  1720.     rect.bottom = -1;
  1721.     for (int row = 0; row < GetFixedRowCount(); row++)
  1722.     {
  1723.         rect.top = rect.bottom+1;
  1724.         rect.bottom = rect.top + GetRowHeight(row) - 1;
  1725.         rect.right = -1;
  1726.         for (int col = 0; col < GetColumnCount(); col++)
  1727.         {
  1728.             rect.left = rect.right+1;
  1729.             rect.right = rect.left + GetColumnWidth(col) - 1;
  1730.             DrawFixedCell(pDC, row, col, rect);
  1731.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ) 
  1732.             {
  1733.                 int Overlap = (col == 0)? 0:1;
  1734.                 pDC->MoveTo(rect.left-Overlap, rect.bottom);
  1735.                 pDC->LineTo(rect.right, rect.bottom);
  1736.                 if (row == 0) {
  1737.                     pDC->MoveTo(rect.left-Overlap, rect.top);
  1738.                     pDC->LineTo(rect.right, rect.top);
  1739.                 }
  1740.             }
  1741.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT) 
  1742.             {
  1743.                 int Overlap = (row == 0)? 0:1;
  1744.                 pDC->MoveTo(rect.right, rect.top-Overlap);
  1745.                 pDC->LineTo(rect.right, rect.bottom);    
  1746.                 if (col == 0) {
  1747.                     pDC->MoveTo(rect.left, rect.top-Overlap);
  1748.                     pDC->LineTo(rect.left, rect.bottom);    
  1749.                 }
  1750.             }
  1751.         }
  1752.     }
  1753.     pDC->SelectObject(pOldFont);
  1754. }
  1755. void CGridCtrl::PrintHeader(CDC *pDC, CPrintInfo *pInfo)
  1756. {
  1757.     CRect   rc(pInfo->m_rectDraw);
  1758.     CString strHeaderString;
  1759.     CFont   BoldFont;
  1760.     LOGFONT lf;
  1761.     //create bold font for header and footer
  1762.     VERIFY(m_PrinterFont.GetLogFont(&lf));
  1763.     lf.lfWeight = FW_BOLD;
  1764.     VERIFY(BoldFont.CreateFontIndirect(&lf));
  1765.  
  1766.     CFont *pNormalFont = pDC->SelectObject(&BoldFont);
  1767.     int nPrevBkMode = pDC->SetBkMode(TRANSPARENT);
  1768.     // print App title on top right margin
  1769.     strHeaderString.LoadString(AFX_IDS_APP_TITLE);
  1770.     pDC->DrawText(strHeaderString, &rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
  1771.     // print parent window title in the centre (Gert Rijs)
  1772.     CWnd *pParentWnd = GetParent();
  1773.     while (pParentWnd)
  1774.     {
  1775.         pParentWnd->GetWindowText(strHeaderString);
  1776.         if (strHeaderString.GetLength())  // can happen if it is a CView, CChildFrm has the title
  1777.             break;
  1778.         pParentWnd = pParentWnd->GetParent();
  1779.     }
  1780.     pDC->DrawText(strHeaderString, &rc, DT_CENTER | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
  1781.     pDC->SetBkMode(nPrevBkMode);
  1782.     pDC->SelectObject(pNormalFont);
  1783.     BoldFont.DeleteObject();
  1784.     pDC->SelectStockObject(BLACK_PEN);
  1785.     pDC->MoveTo(rc.left, rc.bottom);
  1786.     pDC->LineTo(rc.right, rc.bottom);
  1787. }
  1788. //print footer with a line and date, and page number
  1789. void CGridCtrl::PrintFooter(CDC *pDC, CPrintInfo *pInfo)
  1790. {
  1791.     CRect rc(pInfo->m_rectDraw);
  1792.     CFont BoldFont;
  1793.     LOGFONT lf;
  1794.     //draw line
  1795.     pDC->MoveTo(rc.left, rc.top);
  1796.     pDC->LineTo(rc.right, rc.top);
  1797.     //create bold font for header and footer
  1798.     m_PrinterFont.GetLogFont(&lf);
  1799.     lf.lfWeight = FW_BOLD;
  1800.     BoldFont.CreateFontIndirect(&lf);
  1801.     CFont *pNormalFont = pDC->SelectObject(&BoldFont);
  1802.     int nPrevBkMode = pDC->SetBkMode(TRANSPARENT);
  1803.     // draw page number
  1804.     CString   sTemp ;
  1805.     rc.OffsetRect(0, m_CharSize.cy/2);
  1806.     sTemp.Format(_T("Page %d of %d"), pInfo->m_nCurPage, pInfo->GetMaxPage());
  1807.     pDC->DrawText(sTemp,-1,rc, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER);
  1808.     CTime t = CTime::GetCurrentTime();
  1809.     sTemp = t.Format(_T("%c"));
  1810.     pDC->DrawText(sTemp,-1,rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER);
  1811.     pDC->SetBkMode(nPrevBkMode);
  1812.     pDC->SelectObject(pNormalFont);
  1813.     BoldFont.DeleteObject();
  1814. }
  1815. void CGridCtrl::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
  1816. {
  1817.     m_PrinterFont.DeleteObject();
  1818. }
  1819. #endif  // !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  1820. #ifndef _WIN32_WCE
  1821. /////////////////////////////////////////////////////////////////////////////
  1822. // CGridCtrl persistance
  1823. BOOL CGridCtrl::Save(LPCTSTR filename)
  1824. {
  1825.     CStdioFile File;
  1826.     CFileException ex;
  1827.     if (!File.Open(filename, CFile::modeWrite | CFile::modeCreate| CFile::typeText, &ex)) {
  1828.         ex.ReportError();
  1829.         return FALSE;
  1830.     }
  1831.     TRY {
  1832.         int nNumColumns = GetColumnCount();
  1833.         for (int i = 0; i < nNumColumns; i++) {
  1834.             File.WriteString(GetItemText(0,i));
  1835.             File.WriteString((i==(nNumColumns-1))? _T("n"):_T(","));
  1836.         }
  1837.         for (i = 0; i < GetRowCount(); i++) {
  1838.             for (int j = 0; j < nNumColumns; j++) {
  1839.                 File.WriteString(GetItemText(i,j));
  1840.                 File.WriteString((j==(nNumColumns-1))? _T("n"):_T(","));
  1841.             }
  1842.         }
  1843.         File.Close();
  1844.     }
  1845.     CATCH (CFileException, e) {
  1846.         AfxMessageBox(_T("Unable to save grid list"));
  1847.         e->Delete();
  1848.         return FALSE;
  1849.     }
  1850.     END_CATCH
  1851.     return TRUE;
  1852. }
  1853. BOOL CGridCtrl::Load(LPCTSTR filename)
  1854. {
  1855.     TCHAR *token, *end;
  1856.     TCHAR buffer[1024];
  1857.     CStdioFile File;
  1858.     CFileException ex;
  1859.     if (!File.Open(filename, CFile::modeRead | CFile::typeText)) {
  1860.         ex.ReportError();
  1861.         return FALSE;
  1862.     }
  1863.     DeleteAllItems();
  1864.     TRY {
  1865.         // Read Header off file
  1866.         File.ReadString(buffer, 1024);
  1867.         // Get first token
  1868.         for (token=buffer, end=buffer; 
  1869.              *end && (*end != _T(',')) && (*end != _T('n')); end++);
  1870.         if ((*end == _T('')) && (token == end)) token = NULL;
  1871.         *end = _T('');
  1872.         while (token) 
  1873.         {
  1874.             InsertColumn(token);
  1875.                 
  1876.             // Get next token
  1877.             for (token=++end; *end && (*end != _T(',')) && (*end != _T('n'));
  1878.                  end++);
  1879.             if ((*end == _T('')) && (token == end)) token = NULL;
  1880.             *end = _T('');
  1881.         }
  1882.         // Read in rest of data
  1883.         int nItem = 0;
  1884.         while (File.ReadString(buffer, 1024)) {
  1885.             // Get first token
  1886.             for (token=buffer, end=buffer; 
  1887.                  *end && (*end != _T(',')) && (*end != _T('n')); end++);
  1888.             if ((*end == _T('')) && (token == end)) token = NULL;
  1889.             *end = _T('');
  1890.             int nSubItem = 0;
  1891.             while (token) {
  1892.                 if (!nSubItem)
  1893.                     InsertRow(token);
  1894.                 else
  1895.                     SetItemText(nItem, nSubItem, token);
  1896.                 // Get next token
  1897.                 for (token=++end; *end && (*end != _T(',')) && (*end != _T('n'));
  1898.                      end++);
  1899.                 if ((*end == _T('')) && (token == end)) token = NULL;
  1900.                 *end = _T('');
  1901.                 nSubItem++;
  1902.             }
  1903.             nItem++;
  1904.         }
  1905.         AutoSizeColumns();
  1906.         File.Close();
  1907.     }
  1908.     CATCH (CFileException, e) {
  1909.         AfxMessageBox(_T("Unable to load grid data"));
  1910.         e->Delete();
  1911.         return FALSE;
  1912.     }
  1913.     END_CATCH
  1914.     return TRUE;
  1915. }
  1916. #endif
  1917. /////////////////////////////////////////////////////////////////////////////
  1918. // CGridCtrl overrideables
  1919. #ifndef GRIDCONTROL_NO_DRAGDROP
  1920. // This is no longer needed since I've changed to OLE drag and drop - but it's
  1921. // still cool code. :)
  1922. CImageList* CGridCtrl::CreateDragImage(CPoint *pHotSpot)
  1923. {
  1924.     CDC* pDC = GetDC();
  1925.     if (!pDC) return NULL;
  1926.     CRect rect;
  1927.     CCellID cell = GetFocusCell();
  1928.     if (!GetCellRect(cell.row, cell.col, rect)) return NULL;
  1929.     // Translate coordinate system
  1930.     rect.BottomRight() = CPoint(rect.Width(), rect.Height());
  1931.     rect.TopLeft()     = CPoint(0,0);
  1932.     *pHotSpot = rect.BottomRight(); 
  1933.     // Create a new imagelist (the caller of this function has responsibility
  1934.     // for deleting this list)
  1935.     CImageList* pList = new CImageList;
  1936.     if (!pList || !pList->Create(rect.Width(), rect.Height(), ILC_MASK, 1,1))
  1937.     {    
  1938.         if (pList) delete pList;
  1939.         return NULL;
  1940.     }
  1941.     // Create mem DC and bitmap
  1942.     CDC MemDC;
  1943.     CBitmap bm;
  1944.     MemDC.CreateCompatibleDC(pDC);
  1945.     bm.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
  1946.     CBitmap* pOldBitmap = MemDC.SelectObject(&bm);
  1947.     MemDC.SetWindowOrg(0,0);
  1948.     // Draw cell onto bitmap in memDC
  1949.     DrawCell(&MemDC, cell.row, cell.col, rect, TRUE);
  1950.     // Clean up
  1951.     MemDC.SelectObject(pOldBitmap);
  1952.     ReleaseDC(pDC);
  1953.     // Add the bitmap we just drew to the image list.
  1954.     pList->Add(&bm, GetTextBkColor());
  1955.     bm.DeleteObject();
  1956.     return pList;
  1957. }
  1958. #endif
  1959. void CGridCtrl::OnFixedRowClick(CCellID& cell)
  1960. {
  1961.     if (!IsValid(cell)) 
  1962.         return;
  1963.     if (m_bListMode)
  1964.     {
  1965.         if (!m_bSortOnClick) 
  1966.             return;
  1967.         CWaitCursor waiter;
  1968.         if (cell.col == m_SortColumn)
  1969.             m_bAscending = !m_bAscending;
  1970.         else 
  1971.         {
  1972.             m_bAscending = TRUE;
  1973.             m_SortColumn = cell.col;
  1974.         }
  1975.         SortTextItems(m_SortColumn, m_bAscending);
  1976.         Invalidate();
  1977.     } 
  1978.     else if (cell.col < GetFixedColumnCount()) 
  1979.     {
  1980.         m_MouseMode = MOUSE_SELECT_ALL;
  1981.         OnSelecting(cell);
  1982.     } 
  1983.     else 
  1984.     {
  1985.         m_MouseMode = MOUSE_SELECT_COL;
  1986.         OnSelecting(cell);
  1987.     }
  1988. }
  1989. void CGridCtrl::OnFixedColumnClick(CCellID& cell)
  1990. {
  1991.     if (!IsValid(cell))
  1992.         return;
  1993. //    if (m_bListMode && (GetItemState(cell.row, m_nFixedCols) & GVNI_SELECTED))
  1994. //    {
  1995. //        OnEditCell(cell.row, cell.col, VK_LBUTTON);
  1996. //        return;
  1997. //    }
  1998.     if (cell.row < GetFixedRowCount()) 
  1999.     {
  2000.         m_MouseMode = MOUSE_SELECT_ALL;
  2001.         OnSelecting(cell);
  2002.     }
  2003.     else 
  2004.     {
  2005.         m_MouseMode = MOUSE_SELECT_ROW;
  2006.         OnSelecting(cell);
  2007.     }
  2008. }
  2009. // Gets the extent of the text pointed to by str (no CDC needed)
  2010. // By default this uses the selected font (which is a bigger font)
  2011. CSize CGridCtrl::GetTextExtent(LPCTSTR str, BOOL bUseSelectedFont /* = TRUE */)
  2012. {
  2013.     CDC* pDC = GetDC();
  2014.     if (!pDC) return CSize(0,0);
  2015.     CFont *pOldFont, font;
  2016.     if (bUseSelectedFont)
  2017.     {
  2018.         LOGFONT lf;
  2019.         memcpy(&lf, &m_Logfont, sizeof(LOGFONT));
  2020.         lf.lfWeight = SELECTED_CELL_FONT_WEIGHT;
  2021.             
  2022.         font.CreateFontIndirect(&lf);
  2023.     
  2024.         pOldFont = pDC->SelectObject(&font);
  2025.     }
  2026.     else
  2027.         pOldFont = pDC->SelectObject(&m_Font);
  2028.     CSize size = pDC->GetTextExtent(str);
  2029.     pDC->SelectObject(pOldFont);
  2030.     ReleaseDC(pDC);
  2031.     return size + CSize(2*m_nMargin, 2*m_nMargin);
  2032. }
  2033. CSize CGridCtrl::GetCellExtent(int nRow, int nCol, CDC* pDC)
  2034. {
  2035.     LOGFONT *pLF = GetItemFont(nRow, nCol);
  2036.     
  2037.     // use selected font since it's thicker   
  2038.     LOGFONT lf;
  2039.     memcpy(&lf, pLF, sizeof(LOGFONT));
  2040.     
  2041.     if (nRow < m_nFixedRows || nCol < m_nFixedCols)
  2042.         lf.lfWeight = SELECTED_CELL_FONT_WEIGHT;
  2043.             
  2044.     CFont font;
  2045.     font.CreateFontIndirect(&lf);
  2046.     
  2047.     CFont* pOldFont = pDC->SelectObject(&font);
  2048.     CSize size = pDC->GetTextExtent(GetItemText(nRow, nCol));
  2049.     pDC->SelectObject(pOldFont);
  2050.     size += CSize(4*m_nMargin, 2*m_nMargin);
  2051.     CSize ImageSize(0,0);
  2052.     if (m_pImageList) {
  2053.         int nImage = GetItemImage(nRow, nCol);
  2054.         if (nImage >= 0) {
  2055.             IMAGEINFO Info;
  2056.             if (m_pImageList->GetImageInfo(nImage, &Info))
  2057.                 ImageSize = CSize(Info.rcImage.right-Info.rcImage.left+1, 
  2058.                                   Info.rcImage.bottom-Info.rcImage.top+1);
  2059.         }
  2060.     }
  2061.     
  2062.     return CSize(size.cx + ImageSize.cx, max(size.cy, ImageSize.cy));
  2063. }
  2064. BOOL CGridCtrl::DrawFixedCell(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBk)
  2065. {
  2066.     if (!m_bAllowDraw) 
  2067.         return FALSE;
  2068.     GV_ITEM Item;
  2069.     Item.mask = GVIF_TEXT | GVIF_FORMAT | GVIF_IMAGE | GVIF_BKCLR | GVIF_FGCLR;
  2070.     Item.row = nRow;
  2071.     Item.col = nCol;
  2072.     if (!GetItem(&Item))
  2073.         return FALSE;
  2074.     // Force redraw of background if custom colour
  2075.     //if (Item.crBkClr != CLR_DEFAULT)
  2076.         bEraseBk = TRUE;
  2077.     if (bEraseBk
  2078. #if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  2079.         && !pDC->IsPrinting()
  2080. #endif
  2081.         )
  2082.     {
  2083.         CBrush brush((Item.crBkClr == CLR_DEFAULT)? GetFixedBkColor() : Item.crBkClr);
  2084.         pDC->FillRect(rect, &brush);
  2085.     }
  2086.     pDC->SetTextColor((Item.crFgClr == CLR_DEFAULT)? GetFixedTextColor() : Item.crFgClr);
  2087.     
  2088.     int nSavedDC = pDC->SaveDC();
  2089.     
  2090.     // Create the appropriate font and select into DC
  2091.     LOGFONT *pLF = GetItemFont(nRow, nCol);
  2092.     CCellID FocusCell = GetFocusCell();
  2093.     // if (IsCellFixed(nRow, nCol) && (FocusCell.row == nRow || FocusCell.col == nCol))
  2094.     //{
  2095.     if (FocusCell.row == nRow || FocusCell.col == nCol)
  2096.     {
  2097.         static LOGFONT lf;
  2098.         memcpy(&lf, pLF, sizeof(LOGFONT));
  2099.         lf.lfWeight = SELECTED_CELL_FONT_WEIGHT;
  2100.         pLF = &lf;
  2101.     }
  2102.    
  2103.     CFont Font;
  2104.     Font.CreateFontIndirect(pLF);
  2105.     pDC->SelectObject(&Font);
  2106.     if (IsValid(FocusCell) &&  (FocusCell.row == nRow || FocusCell.col == nCol))
  2107.     {
  2108.         rect.right++; rect.bottom++;
  2109.         pDC->DrawEdge(rect, BDR_SUNKENINNER /*EDGE_RAISED*/, BF_RECT);
  2110.         rect.DeflateRect(1,1);
  2111.     }
  2112.     else
  2113.     {
  2114.         CPen lightpen(PS_SOLID, 1,  ::GetSysColor(COLOR_3DHIGHLIGHT)),
  2115.               darkpen(PS_SOLID,  1, ::GetSysColor(COLOR_3DDKSHADOW)),
  2116.              *pOldPen = pDC->GetCurrentPen();
  2117.     
  2118.         pDC->SelectObject(&lightpen);
  2119.         pDC->MoveTo(rect.right, rect.top);
  2120.         pDC->LineTo(rect.left, rect.top);
  2121.         pDC->LineTo(rect.left, rect.bottom);
  2122.         pDC->SelectObject(&darkpen);
  2123.         pDC->MoveTo(rect.right, rect.top);
  2124.         pDC->LineTo(rect.right, rect.bottom);
  2125.         pDC->LineTo(rect.left, rect.bottom);
  2126.         pDC->SelectObject(pOldPen);
  2127.         rect.DeflateRect(1,1);
  2128.     }
  2129.     pDC->SetBkMode(TRANSPARENT);
  2130.     rect.DeflateRect(m_nMargin, 0);
  2131.     if (m_pImageList && Item.iImage >= 0)
  2132.     {
  2133.         IMAGEINFO Info;
  2134.         if (m_pImageList->GetImageInfo(Item.iImage, &Info))
  2135.         {
  2136.             //CRgn rgn;
  2137.             //rgn.CreateRectRgnIndirect(rect);
  2138.             //pDC->SelectClipRgn(&rgn);
  2139.             //rgn.DeleteObject();
  2140.     
  2141.             int nImageWidth = Info.rcImage.right-Info.rcImage.left+1;
  2142.             m_pImageList->Draw(pDC, Item.iImage, rect.TopLeft(), ILD_NORMAL);
  2143.             rect.left += nImageWidth+m_nMargin;
  2144.         }
  2145.     }
  2146.     DrawText(pDC->m_hDC, Item.szText, -1, rect, Item.nFormat);
  2147.     pDC->RestoreDC(nSavedDC);
  2148.     Font.DeleteObject();
  2149.     return TRUE;
  2150. }
  2151. BOOL CGridCtrl::DrawCell(CDC* pDC, int nRow, int nCol, CRect rect, BOOL bEraseBk)
  2152. {
  2153.     if (!m_bAllowDraw) 
  2154.         return FALSE;
  2155.     GV_ITEM Item;
  2156.     Item.mask = GVIF_TEXT | GVIF_FORMAT | GVIF_STATE | GVIF_IMAGE | GVIF_BKCLR | GVIF_FGCLR;
  2157.     Item.row = nRow;
  2158.     Item.col = nCol;
  2159.     if (!GetItem(&Item))
  2160.         return FALSE;
  2161.     COLORREF TextBkClr = (Item.crBkClr == CLR_DEFAULT)? GetTextBkColor() : Item.crBkClr;
  2162.     COLORREF TextClr   = (Item.crFgClr == CLR_DEFAULT)? GetTextColor()   : Item.crFgClr;
  2163.     // Force redraw of background if custom colour
  2164.     //if (Item.crBkClr != CLR_DEFAULT)
  2165.         bEraseBk = TRUE;
  2166.     int nSavedDC = pDC->SaveDC();
  2167.     pDC->SetBkMode(TRANSPARENT);
  2168.     if (Item.state & GVIS_FOCUSED 
  2169. #if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  2170.         && !pDC->IsPrinting()
  2171. #endif
  2172.         ) 
  2173.     {
  2174.         rect.right++; rect.bottom++;    // FillRect doesn't draw RHS or bottom
  2175.         if (bEraseBk) 
  2176.         {
  2177.             CBrush brush(TextBkClr);
  2178.             pDC->FillRect(rect, &brush);
  2179.         }
  2180.         rect.right--; rect.bottom--;    
  2181.         pDC->SelectStockObject(BLACK_PEN);
  2182.         pDC->SelectStockObject(NULL_BRUSH);
  2183.         pDC->Rectangle(rect);
  2184. pDC->DrawFocusRect(&rect);
  2185.         pDC->SetTextColor(TextClr);
  2186.         rect.DeflateRect(1,1);
  2187.     }
  2188.     else if (Item.state & GVIS_SELECTED 
  2189. #if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  2190.         && !pDC->IsPrinting()
  2191. #endif
  2192.         ) 
  2193.     {
  2194.         rect.right++; rect.bottom++;    // FillRect doesn't draw RHS or bottom
  2195.         pDC->FillSolidRect(rect, GetSelectedBkColor() );
  2196.         rect.right--; rect.bottom--;
  2197.         pDC->SetTextColor(TextClr); //::GetSysColor(COLOR_HIGHLIGHTTEXT));
  2198.     } else {
  2199.         rect.right++; rect.bottom++;    // FillRect doesn't draw RHS or bottom
  2200.         if (bEraseBk) 
  2201.         {
  2202.             CBrush brush(TextBkClr);
  2203.             pDC->FillRect(rect, &brush);
  2204.         }
  2205.         rect.right--; rect.bottom--;
  2206.         pDC->SetTextColor(TextClr);
  2207.     }
  2208.     if (Item.state & GVIS_DROPHILITED 
  2209. #if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  2210.         && !pDC->IsPrinting()
  2211. #endif
  2212.         )
  2213.     {
  2214.         pDC->SelectStockObject(BLACK_PEN);
  2215.         pDC->SelectStockObject(NULL_BRUSH);
  2216.         pDC->Rectangle(rect);
  2217.     }
  2218.     // Create the appropriate font and select into DC
  2219.     LOGFONT *pLF = GetItemFont(nRow, nCol);
  2220.     //CCellID FocusCell = GetFocusCell();
  2221.     //if (IsCellFixed(nRow, nCol) && (FocusCell.row == nRow || FocusCell.col == nCol))
  2222.     //{
  2223.     //    static LOGFONT lf;
  2224.     //    memcpy(&lf, pLF, sizeof(LOGFONT));
  2225.     //    lf.lfWeight = SELECTED_CELL_FONT_WEIGHT;
  2226.     //    pLF = &lf;
  2227.     //}
  2228.    
  2229.     CFont Font;
  2230.     Font.CreateFontIndirect(pLF);
  2231.     pDC->SelectObject(&Font);
  2232.     rect.DeflateRect(m_nMargin, 0);
  2233.     if (m_pImageList && Item.iImage >= 0)
  2234.     {
  2235.         IMAGEINFO Info;
  2236.         if (m_pImageList->GetImageInfo(Item.iImage, &Info))
  2237.         {
  2238.             //CRgn rgn;
  2239.             //rgn.CreateRectRgnIndirect(rect);
  2240.             //pDC->SelectClipRgn(&rgn);
  2241.             //rgn.DeleteObject();
  2242.             int nImageWidth = Info.rcImage.right-Info.rcImage.left+1;
  2243.             m_pImageList->Draw(pDC, Item.iImage, rect.TopLeft(), ILD_NORMAL);
  2244.             rect.left += nImageWidth+m_nMargin;
  2245.         }
  2246.     }
  2247.     DrawText(pDC->m_hDC, Item.szText, -1, rect, Item.nFormat);
  2248.     pDC->RestoreDC(nSavedDC);
  2249.     Font.DeleteObject();
  2250.     return TRUE;
  2251. }
  2252. void CGridCtrl::OnEditCell(int nRow, int nCol, UINT nChar)
  2253. {
  2254.     EnsureVisible(nRow, nCol);
  2255.     CCellID cell(nRow, nCol);
  2256.     if (!IsValid(cell) || !IsCellEditable(nRow, nCol) || !IsCellVisible(nRow, nCol)) 
  2257.         return;
  2258.     CRect rect;
  2259.     if (!GetCellRect(cell, rect)) return;
  2260.     SendMessageToParent(nRow, nCol, GVN_BEGINLABELEDIT);
  2261.     GV_ITEM Item;
  2262.     Item.mask = GVIF_TEXT | GVIF_FORMAT;
  2263.     Item.row = nRow;
  2264.     Item.col = nCol;
  2265.     if (!GetItem(&Item)) return;
  2266.     DWORD dwStyle = ES_LEFT;
  2267.     if (Item.nFormat & DT_RIGHT) dwStyle = ES_RIGHT;
  2268.     else if (Item.nFormat & DT_CENTER) dwStyle = ES_CENTER;
  2269.     CreateInPlaceEditControl(rect, dwStyle, IDC_INPLACE_CONTROL, 
  2270.                              nRow, nCol, Item.szText, nChar);
  2271. }
  2272. void CGridCtrl::CreateInPlaceEditControl(CRect& rect, DWORD dwStyle, UINT nID,
  2273.                                          int nRow, int nCol,
  2274.                                          LPCTSTR szText, int nChar)
  2275. {
  2276.     // InPlaceEdit auto-deletes itself
  2277.     new CInPlaceEdit(this, rect, dwStyle, nID, nRow, nCol, szText, nChar);
  2278. }
  2279. void CGridCtrl::OnEndEditCell(int nRow, int nCol, CString str)
  2280. {
  2281. CString strCurrent = GetItemText(nRow,nCol);
  2282. if (strCurrent != str)
  2283.     {
  2284. SetModified(TRUE, nRow, nCol);
  2285.         SetItemText(nRow, nCol, str);
  2286. }
  2287. }
  2288. CString CGridCtrl::GetItemText(int nRow, int nCol)
  2289. {
  2290.     if (nRow < 0 || nRow >= m_nRows || nCol < 0 || nCol >= m_nCols) return "";
  2291.     CGridCell* pCell = GetCell(nRow, nCol);
  2292.     ASSERT(pCell);
  2293.     if (!pCell) return "";
  2294.     return pCell->szText;
  2295. }