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

金融证券系统

开发平台:

Visual C++

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