GRIDCTRL.CPP
上传用户:asikq0571
上传日期:2014-07-12
资源大小:528k
文件大小:156k
源码类别:

Internet/IE编程

开发平台:

Visual C++

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