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

Internet/IE编程

开发平台:

Visual C++

  1. // GridCtrl.cpp : implementation file
  2. //
  3. // MFC Grid Control
  4. //
  5. // Written by Chris Maunder 
  6. //        mailto:chrismaunder@codeguru.com
  7. //
  8. // Copyright (c) 1998.
  9. //
  10. // The code contained in this file is based on the original
  11. // WorldCom Grid control written by Joe Willcoxson,
  12. //        mailto:chinajoe@aol.com
  13. //        http://users.aol.com/chinajoe
  14. //
  15. // This code may be used in compiled form in any way you desire. This
  16. // file may be redistributed unmodified by any means PROVIDING it is 
  17. // not sold for profit without the authors written consent, and 
  18. // providing that this notice and the authors name and all copyright 
  19. // notices remains intact. If the source code in this file is used in 
  20. // any  commercial application then a statement along the lines of 
  21. // "Portions copyright (c) Chris Maunder, 1998" must be included in 
  22. // the startup banner, "About" box or printed documentation. An email 
  23. // letting me know that you are using it would be nice as well. That's 
  24. // not much to ask considering the amount of work that went into this.
  25. //
  26. // This file is provided "as is" with no expressed or implied warranty.
  27. // The author accepts no liability for any damage/loss of business that
  28. // this product may cause.
  29. //
  30. // Expect bugs!
  31. // 
  32. // Please use and enjoy, and let me know of any bugs/mods/improvements 
  33. // that you have found/implemented and I will fix/incorporate them into 
  34. // this file. 
  35. //
  36. //  History:
  37. //  --------
  38. //  This control is constantly evolving, sometimes due to new features that I
  39. //  feel are necessary, and sometimes due to existing bugs. Where possible I 
  40. //  have credited the changes to those who contributed code corrections or
  41. //  enhancements (names in brackets) or code suggestions (suggested by...)
  42. //   
  43. //          1.0     20 Feb 1998   First release version.
  44. //          1.01    24 Feb 1998   Memory leak fix (Jens Bohlmann)
  45. //                                Fixec typo (my fault!) in CMemDC.h - Claus Arend-Schneider)
  46. //                                Bug in GetSelectedCount (Lyn Newton)
  47. //          1.02    4  Mar 1998   Scrolling a little neater (less dead area)
  48. //                                Cell selection via OnTimer correctly updates Focus cell (Suggested by Lyn Newton)
  49. //          1.03    17 Mar 1998   Clipboard functions added, Intellimouse support
  50. //                                Using 32 bit scroll pos functions instead of 16 bit ("cronos")
  51. //                                Added OLE drag and drop.
  52. //          1.04     6 Apr 1998   Added Ctrl-A = Select All, fixed CGridDropTarget 
  53. //                                problem, minor bug in CopyTextFromGrid (assert on
  54. //                                empty string). Cleaned up reponse to m_bEditable
  55. //                                (OnDrop and Ctrl-X disabled)
  56. //          1.05    10 May 1998   Memory leak fixed. (Yuheng Zhao)
  57. //                                Changed OLE initialisation (Carlo Comino)
  58. //                                Added separate fore + background cell colours (Suggested by John Crane)
  59. //                                ExpandToFit etc cleaned up - now decreases and
  60. //                                increases cell sizes to fit client area.
  61. //                                Added notification messages for the grid's parent (Suggested by 
  62. //                                Added GVIS_READONLY state
  63. //          1.06    20 May 1998   Added TAB key handling. (Daniela Rybarova)
  64. //                                Intellimouse code correction for whole page scrolling (Paul Grant) 
  65. //                                Fixed 16 bit thumb track problems (now 32 bit) (Paul Grant) 
  66. //                                Fixed accelerator key problem in CInPlaceEdit (Matt Weagle)
  67. //                                Fixed Stupid ClassWizard code parsing problem (Michael A. Barnhart)
  68. //                                Double buffering now programmatically selectable
  69. //                                Workaround for win95 drag and drop registration problem
  70. //                                Corrected UNICODE implementation of clipboard stuff
  71. //                                Dragging and dropping from a selection onto itself no 
  72. //                                no longer causes the cells to be emptied
  73. //          1.07    28 Jul 1998   Added EnsureVisible. (Roelf Werkman)
  74. //                                Fixed delete key problem on read-only cells. (Serge Weinstock)
  75. //                                OnEndInPlaceEdit sends notification AFTER storing
  76. //                                the modified text in the cell.
  77. //                                Added CreateInPlaceEditControl to make it easier to 
  78. //                                change the way cells are edited. (suggested by Chris Clark)
  79. //                                Added Set/GetGridColor.
  80. //                                CopyTextToClipboard and PasteTextToGrid problem with 
  81. //                                blank cells fixed, and CopyTextToClipboard tweaked.
  82. //                                SetModified called when cutting text or hitting DEL. (Jonathan Watters)
  83. //                                Focus cell made visible when editing begins.
  84. //                                Blank lines now treated correctly when pasting data.
  85. //                                Removed ES_MULTILINE style from the default edit control.
  86. //                                Added virtual CreateCell(row, col) function.
  87. //                                Fonts now specified on a per-cell basis using Get/SetItemFont.
  88. //          1.08     6 Aug 1998   Ctrl+arrows now allows cell navigation. Modified
  89. //                                CreateInPlaceEditControl to accept ID of control.
  90. //                                Added Titletips to grid cells. (Added EnableTitleTips / GetTitleTips)
  91. //          1.09    12 Sep 1998   When printing, parent window title is printed in header - Gert Rijs
  92. //                                GetNextItem search with GVNI_DROPHILITED now returns
  93. //                                cells with GVIS_DROPHILITED set, instead of GVIS_FOCUSED (Franco Bez)
  94. //                                (Also fixed minor bug in GetNextItem) (Franco Bez)
  95. //                                Cell selection using Shift+arrows works - Franco Bez 
  96. //                                SetModified called after edits ONLY if contents changed (Franco Bez)
  97. //                                Cell colours now dithered in 256 colour screens.
  98. //                                Support for MSVC 4.2 (Graham Cheetham)
  99. //          1.10    30 Nov 1998   Titletips now disappear on a scroll event. Compiler errors
  100. //                                fixed. Grid lines drawing fixed (Graham Cheetham). 
  101. //                                Cell focus fix on Isert Row/Col (Jochen Kauffmann) 
  102. //                                Added DeleteNonFixedRows() (John Rackley)
  103. //                                Message #define conflict fixed (Oskar Wieland)
  104. //                                Titletips & cell insert/delete fix (Ramesh Dhar) 
  105. //                                Titletips repeat-creation bug fixed.
  106. //                                GVN_SELCHANGED message now sends current cell ID
  107. //                                Font resource leak in GetTextExtent fixed (Gavin Jerman)
  108. //                                More TAB fixes (Andreas Ruh)
  109. //          1.11    1 Dec 1998    GetNextItem bug fix (suggested by Francis Fu)
  110. //                                InsertColumn (-1) fix (Roy Hopkins)
  111. //                                Was too liberal with the "IsEditable"'s. oops. (Michel Hete)
  112. //                                          
  113. // TODO:
  114. //    - OnOutOfMemory function instead of exceptions
  115. //    - Decrease timer interval over time to speed up selection over time
  116. // 
  117. // NOTE: Grid data is stored row-by-row, so all operations on large numbers
  118. //       of cells should be done row-by-row as well.
  119. // 
  120. // KNOWN ISSUES TO BE ADDRESSED (Please don't send bug reports): 
  121. // * Killfocus comes to late when a command is selected by the Menu. 
  122. //   When you are editing a cell and choose a Menuitem that searches for all the
  123. //   modified cells it is not found. When you chose the menu a second time it is
  124. //   found. I assume that the Menu command is executed before the cell receives the
  125. //   KillFocus event. Expect similar Problems with accelerators. (Franco Bez)
  126. // * When you select a cell and move the mouse around (with the Left button down 
  127. //   i.e continuing with your selection) - if the mouse is over the Fixed column 
  128. //   or Row the drawing of the selected region is strange - in particular as you 
  129. //   move up and down say the Left Fixed Column notice the behaviour of the Focus 
  130. //   Cell - it is out of sync. (Vinay Desai)
  131. //
  132. /////////////////////////////////////////////////////////////////////////////
  133.     
  134. #include "stdafx.h"
  135. #include "MemDC.h"
  136. #include "GridCtrl.h"
  137. #include "InPlaceEdit.h"
  138. #include "..PublicFunc.h"
  139. // OLE stuff for clipboard operations
  140. #include <afxadv.h>            // For CSharedFile
  141. #include <afxole.h>            // For COleDataSource
  142. #include <afxconv.h>           // For LPTSTR -> LPSTR macros
  143. #ifdef _DEBUG
  144. #define new DEBUG_NEW
  145. #undef THIS_FILE
  146. static char THIS_FILE[] = __FILE__;
  147. #endif
  148. #define HEADER_HEIGHT       2    // For printing
  149. #define FOOTER_HEIGHT       2
  150. #define LEFT_MARGIN         4
  151. #define RIGHT_MARGIN        4
  152. #define TOP_MARGIN          1
  153. #define BOTTOM_MARGIN       1 
  154. #define GAP                 1
  155. #define SELECTED_CELL_FONT_WEIGHT 600    // wieght of text for selected items
  156. #define IDC_INPLACE_EDIT            8    // ID of inplace edit controls
  157. IMPLEMENT_DYNCREATE(CGridCtrl, CWnd)
  158. void AFXAPI DDX_GridControl(CDataExchange* pDX, int nIDC, CGridCtrl& rControl)
  159. {
  160.     if (rControl.GetSafeHwnd() == NULL)    // not subclassed yet
  161.     {
  162.         ASSERT(!pDX->m_bSaveAndValidate);
  163.         HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  164.         if (!rControl.SubclassWindow(hWndCtrl))
  165.         {
  166.             ASSERT(FALSE);      // possibly trying to subclass twice?
  167.             AfxThrowNotSupportedException();
  168.         }
  169. #ifndef _AFX_NO_OCC_SUPPORT
  170.         else
  171.         {
  172.             // If the control has reparented itself (e.g., invisible control),
  173.             // make sure that the CWnd gets properly wired to its control site.
  174.             if (pDX->m_pDlgWnd->GetSafeHwnd() != ::GetParent(rControl.GetSafeHwnd()))
  175.                 rControl.AttachControlSite(pDX->m_pDlgWnd);
  176.         }
  177. #endif //!_AFX_NO_OCC_SUPPORT
  178.     }
  179. }
  180. // Get the number of lines to scroll with each mouse wheel notch
  181. // Why doesn't windows give us this function???
  182. UINT GetMouseScrollLines()
  183. {
  184.     int nScrollLines = 3;            // reasonable default
  185.     HKEY hKey;
  186.     if (RegOpenKeyEx(HKEY_CURRENT_USER,  _T("Control Panel\Desktop"),
  187.                      0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
  188.     {
  189.         TCHAR szData[128];
  190.         DWORD dwKeyDataType;
  191.         DWORD dwDataBufSize = sizeof(szData);
  192.         if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
  193.                            (LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
  194.         {
  195.             nScrollLines = _tcstoul(szData, NULL, 10);
  196.         }
  197.         RegCloseKey(hKey);
  198.     }
  199.     return nScrollLines;
  200. }
  201. /////////////////////////////////////////////////////////////////////////////
  202. // CGridCtrl
  203. CGridCtrl::CGridCtrl(int nRows, int nCols, int nFixedRows, int nFixedCols)
  204. {
  205.     RegisterWindowClass();
  206.     // Initialize OLE libraries
  207. m_bIsYXGrid = FALSE;
  208.     m_bMustUninitOLE = FALSE;
  209. #ifdef GRIDCONTROL_USE_OLE_DRAGDROP
  210.     _AFX_THREAD_STATE* pState = AfxGetThreadState();
  211.     if (!pState->m_bNeedTerm)
  212.     {
  213.         SCODE sc = ::OleInitialize(NULL);
  214.         if (FAILED(sc))
  215.             AfxMessageBox(_T("OLE initialization failed. Make sure that the OLE libraries are the correct version"));
  216.         else
  217.             m_bMustUninitOLE = TRUE;
  218.     }
  219. #endif
  220.     // Store the system colours in case they change. The gridctrl uses
  221.     // these colours, and in OnSysColorChange we can check to see if 
  222.     // the gridctrl colours have been changed from the system colours.
  223.     // If they have, then leave them, otherwise change them to reflect
  224.     // the new system colours.
  225.     m_crWindowText       = ::GetSysColor(COLOR_WINDOWTEXT);
  226.     m_crWindowColour     = ::GetSysColor(COLOR_WINDOW);
  227.     m_cr3DFace           = ::GetSysColor(COLOR_3DFACE);
  228.     m_crShadow           = ::GetSysColor(COLOR_3DSHADOW);
  229.     m_crGridColour       = RGB(0,0,0);
  230.     m_nRows              = 0;
  231.     m_nCols              = 0;
  232.     m_nFixedRows         = 0;
  233.     m_nFixedCols         = 0;
  234.     m_nDefCellHeight     = 10;        // These will get changed to something meaningful
  235.     m_nDefCellHeight     = 30;        //    when the window is created or subclassed
  236.     m_nVScrollMax        = 0;         // Scroll position
  237.     m_nHScrollMax        = 0;
  238.     m_nMargin            = 0;         // cell padding
  239.     m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
  240.                                                   // per mouse wheel notch to scroll
  241.     m_MouseMode          = MOUSE_NOTHING;
  242.     m_nGridLines         = GVL_BOTH;
  243.     m_bEditable          = TRUE;
  244.     m_bListMode          = FALSE;
  245.     m_bAllowDraw         = TRUE;      // allow draw updates
  246.     m_bEnableSelection   = TRUE;
  247.     m_bAllowRowResize    = TRUE;
  248.     m_bAllowColumnResize = TRUE;
  249.     m_bSortOnClick       = TRUE;      // Sort on header row click if in list mode
  250.     m_bHandleTabKey      = TRUE;
  251.     m_bDoubleBuffer      = TRUE;      // Use double buffering to avoid flicker?
  252.     m_bTitleTips         = TRUE;      // show cell title tips
  253.     m_bAscending         = TRUE;      // sorting stuff
  254.     m_SortColumn         = -1;
  255.     m_nTimerID           = 0;         // For drag-selection
  256.     m_nTimerInterval     = 25;        // (in milliseconds)
  257.     m_nResizeCaptureRange = 3;        // When resizing columns/row, the cursor has to be 
  258.                                       // within +/-3 pixels of the dividing line for 
  259.                                       // resizing to be possible
  260.     m_pImageList         = NULL;      
  261.     m_bAllowDragAndDrop  = FALSE;     // for drag and drop
  262.     // Initially use the system message font for the GridCtrl font
  263.     NONCLIENTMETRICS ncm;
  264.     ncm.cbSize = sizeof(NONCLIENTMETRICS);
  265.     VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));    
  266.     memcpy(&m_Logfont, &(ncm.lfMessageFont), sizeof(LOGFONT));
  267.     // Set up the initial grid size
  268.     SetRowCount(nRows);
  269.     SetColumnCount(nCols);
  270.     SetFixedRowCount(nFixedRows);
  271.     SetFixedColumnCount(nFixedCols);
  272.     // Set the colours
  273.     SetTextColor(m_crWindowText);
  274.     SetTextBkColor(m_crWindowColour);
  275.     SetBkColor(m_crShadow);
  276.     SetFixedTextColor(m_crWindowText);
  277.     SetFixedBkColor(m_cr3DFace);
  278.     // set initial selection range (ie. none)
  279.     m_SelectedCellMap.RemoveAll();
  280.     m_PrevSelectedCellMap.RemoveAll();
  281. }
  282. CGridCtrl::~CGridCtrl()
  283. {
  284.     DeleteAllItems();
  285.     DestroyWindow();
  286.     m_Font.DeleteObject();
  287.     // Uninitialize OLE support
  288.     if (m_bMustUninitOLE)
  289.         ::OleUninitialize();
  290. }
  291. // Register the window class if it has not already been registered.
  292. BOOL CGridCtrl::RegisterWindowClass()
  293. {
  294.     WNDCLASS wndcls;
  295.     HINSTANCE hInst = AfxGetResourceHandle();
  296.     if (!(::GetClassInfo(hInst, GRIDCTRL_CLASSNAME, &wndcls)))
  297.     {
  298.         // otherwise we need to register a new class
  299.         wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  300.         wndcls.lpfnWndProc      = ::DefWindowProc;
  301.         wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
  302.         wndcls.hInstance        = hInst;
  303.         wndcls.hIcon            = NULL;
  304.         wndcls.hCursor          = NULL;
  305.         wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
  306.         wndcls.lpszMenuName     = NULL;
  307.         wndcls.lpszClassName    = GRIDCTRL_CLASSNAME;
  308.         if (!AfxRegisterClass(&wndcls)) {
  309.             AfxThrowResourceException();
  310.             return FALSE;
  311.         }
  312.     }
  313.     return TRUE;
  314. }
  315. BOOL CGridCtrl::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
  316. {
  317.     ASSERT(pParentWnd->GetSafeHwnd());
  318.     if (!CWnd::Create(GRIDCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID)) 
  319.         return FALSE;
  320. #ifdef GRIDCONTROL_USE_OLE_DRAGDROP
  321.     m_DropTarget.Register(this);
  322. #endif
  323.     // Create titletips
  324. #ifdef GRIDCONTROL_USE_TITLETIPS
  325.     if (m_bTitleTips)        
  326.         m_TitleTip.Create(this);
  327. #endif
  328.     // The number of rows and columns will only be non-zero if the constructor
  329.     // was called with non-zero initialising parameters. If this window was created
  330.     // using a dialog template then the number of rows and columns will be 0 (which
  331.     // means that the code below will not be needed - which is lucky 'cause it ain't
  332.     // gonna get called in a dialog-template-type-situation.
  333.     try {
  334.         m_arRowHeights.SetSize(m_nRows);    // initialize row heights
  335.         m_arColWidths.SetSize(m_nCols);     // initialize column widths
  336.     }
  337.     catch (CMemoryException *e) {
  338.         e->ReportError();
  339.         e->Delete();
  340.         return FALSE;
  341.     }
  342.     for (int i = 0; i < m_nRows; i++) m_arRowHeights[i] = m_nDefCellHeight;
  343.     for (i = 0; i < m_nCols; i++)      m_arColWidths[i] = m_nDefCellWidth;
  344.     ResetScrollBars();
  345.     return TRUE;
  346. }
  347. void CGridCtrl::PreSubclassWindow() 
  348. {    
  349.     CWnd::PreSubclassWindow();
  350.     HFONT hFont = ::CreateFontIndirect(&m_Logfont);
  351.     OnSetFont((LPARAM)hFont, 0);
  352.     DeleteObject(hFont);
  353.     
  354.     ResetScrollBars();   
  355. }
  356. BOOL CGridCtrl::SubclassWindow(HWND hWnd) 
  357. {    
  358.     if (!CWnd::SubclassWindow(hWnd)) return FALSE;
  359. #ifdef GRIDCONTROL_USE_OLE_DRAGDROP
  360.     m_DropTarget.Register(this);
  361. #endif
  362. #ifdef GRIDCONTROL_USE_TITLETIPS
  363.     if (m_bTitleTips && !IsWindow(m_TitleTip.m_hWnd))
  364.         m_TitleTip.Create(this);
  365. #endif
  366.     return TRUE;
  367. }
  368. LRESULT CGridCtrl::SendMessageToParent(int nRow, int nCol, int nMessage)
  369. {
  370.     if (!IsWindow(m_hWnd))
  371.         return 0;
  372.     NM_GRIDVIEW nmgv;
  373.     nmgv.iRow         = nRow;
  374.     nmgv.iColumn      = nCol;
  375.     nmgv.hdr.hwndFrom = m_hWnd;
  376.     nmgv.hdr.idFrom   = GetDlgCtrlID();
  377.     nmgv.hdr.code     = nMessage;
  378.     CWnd *pOwner = GetOwner();
  379.     if (pOwner && IsWindow(pOwner->m_hWnd))
  380.         return pOwner->SendMessage(WM_NOTIFY, nmgv.hdr.idFrom, (LPARAM)&nmgv);
  381.     else
  382.         return 0;
  383. }
  384. BEGIN_MESSAGE_MAP(CGridCtrl, CWnd)
  385.     //{{AFX_MSG_MAP(CGridCtrl)
  386.     ON_WM_PAINT()
  387.     ON_WM_HSCROLL()
  388.     ON_WM_VSCROLL()
  389.     ON_WM_SIZE()
  390.     ON_WM_LBUTTONUP()
  391.     ON_WM_LBUTTONDOWN()
  392.     ON_WM_MOUSEMOVE()
  393.     ON_WM_TIMER()
  394.     ON_WM_GETDLGCODE()
  395.     ON_WM_KEYDOWN()
  396.     ON_WM_CHAR()
  397.     ON_WM_LBUTTONDBLCLK()
  398.     ON_WM_ERASEBKGND()
  399.     ON_WM_SETCURSOR()
  400.     ON_WM_SYSCOLORCHANGE()
  401.     ON_WM_CAPTURECHANGED()
  402.     ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
  403.     ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
  404.     ON_COMMAND(ID_EDIT_CUT, OnEditCut)
  405.     ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
  406.     ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
  407.     ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
  408.     ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
  409.     ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
  410.     //}}AFX_MSG_MAP
  411. #if _MFC_VER >= 0x0421
  412.     ON_WM_MOUSEWHEEL()
  413.     ON_WM_SETTINGCHANGE()
  414. #endif
  415.     ON_MESSAGE(WM_SETFONT, OnSetFont)
  416.     ON_MESSAGE(WM_GETFONT, OnGetFont)
  417.     ON_NOTIFY(GVN_ENDLABELEDIT, IDC_INPLACE_EDIT, OnEndInPlaceEdit)
  418. END_MESSAGE_MAP()
  419. /////////////////////////////////////////////////////////////////////////////
  420. // CGridCtrl message handlers
  421. void CGridCtrl::OnPaint() 
  422. {
  423.     CPaintDC dc(this);      // device context for painting
  424.     if (m_bDoubleBuffer)    // Use a memory DC to remove flicker
  425.     {
  426.         CMemDC MemDC(&dc);
  427.         OnDraw(&MemDC);
  428.     }
  429.     else                    // Draw raw - this helps in debugging vis problems.
  430.         OnDraw(&dc);
  431. }
  432. BOOL CGridCtrl::OnEraseBkgnd(CDC* /*pDC*/) 
  433. {
  434.     return TRUE;    // Don't erase the background.
  435. }
  436. // Custom background erasure. This gets called from within the OnDraw function,
  437. // since we will (most likely) be using a memory DC to stop flicker. If we just
  438. // erase the background normally through OnEraseBkgnd, and didn't fill the memDC's
  439. // selected bitmap with colour, then all sorts of vis problems would occur
  440. void CGridCtrl::EraseBkgnd(CDC* pDC) 
  441. {
  442.     CRect  VisRect, ClipRect, rect;
  443.     CBrush FixedBack(GetFixedBkColor()),
  444.            TextBack(GetTextBkColor()),
  445.            Back(GetBkColor());
  446.     if (pDC->GetClipBox(ClipRect) == ERROR) return;
  447.     GetVisibleNonFixedCellRange(VisRect);
  448.     // Draw Fixed columns background
  449.     int nFixedColumnWidth = GetFixedColumnWidth();
  450.     if (ClipRect.left < nFixedColumnWidth && ClipRect.top < VisRect.bottom)
  451.         pDC->FillRect(CRect(ClipRect.left, ClipRect.top, nFixedColumnWidth, VisRect.bottom),
  452.                       &FixedBack);
  453.         
  454.     // Draw Fixed rows background
  455.     int nFixedRowHeight = GetFixedRowHeight();
  456.     if (ClipRect.top < nFixedRowHeight && 
  457.         ClipRect.right > nFixedColumnWidth && ClipRect.left < VisRect.right)
  458.         pDC->FillRect(CRect(nFixedColumnWidth-1, ClipRect.top, VisRect.right, nFixedRowHeight),
  459.                       &FixedBack);
  460.     // Draw non-fixed cell background
  461.     if (rect.IntersectRect(VisRect, ClipRect)) 
  462.     {
  463.         CRect CellRect(max(nFixedColumnWidth, rect.left), max(nFixedRowHeight, rect.top),
  464.                        rect.right, rect.bottom);
  465.         pDC->FillRect(CellRect, &TextBack);
  466.     }
  467.     // Draw right hand side of window outside grid
  468.     if (VisRect.right < ClipRect.right) 
  469.         pDC->FillRect(CRect(VisRect.right, ClipRect.top, ClipRect.right, ClipRect.bottom),
  470.                       &Back);
  471.     // Draw bottom of window below grid
  472.     if (VisRect.bottom < ClipRect.bottom && ClipRect.left < VisRect.right) 
  473.         pDC->FillRect(CRect(ClipRect.left, VisRect.bottom, VisRect.right, ClipRect.bottom),
  474.                       &Back);
  475. }
  476. void CGridCtrl::OnSize(UINT nType, int cx, int cy) 
  477. {
  478. //    if (::IsWindow(GetSafeHwnd()) && GetFocus()->GetSafeHwnd() != GetSafeHwnd()) 
  479.         SetFocus();        // Auto-destroy any InPlaceEdit's
  480.     CWnd::OnSize(nType, cx, cy);
  481.     ResetScrollBars();    
  482. }
  483. UINT CGridCtrl::OnGetDlgCode() 
  484. {
  485.     UINT nCode = DLGC_WANTARROWS | DLGC_WANTCHARS;
  486.     if (m_bHandleTabKey && !IsCTRLpressed()) 
  487.         nCode |= DLGC_WANTTAB;
  488.     return nCode;
  489. }
  490. // If system colours change, then redo colours
  491. void CGridCtrl::OnSysColorChange() 
  492. {
  493.     CWnd::OnSysColorChange();
  494.     
  495.     if (GetTextColor() == m_crWindowText)                   // Still using system colours
  496.         SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  497.     if (GetTextBkColor() == m_crWindowColour)
  498.         SetTextBkColor(::GetSysColor(COLOR_WINDOW));
  499.     if (GetBkColor() == m_crShadow)
  500.         SetBkColor(::GetSysColor(COLOR_3DSHADOW));
  501.     if (GetFixedTextColor() == m_crWindowText)
  502.         SetFixedTextColor(::GetSysColor(COLOR_WINDOWTEXT));
  503.     if (GetFixedBkColor() == m_cr3DFace)
  504.         SetFixedBkColor(::GetSysColor(COLOR_3DFACE));
  505.     m_crWindowText   = ::GetSysColor(COLOR_WINDOWTEXT);
  506.     m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
  507.     m_cr3DFace       = ::GetSysColor(COLOR_3DFACE);
  508.     m_crShadow       = ::GetSysColor(COLOR_3DSHADOW);
  509. }
  510. // If we are drag-selecting cells, or drag and dropping, stop now
  511. void CGridCtrl::OnCaptureChanged(CWnd *pWnd) 
  512. {
  513.     if (pWnd->GetSafeHwnd() == GetSafeHwnd()) return;
  514.     // kill timer if active
  515.     if (m_nTimerID != 0)
  516.     {
  517.         KillTimer(m_nTimerID);
  518.         m_nTimerID = 0;
  519.     }
  520.     // Kill drag and drop if active
  521.     if (m_MouseMode == MOUSE_DRAGGING)
  522.         m_MouseMode = MOUSE_NOTHING;
  523. }
  524. #if _MFC_VER >= 0x0421
  525. // If system settings change, then redo colours
  526. void CGridCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection) 
  527. {
  528.     CWnd::OnSettingChange(uFlags, lpszSection);
  529.     
  530.     if (GetTextColor() == m_crWindowText)                   // Still using system colours
  531.         SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  532.     if (GetTextBkColor() == m_crWindowColour)
  533.         SetTextBkColor(::GetSysColor(COLOR_WINDOW));
  534.     if (GetBkColor() == m_crShadow)
  535.         SetBkColor(::GetSysColor(COLOR_3DSHADOW));
  536.     if (GetFixedTextColor() == m_crWindowText)
  537.         SetFixedTextColor(::GetSysColor(COLOR_WINDOWTEXT));
  538.     if (GetFixedBkColor() == m_cr3DFace)
  539.         SetFixedBkColor(::GetSysColor(COLOR_3DFACE));
  540.     m_crWindowText   = ::GetSysColor(COLOR_WINDOWTEXT);
  541.     m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
  542.     m_cr3DFace       = ::GetSysColor(COLOR_3DFACE);
  543.     m_crShadow       = ::GetSysColor(COLOR_3DSHADOW);
  544.     m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
  545. }
  546. #endif
  547. // For drag-selection. Scrolls hidden cells into view
  548. // TODO: decrease timer interval over time to speed up selection over time
  549. void CGridCtrl::OnTimer(UINT nIDEvent)
  550. {
  551.     ASSERT(nIDEvent == WM_LBUTTONDOWN);
  552.     if (nIDEvent != WM_LBUTTONDOWN) return;
  553.     CPoint pt, origPt;
  554.     if (!GetCursorPos(&origPt)) return;
  555.     ScreenToClient(&origPt);
  556.     CRect rect;
  557.     GetClientRect(rect);
  558.     int nFixedRowHeight = GetFixedRowHeight();
  559.     int nFixedColWidth = GetFixedColumnWidth();
  560.     pt = origPt;
  561.     if (pt.y > rect.bottom)
  562.     {
  563.         //SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  564.         SendMessage(WM_KEYDOWN, VK_DOWN, 0);
  565.         if (pt.x < rect.left)  pt.x = rect.left;
  566.         if (pt.x > rect.right) pt.x = rect.right;
  567.         pt.y = rect.bottom;
  568.         OnSelecting(GetCellFromPt(pt));
  569.     }
  570.     else if (pt.y < nFixedRowHeight)
  571.     {
  572.         //SendMessage(WM_VSCROLL, SB_LINEUP, 0);
  573.         SendMessage(WM_KEYDOWN, VK_UP, 0);
  574.         if (pt.x < rect.left)  pt.x = rect.left;
  575.         if (pt.x > rect.right) pt.x = rect.right;
  576.         pt.y = nFixedRowHeight + 1;
  577.         OnSelecting(GetCellFromPt(pt));
  578.     }
  579.     pt = origPt;
  580.     if (pt.x > rect.right)
  581.     {
  582.         // SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
  583.         SendMessage(WM_KEYDOWN, VK_RIGHT, 0);
  584.         if (pt.y < rect.top)    pt.y = rect.top;
  585.         if (pt.y > rect.bottom) pt.y = rect.bottom;
  586.         pt.x = rect.right;
  587.         OnSelecting(GetCellFromPt(pt));
  588.     } 
  589.     else if (pt.x < nFixedColWidth)
  590.     {
  591.         //SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
  592.         SendMessage(WM_KEYDOWN, VK_LEFT, 0);
  593.         if (pt.y < rect.top)    pt.y = rect.top;
  594.         if (pt.y > rect.bottom) pt.y = rect.bottom;
  595.         pt.x = nFixedColWidth + 1;
  596.         OnSelecting(GetCellFromPt(pt));
  597.     }
  598. }
  599. // move about with keyboard
  600. void CGridCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
  601. {
  602.     if (!IsValid(m_idCurrentCell)) 
  603.     {
  604.         CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  605.         return;
  606.     }
  607.     CCellID next = m_idCurrentCell;
  608.     BOOL bChangeLine = FALSE;
  609.     if (IsCTRLpressed())
  610.     {
  611.         switch (nChar)
  612.         {
  613.            case 'A': OnEditSelectAll();  break;
  614.            case 'X': OnEditCut();        break;
  615.            case 'C': OnEditCopy();       break;
  616.            case 'V': OnEditPaste();      break;
  617.         }
  618.     }
  619.     switch (nChar)
  620.     {
  621.         case VK_DELETE: 
  622.             if (IsCellEditable(m_idCurrentCell.row, m_idCurrentCell.col))
  623.             {
  624.                 SetItemText(m_idCurrentCell.row, m_idCurrentCell.col, _T(""));
  625.                 RedrawCell(m_idCurrentCell);
  626.                 SetModified(TRUE);
  627.             }
  628.             break;
  629.         case VK_TAB:    
  630.             if (IsSHIFTpressed())
  631.             {
  632.                 if (next.col > m_nFixedCols) 
  633.                     next.col--;
  634.                 else if (next.col == m_nFixedCols && next.row > m_nFixedRows) 
  635.                 {
  636.                     next.row--; 
  637.                     next.col = GetColumnCount() - 1; 
  638.                     bChangeLine = TRUE;
  639.                 }
  640.                 else
  641.                     CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  642.             }
  643.             else
  644.             {
  645.                 if (next.col < (GetColumnCount() - 1)) 
  646.                     next.col++;
  647.                 else if (next.col == (GetColumnCount() - 1) && 
  648.                          next.row < (GetRowCount() - 1) )
  649.                 {
  650.                     next.row++; 
  651.                     next.col = m_nFixedCols; 
  652.                     bChangeLine = TRUE;
  653.                 }
  654.                 else
  655.                     CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  656.             } 
  657.             break;
  658.         case VK_DOWN:   
  659.             if (next.row < (GetRowCount() - 1))
  660.                 next.row++; 
  661.             break;            
  662.             
  663.         case VK_UP:     
  664.             if (next.row > m_nFixedRows)           
  665.                 next.row--; 
  666.             break;
  667.         case VK_RIGHT:  
  668.             if (next.col < (GetColumnCount() - 1)) 
  669.                 next.col++; 
  670.             break;
  671.             
  672.         case VK_LEFT:   
  673.             if (next.col > m_nFixedCols)           
  674.                 next.col--; 
  675.             break;
  676.         case VK_NEXT:   
  677.             {
  678.             CCellID idOldTopLeft = GetTopleftNonFixedCell();
  679.             SendMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
  680.             CCellID idNewTopLeft = GetTopleftNonFixedCell();
  681.             int increment = idNewTopLeft.row - idOldTopLeft.row;
  682.             if (increment) {
  683.                 next.row += increment;
  684.                 if (next.row > (GetRowCount() - 1)) 
  685.                     next.row = GetRowCount() - 1;
  686.             }
  687.             else
  688.                 next.row = GetRowCount() - 1;
  689.                 break;
  690.             }
  691.     
  692.             case VK_PRIOR:  
  693.             {
  694.             CCellID idOldTopLeft = GetTopleftNonFixedCell();
  695.             SendMessage(WM_VSCROLL, SB_PAGEUP, 0);
  696.             CCellID idNewTopLeft = GetTopleftNonFixedCell();
  697.             int increment = idNewTopLeft.row - idOldTopLeft.row;
  698.             if (increment) 
  699.             {
  700.                 next.row += increment;
  701.                 if (next.row < m_nFixedRows) 
  702.                     next.row = m_nFixedRows;
  703.             } else
  704.                 next.row = m_nFixedRows;
  705.                 break;
  706.             }
  707.     
  708.         case VK_HOME:   
  709.             SendMessage(WM_VSCROLL, SB_TOP, 0);
  710.             next.row = m_nFixedRows;
  711.             break;
  712.         
  713.         case VK_END:    
  714.             SendMessage(WM_VSCROLL, SB_BOTTOM, 0);
  715.             next.row = GetRowCount() - 1;
  716.             break;
  717.                 
  718.         default:
  719.             CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  720.     }
  721.   
  722.     if (next != m_idCurrentCell) 
  723.     {
  724.         // While moving with the Cursorkeys the current ROW/CELL will get selected
  725.         // OR Selection will get expanded when SHIFT is pressed
  726.         // Cut n paste from OnLButtonDown - Franco Bez 
  727.         // Added check for NULL mouse mode - Chris Maunder.
  728.         if (m_MouseMode == MOUSE_NOTHING)
  729.         {
  730.             m_PrevSelectedCellMap.RemoveAll();
  731.             m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
  732.             if (!IsSHIFTpressed() || nChar == VK_TAB)
  733.                 m_SelectionStartCell = next;
  734.             OnSelecting(next);
  735.             m_MouseMode = MOUSE_NOTHING;
  736.         }
  737.         SetFocusCell(next);
  738.         if (!IsCellVisible(next))
  739.         {   
  740.             EnsureVisible(next); // Make sure cell is visible
  741.             switch (nChar) {
  742.                 case VK_RIGHT:  
  743.                     SendMessage(WM_HSCROLL, SB_LINERIGHT, 0); 
  744.                     break;
  745.                 case VK_LEFT:   
  746.                     SendMessage(WM_HSCROLL, SB_LINELEFT, 0);  
  747.                     break;
  748.                 case VK_DOWN:   
  749.                     SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);  
  750.                     break;
  751.                 
  752.                 case VK_UP:     
  753.                     SendMessage(WM_VSCROLL, SB_LINEUP, 0);    
  754.                     break;                
  755.                 
  756.                 case VK_TAB:    
  757.                     if (IsSHIFTpressed())
  758.                     {
  759.                         if (bChangeLine) 
  760.                         {
  761.                             SendMessage(WM_VSCROLL, SB_LINEUP, 0);
  762.                             SetScrollPos32(SB_HORZ, m_nHScrollMax);
  763.                             break;
  764.                         }
  765.                         else 
  766.                             SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
  767.                     }
  768.                     else
  769.                     {
  770.                         if (bChangeLine) 
  771.                         {
  772.                             SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  773.                             SetScrollPos32(SB_HORZ, 0);
  774.                             break;
  775.                         }
  776.                         else 
  777.                             SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
  778.                     }
  779.                     break;
  780.             }
  781.             Invalidate();
  782.         }
  783.     }
  784. }
  785. // Instant editing of cells when keys are pressed
  786. void CGridCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
  787. {
  788.     if (!IsCTRLpressed() && m_MouseMode == MOUSE_NOTHING)
  789.     {
  790.         if (!m_bHandleTabKey || (m_bHandleTabKey && nChar != VK_TAB))
  791.             OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, nChar);
  792.     }
  793.     CWnd::OnChar(nChar, nRepCnt, nFlags);
  794. }
  795. // Callback from any CInPlaceEdits that ended. This just calls OnEndEditCell,
  796. // refreshes the edited cell and moves onto next cell if the return character
  797. // from the edit says we should.
  798. void CGridCtrl::OnEndInPlaceEdit(NMHDR* pNMHDR, LRESULT* pResult) 
  799. {    
  800.     GV_DISPINFO *pgvDispInfo = (GV_DISPINFO *)pNMHDR;
  801.     GV_ITEM     *pgvItem = &pgvDispInfo->item;
  802.     // In case OnEndInPlaceEdit called as window is being destroyed
  803.     if (!IsWindow(GetSafeHwnd()))
  804.         return;
  805.     // Only set as modified if (a) it actually was, and (b) ESC was not hit.
  806.     if (pgvItem->lParam != VK_ESCAPE &&
  807.         GetItemText(pgvItem->row, pgvItem->col) != pgvItem->szText)
  808.     {
  809.         SetModified(TRUE);
  810.     }
  811.     OnEndEditCell(pgvItem->row, pgvItem->col, pgvItem->szText);
  812.     InvalidateCellRect(CCellID(pgvItem->row, pgvItem->col));
  813.     SendMessageToParent(pgvItem->row, pgvItem->col, GVN_ENDLABELEDIT);
  814.     switch (pgvItem->lParam) 
  815.     {
  816.         case VK_DOWN: 
  817.         case VK_UP:   
  818.         case VK_RIGHT:
  819.         case VK_LEFT:  
  820.         case VK_NEXT:  
  821.         case VK_PRIOR: 
  822.         case VK_HOME:  
  823.         case VK_END:    OnKeyDown(pgvItem->lParam, 0, 0);
  824.                         OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, pgvItem->lParam);
  825.     }
  826.     *pResult = 0;
  827. }
  828. // Handle horz scrollbar notifications
  829. void CGridCtrl::OnHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
  830. {
  831.     if (GetFocus()->GetSafeHwnd() != GetSafeHwnd()) 
  832.         SetFocus();  // Auto-destroy any InPlaceEdit's
  833. #ifdef GRIDCONTROL_USE_TITLETIPS
  834.     m_TitleTip.Hide();  // hide any titletips
  835. #endif
  836.     int scrollPos = GetScrollPos32(SB_HORZ);
  837.     CCellID idTopLeft = GetTopleftNonFixedCell();
  838.     CRect rect;
  839.     GetClientRect(rect);
  840.     switch (nSBCode)
  841.     {
  842.         case SB_LINERIGHT:
  843.             if (scrollPos < m_nHScrollMax)
  844.             {
  845.                 int xScroll = GetColumnWidth(idTopLeft.col);
  846.                 SetScrollPos32(SB_HORZ, scrollPos + xScroll);
  847.                 if (GetScrollPos32(SB_HORZ) == scrollPos) break;          // didn't work
  848.                 rect.left = GetFixedColumnWidth() + xScroll;
  849.                 ScrollWindow(-xScroll, 0, rect);
  850.                 rect.left = rect.right - xScroll;
  851.                 InvalidateRect(rect);
  852.             }
  853.             break;
  854.         case SB_LINELEFT:
  855.             if (scrollPos > 0 && idTopLeft.col > GetFixedColumnCount())
  856.             {
  857.                 int xScroll = GetColumnWidth(idTopLeft.col-1);
  858.                 SetScrollPos32(SB_HORZ, max(0,scrollPos - xScroll));
  859.                 rect.left = GetFixedColumnWidth();
  860.                 ScrollWindow(xScroll, 0, rect);
  861.                 rect.right = rect.left + xScroll;
  862.                 InvalidateRect(rect);
  863.             }
  864.             break;
  865.         case SB_PAGERIGHT:
  866.             if (scrollPos < m_nHScrollMax)
  867.             {
  868.                 rect.left = GetFixedColumnWidth();
  869.                 int offset = rect.Width();
  870.                 int pos = min(m_nHScrollMax, scrollPos + offset);
  871.                 SetScrollPos32(SB_HORZ, pos);
  872.                 rect.left = GetFixedColumnWidth();
  873.                 InvalidateRect(rect);
  874.             }
  875.             break;
  876.         case SB_PAGELEFT:
  877.             if (scrollPos > 0)
  878.             {
  879.                 rect.left = GetFixedColumnWidth();
  880.                 int offset = -rect.Width();
  881.                 int pos = max(0, scrollPos + offset);
  882.                 SetScrollPos32(SB_HORZ, pos);
  883.                 rect.left = GetFixedColumnWidth();
  884.                 InvalidateRect(rect);
  885.             }
  886.             break;
  887.         case SB_THUMBPOSITION:
  888.         case SB_THUMBTRACK:
  889.             {
  890.                 SetScrollPos32(SB_HORZ, GetScrollPos32(SB_HORZ, TRUE));
  891.                 rect.left = GetFixedColumnWidth();
  892.                 InvalidateRect(rect);
  893.             }
  894.             break;
  895.         case SB_LEFT:
  896.             if (scrollPos > 0)
  897.             {
  898.                 SetScrollPos32(SB_HORZ, 0);
  899.                 Invalidate();
  900.             }
  901.             break;
  902.         case SB_RIGHT:
  903.             if (scrollPos < m_nHScrollMax)
  904.             {
  905.                 SetScrollPos32(SB_HORZ, m_nHScrollMax);
  906.                 Invalidate();
  907.             }
  908.             break;
  909.         default: break;
  910.     }
  911. }
  912. // Handle vert scrollbar notifications
  913. void CGridCtrl::OnVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
  914. {
  915.     if (GetFocus()->GetSafeHwnd() != GetSafeHwnd()) 
  916.         SetFocus();        // Auto-destroy any InPlaceEdit's
  917. #ifdef GRIDCONTROL_USE_TITLETIPS
  918.     m_TitleTip.Hide();  // hide any titletips
  919. #endif
  920.     // Get the scroll position ourselves to ensure we get a 32 bit value
  921.     int scrollPos = GetScrollPos32(SB_VERT);
  922.     CCellID idTopLeft = GetTopleftNonFixedCell();
  923.     CRect rect;
  924.     GetClientRect(rect);
  925.     switch (nSBCode)
  926.     {
  927.         case SB_LINEDOWN:
  928.             if (scrollPos < m_nVScrollMax)
  929.             {
  930.                 int yScroll = GetRowHeight(idTopLeft.row);
  931.                 SetScrollPos32(SB_VERT, scrollPos + yScroll);
  932.                 if (GetScrollPos32(SB_VERT) == scrollPos) break;          // didn't work
  933.                 rect.top = GetFixedRowHeight() + yScroll;
  934.                 ScrollWindow( 0, -yScroll, rect);
  935.                 rect.top = rect.bottom - yScroll;
  936.                 InvalidateRect(rect);
  937.             }
  938.             break;
  939.         case SB_LINEUP:
  940.             if (scrollPos > 0 && idTopLeft.row > GetFixedRowCount())
  941.             {
  942.                 int yScroll = GetRowHeight(idTopLeft.row-1);
  943.                 SetScrollPos32(SB_VERT, max(0, scrollPos - yScroll));
  944.                 rect.top = GetFixedRowHeight();
  945.                 ScrollWindow(0, yScroll, rect);
  946.                 rect.bottom = rect.top + yScroll;
  947.                 InvalidateRect(rect);
  948.             }
  949.             break;
  950.         case SB_PAGEDOWN:
  951.             if (scrollPos < m_nVScrollMax)
  952.             {
  953.                 rect.top = GetFixedRowHeight();
  954.                 scrollPos = min(m_nVScrollMax, scrollPos + rect.Height());
  955.                 SetScrollPos32(SB_VERT, scrollPos);
  956.                 rect.top = GetFixedRowHeight();
  957.                 InvalidateRect(rect);
  958.             }
  959.             break;
  960.         case SB_PAGEUP:
  961.             if (scrollPos > 0)
  962.             {
  963.                 rect.top = GetFixedRowHeight();
  964.                 int offset = -rect.Height();
  965.                 int pos = max(0, scrollPos + offset);
  966.                 SetScrollPos32(SB_VERT, pos);
  967.                 rect.top = GetFixedRowHeight();
  968.                 InvalidateRect(rect);
  969.             }
  970.             break;
  971.         case SB_THUMBPOSITION:
  972.         case SB_THUMBTRACK:
  973.             {
  974.                 SetScrollPos32(SB_VERT, GetScrollPos32(SB_VERT, TRUE));
  975.                 rect.top = GetFixedRowHeight();
  976.                 InvalidateRect(rect);
  977.             }
  978.             break;
  979.         case SB_TOP:
  980.             if (scrollPos > 0)
  981.             {
  982.                 SetScrollPos32(SB_VERT, 0);
  983.                 Invalidate();
  984.             }
  985.             break;
  986.         case SB_BOTTOM:
  987.             if (scrollPos < m_nVScrollMax)
  988.             {
  989.                 SetScrollPos32(SB_VERT, m_nVScrollMax);
  990.                 Invalidate();
  991.             }
  992.         default: break;
  993.     }
  994. }
  995. /////////////////////////////////////////////////////////////////////////////
  996. // CGridCtrl implementation functions
  997. void CGridCtrl::OnDraw(CDC* pDC)
  998. {
  999.     CRect rect;
  1000.     int row,col;
  1001.     CRect clipRect;
  1002.     if (pDC->GetClipBox(&clipRect) == ERROR) return;
  1003.     EraseBkgnd(pDC);            // OnEraseBkgnd does nothing, so erase bkgnd here.
  1004.                                 // This necessary since we may be using a Memory DC.
  1005.     int nFixedRowHeight = GetFixedRowHeight();
  1006.     int nFixedColWidth  = GetFixedColumnWidth();
  1007.     CCellID idTopLeft = GetTopleftNonFixedCell();
  1008.     int minVisibleRow = idTopLeft.row,
  1009.         minVisibleCol = idTopLeft.col;
  1010.     CRect VisRect;
  1011.     CCellRange VisCellRange = GetVisibleNonFixedCellRange(VisRect);
  1012.     int maxVisibleRow = VisCellRange.GetMaxRow(),
  1013.         maxVisibleCol = VisCellRange.GetMaxCol();
  1014.     // draw top-left cells 0..m_nFixedRows-1, 0..m_nFixedCols-1
  1015.     rect.bottom = -1;
  1016.     for (row = 0; row < m_nFixedRows; row++)
  1017.     {
  1018.         rect.top = rect.bottom+1;
  1019.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1020.         rect.right = -1;
  1021.         for (col = 0; col < m_nFixedCols; col++)
  1022.         {
  1023.             rect.left = rect.right+1;
  1024.             rect.right = rect.left + GetColumnWidth(col)-1;
  1025.             DrawFixedCell(pDC, row, col, rect);
  1026.         }
  1027.     }
  1028.      
  1029.     // draw fixed column cells:  m_nFixedRows..n, 0..m_nFixedCols-1
  1030.     rect.bottom = nFixedRowHeight-1;
  1031.     for (row = minVisibleRow; row <= maxVisibleRow; row++)
  1032.     {
  1033.         rect.top = rect.bottom+1;
  1034.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1035.         // rect.bottom = bottom pixel of previous row
  1036.         if (rect.top > clipRect.bottom) break;                // Gone past cliprect
  1037.         if (rect.bottom < clipRect.top) continue;             // Reached cliprect yet?
  1038.         rect.right = -1;
  1039.         for (col = 0; col < m_nFixedCols; col++)
  1040.         {
  1041.             rect.left = rect.right+1;
  1042.             rect.right = rect.left + GetColumnWidth(col)-1;
  1043.             if (rect.left > clipRect.right) break;            // gone past cliprect
  1044.             if (rect.right < clipRect.left) continue;         // Reached cliprect yet?
  1045.             DrawFixedCell(pDC, row, col, rect);
  1046.         }
  1047.     }
  1048.     
  1049.     // draw fixed row cells  0..m_nFixedRows, m_nFixedCols..n
  1050.     rect.bottom = -1;
  1051.     for (row = 0; row < m_nFixedRows; row++)
  1052.     {
  1053.         rect.top = rect.bottom+1;
  1054.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1055.         // rect.bottom = bottom pixel of previous row
  1056.         if (rect.top > clipRect.bottom) break;                // Gone past cliprect
  1057.         if (rect.bottom < clipRect.top) continue;             // Reached cliprect yet?
  1058.         rect.right = nFixedColWidth-1;
  1059.         for (col = minVisibleCol; col <= maxVisibleCol; col++)
  1060.         {                                       
  1061.             rect.left = rect.right+1;
  1062.             rect.right = rect.left + GetColumnWidth(col)-1;
  1063.             if (rect.left > clipRect.right) break;        // gone past cliprect
  1064.             if (rect.right < clipRect.left) continue;     // Reached cliprect yet?
  1065.             DrawFixedCell(pDC, row, col, rect);
  1066.         }
  1067.     }
  1068.     
  1069.     // draw rest of non-fixed cells
  1070.     rect.bottom = nFixedRowHeight-1;
  1071.     for (row = minVisibleRow; row <= maxVisibleRow; row++)
  1072.     {
  1073.         rect.top = rect.bottom+1;
  1074.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1075.         // rect.bottom = bottom pixel of previous row
  1076.         if (rect.top > clipRect.bottom) break;                // Gone past cliprect
  1077.         if (rect.bottom < clipRect.top) continue;             // Reached cliprect yet?
  1078.         rect.right = nFixedColWidth-1;
  1079.         for (col = minVisibleCol; col <= maxVisibleCol; col++)
  1080.         {
  1081.             rect.left = rect.right+1;
  1082.             rect.right = rect.left + GetColumnWidth(col)-1;
  1083.             if (rect.left > clipRect.right) break;        // gone past cliprect
  1084.             if (rect.right < clipRect.left) continue;     // Reached cliprect yet?
  1085.             DrawCell(pDC, row, col, rect);
  1086.         }
  1087.     }
  1088.     CPen pen;
  1089.     try {
  1090.         pen.CreatePen(PS_SOLID, 0, m_crGridColour);
  1091.     }
  1092.     catch (CResourceException *e)
  1093.     {
  1094.         e->Delete();
  1095.         return;
  1096.     }
  1097.     pDC->SelectObject(&pen);
  1098.     // draw vertical lines (drawn at ends of cells)
  1099.     if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT) 
  1100.     {
  1101.         int x = nFixedColWidth;
  1102.         for (col = minVisibleCol; col <= maxVisibleCol; col++) {
  1103.             x += GetColumnWidth(col);
  1104.             pDC->MoveTo(x-1, nFixedRowHeight);
  1105.             pDC->LineTo(x-1, VisRect.bottom);   
  1106.         }
  1107.     }
  1108.     
  1109.     // draw horizontal lines (drawn at bottom of each cell)
  1110.     if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ) 
  1111.     {
  1112.         int y = nFixedRowHeight;
  1113.         for (row = minVisibleRow; row <= maxVisibleRow; row++) {
  1114.             y += GetRowHeight(row);
  1115.             pDC->MoveTo(nFixedColWidth, y-1);    
  1116.             pDC->LineTo(VisRect.right,  y-1);
  1117.         }
  1118.     }
  1119.     pDC->SelectStockObject(NULL_PEN);
  1120. #ifdef USE_MEMDC                        // Use a memDC for flicker free update
  1121. }
  1122. #else                                   // Use normal DC - this helps in debugging
  1123. }
  1124. #endif
  1125. ////////////////////////////////////////////////////////////////////////////////////////
  1126. // CGridCtrl Cell selection stuff
  1127. BOOL CGridCtrl::IsValid(int nRow, int nCol) const
  1128. {
  1129.     return (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols);
  1130. }
  1131. BOOL CGridCtrl::IsValid(const CCellID& cell) const
  1132. {
  1133.     return IsValid(cell.row, cell.col);
  1134. }
  1135. BOOL CGridCtrl::IsValid(const CCellRange& range) const
  1136. {
  1137.      return (range.GetMinRow() >= 0 && range.GetMinCol() >= 0 && 
  1138.             range.GetMaxRow() >= 0 && range.GetMaxCol() >= 0 &&
  1139.             range.GetMaxRow() < m_nRows && range.GetMaxCol() < m_nCols &&
  1140.              range.GetMinRow() <= range.GetMaxRow() && range.GetMinCol() <= range.GetMaxCol());
  1141. }
  1142. // Enables/Disables redraw for certain operations like columns auto-sizing etc,
  1143. // but not for user caused things such as selection changes.
  1144. void CGridCtrl::SetRedraw(BOOL bAllowDraw, BOOL bResetScrollBars /* = FALSE */)
  1145. {
  1146.     TRACE(_T("%s: Setting redraw to %sn"), 
  1147.              GetRuntimeClass()->m_lpszClassName, bAllowDraw? _T("TRUE") : _T("FALSE"));
  1148.     if (bAllowDraw && !m_bAllowDraw) Invalidate();
  1149.     m_bAllowDraw = bAllowDraw;
  1150.     if (bResetScrollBars) ResetScrollBars();
  1151. }
  1152. // Forces a redraw of a cell immediately (using a direct DC construction, 
  1153. // or the supplied dc)
  1154. BOOL CGridCtrl::RedrawCell(const CCellID& cell, CDC* pDC /* = NULL */)
  1155. {
  1156.     return RedrawCell(cell.row, cell.col, pDC);
  1157. }
  1158. BOOL CGridCtrl::RedrawCell(int nRow, int nCol, CDC* pDC /* = NULL */)
  1159. {
  1160.     BOOL bResult = TRUE;
  1161.     BOOL bMustReleaseDC = FALSE;
  1162.     if (!m_bAllowDraw || !IsCellVisible(nRow, nCol)) return FALSE;
  1163.     CRect rect;
  1164.     if (!GetCellRect(nRow, nCol, rect)) return FALSE;
  1165.     if (!pDC) {
  1166.         pDC = GetDC();
  1167.         if (pDC) bMustReleaseDC = TRUE;
  1168.     }
  1169.     if (pDC)
  1170.     {
  1171.         // Redraw cells directly
  1172.         if (nRow < m_nFixedRows || nCol < m_nFixedCols)
  1173.             bResult = DrawFixedCell(pDC, nRow, nCol, rect, TRUE);
  1174.         else
  1175.             bResult = DrawCell(pDC, nRow, nCol, rect, TRUE);
  1176.         // Since we have erased the background, we will need to redraw the gridlines
  1177.         CPen pen;
  1178.         try {
  1179.             pen.CreatePen(PS_SOLID, 0, m_crGridColour);
  1180.         } catch (...) {}
  1181.         
  1182.         CPen* pOldPen = (CPen*) pDC->SelectObject(&pen);
  1183.         if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ) 
  1184.         {
  1185.             pDC->MoveTo(rect.left,    rect.bottom);
  1186.             pDC->LineTo(rect.right+1, rect.bottom);
  1187.         }
  1188.         if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT) 
  1189.         {
  1190.             pDC->MoveTo(rect.right, rect.top);
  1191.             pDC->LineTo(rect.right, rect.bottom+1);    
  1192.         }
  1193.         pDC->SelectObject(pOldPen);
  1194.     } else
  1195.         InvalidateRect(rect, TRUE);     // Could not get a DC - invalidate it anyway
  1196.                                         // and hope that OnPaint manages to get one
  1197.     if (bMustReleaseDC) 
  1198.         ReleaseDC(pDC);
  1199.     return bResult;
  1200. }
  1201. // redraw a complete row
  1202. BOOL CGridCtrl::RedrawRow(int row)
  1203. {
  1204.     BOOL bResult = TRUE;
  1205.     CDC* pDC = GetDC();
  1206.     for (int col = 0; col < GetColumnCount(); col++)
  1207.         bResult = RedrawCell(row, col, pDC) && bResult;
  1208.     if (pDC) ReleaseDC(pDC);
  1209.     return bResult;
  1210. }
  1211. // redraw a complete column
  1212. BOOL CGridCtrl::RedrawColumn(int col)
  1213. {
  1214.     BOOL bResult = TRUE;
  1215.     CDC* pDC = GetDC();
  1216.     for (int row = 0; row < GetRowCount(); row++)
  1217.         bResult = RedrawCell(row, col, pDC) && bResult;
  1218.     if (pDC) ReleaseDC(pDC);
  1219.     return bResult;
  1220. }
  1221. // Sets the currently selected cell, returning the previous current cell
  1222. CCellID CGridCtrl::SetFocusCell(int nRow, int nCol)
  1223. {
  1224.     return SetFocusCell(CCellID(nRow, nCol));
  1225. }
  1226. CCellID CGridCtrl::SetFocusCell(CCellID cell)
  1227. {
  1228.     if (cell == m_idCurrentCell) 
  1229.         return m_idCurrentCell;
  1230.     CCellID idPrev = m_idCurrentCell;
  1231.     m_idCurrentCell = cell;
  1232.     if (IsValid(idPrev)) 
  1233.     {
  1234.         SendMessageToParent(idPrev.row, idPrev.col, GVN_SELCHANGING); 
  1235.         SetItemState(idPrev.row, idPrev.col, 
  1236.                      GetItemState(idPrev.row, idPrev.col) & ~GVIS_FOCUSED);
  1237.         RedrawCell(idPrev);
  1238.         if (idPrev.col != m_idCurrentCell.col)
  1239.             for (int row = 0; row < m_nFixedRows; row++)
  1240.                 RedrawCell(row, idPrev.col);
  1241.         if (idPrev.row != m_idCurrentCell.row)
  1242.             for (int col = 0; col < m_nFixedCols; col++) 
  1243.                 RedrawCell(idPrev.row, col);
  1244.     }
  1245.     if (IsValid(m_idCurrentCell)) {
  1246.         SetItemState(m_idCurrentCell.row, m_idCurrentCell.col, 
  1247.                      GetItemState(m_idCurrentCell.row, m_idCurrentCell.col) | GVIS_FOCUSED);
  1248.         RedrawCell(m_idCurrentCell);
  1249.         if (idPrev.col != m_idCurrentCell.col)
  1250.             for (int row = 0; row < m_nFixedRows; row++) 
  1251.                 RedrawCell(row, m_idCurrentCell.col);
  1252.         if (idPrev.row != m_idCurrentCell.row)
  1253.             for (int col = 0; col < m_nFixedCols; col++) 
  1254.                 RedrawCell(m_idCurrentCell.row, col);
  1255.         SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_SELCHANGED); 
  1256.     }
  1257.     return idPrev;
  1258. }
  1259. // Sets the range of currently selected cells
  1260. void CGridCtrl::SetSelectedRange(const CCellRange& Range, 
  1261.                                  BOOL bForceRepaint /* = FALSE */)
  1262. {
  1263.     SetSelectedRange(Range.GetMinRow(), Range.GetMinCol(), Range.GetMaxRow(), Range.GetMaxCol(),
  1264.                      bForceRepaint);
  1265. }
  1266. void CGridCtrl::SetSelectedRange(int nMinRow, int nMinCol, int nMaxRow, int nMaxCol,
  1267.                                  BOOL bForceRepaint /* = FALSE */)
  1268. {
  1269.     if (!m_bEnableSelection) return;
  1270.     CDC* pDC = NULL;
  1271.     if (bForceRepaint) pDC = GetDC();
  1272.     // Unselect all previously selected cells
  1273.     for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  1274.     {
  1275.         DWORD key;
  1276.         CCellID cell;
  1277.         m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1278.         // Reset the selection flag on the cell
  1279.         if (IsValid(cell)) {
  1280.             SetItemState(cell.row, cell.col, 
  1281.                          GetItemState(cell.row, cell.col) & ~GVIS_SELECTED);
  1282.             // If this is to be reselected, continue on past the redraw
  1283.             if (nMinRow <= cell.row && cell.row <= nMaxRow &&
  1284.                 nMinCol <= cell.col && cell.col <= nMaxCol)
  1285.                     continue;
  1286.             if (bForceRepaint && pDC)                    // Redraw NOW
  1287.                 RedrawCell(cell.row, cell.col, pDC);
  1288.             else
  1289.                 InvalidateCellRect(cell);                // Redraw at leisure
  1290.         }
  1291.     }
  1292.     
  1293.     // if any previous selected cells are to be retained (eg Ctrl is being held down)
  1294.     // then copy them to the newly created list, and mark all these cells as
  1295.     // selected
  1296.     for (pos = m_PrevSelectedCellMap.GetStartPosition(); pos != NULL; )
  1297.     {
  1298.         DWORD key;
  1299.         CCellID cell;
  1300.         m_PrevSelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1301.         if (!IsValid(cell)) continue;
  1302.         int nState = GetItemState(cell.row, cell.col);
  1303.         // Set state as Selected. This will add the cell to m_SelectedCells[]
  1304.         SetItemState(cell.row, cell.col, nState | GVIS_SELECTED);
  1305.         // Redraw (immediately or at leisure)
  1306.         if (bForceRepaint && pDC)
  1307.             RedrawCell(cell.row, cell.col, pDC);
  1308.         else
  1309.             InvalidateCellRect(cell);
  1310.     }
  1311.     // Now select all cells in the cell range specified. If the cell has already
  1312.     // been marked as selected (above) then ignore it.
  1313.     if (nMinRow >= 0 && nMinCol >= 0 && nMaxRow >= 0 && nMaxCol >= 0 &&
  1314.         nMaxRow < m_nRows && nMaxCol < m_nCols &&
  1315.          nMinRow <= nMaxRow && nMinCol <= nMaxCol)
  1316.     {
  1317.         for (int row = nMinRow; row <= nMaxRow; row++)
  1318.             for (int col = nMinCol; col <= nMaxCol; col++) 
  1319.             {
  1320.                 int nState = GetItemState(row, col);
  1321.                 if (nState & GVIS_SELECTED) {
  1322.                     continue;    // Already selected - ignore
  1323.                 }
  1324.                 // Add to list of selected cells
  1325.                 CCellID cell(row, col);
  1326.                 // Set state as Selected. This will add the cell to m_SelectedCells[]
  1327.                 SetItemState(row, col, nState | GVIS_SELECTED);
  1328.                 // Redraw (immediately or at leisure)
  1329.                 if (bForceRepaint && pDC)
  1330.                     RedrawCell(row, col, pDC);
  1331.                 else
  1332.                     InvalidateCellRect(cell);
  1333.             }
  1334.     }
  1335. //    TRACE(_T("%d cells selected.n"), m_SelectedCellMap.GetCount());
  1336.     if (pDC != NULL) ReleaseDC(pDC);
  1337. // selects all cells
  1338. void CGridCtrl::SelectAllCells()
  1339. {
  1340.     if (!m_bEnableSelection) return;
  1341.     SetSelectedRange(m_nFixedRows, m_nFixedCols, GetRowCount()-1, GetColumnCount()-1);
  1342. }
  1343. // selects columns
  1344. void CGridCtrl::SelectColumns(CCellID currentCell)
  1345. {
  1346.     if (!m_bEnableSelection) return;
  1347.     //if (currentCell.col == m_idCurrentCell.col) return;
  1348.     if (currentCell.col < m_nFixedCols) return;
  1349.     if (!IsValid(currentCell)) return;
  1350.     SetSelectedRange(GetFixedRowCount(), min(m_SelectionStartCell.col, currentCell.col), 
  1351.                      GetRowCount()-1,    max(m_SelectionStartCell.col, currentCell.col)); 
  1352. }
  1353. // selects rows
  1354. void CGridCtrl::SelectRows(CCellID currentCell)
  1355. {  
  1356.     if (!m_bEnableSelection) return;
  1357.     //if (currentCell.row; == m_idCurrentCell.row) return;
  1358.     if (currentCell.row < m_nFixedRows) return;
  1359.     if (!IsValid(currentCell)) return;
  1360.     SetSelectedRange(min(m_SelectionStartCell.row, currentCell.row), GetFixedColumnCount(), 
  1361.                      max(m_SelectionStartCell.row, currentCell.row), GetColumnCount()-1); 
  1362. }
  1363. // selects cells
  1364. void CGridCtrl::SelectCells(CCellID currentCell)
  1365. {
  1366.     if (!m_bEnableSelection) return;
  1367.     int row = currentCell.row;
  1368.     int col = currentCell.col;
  1369.     if (row < m_nFixedRows || col < m_nFixedCols) return;
  1370.     if (!IsValid(currentCell)) return;
  1371.     // Prevent unnecessary redraws
  1372.     //if (currentCell == m_LeftClickDownCell)  return;
  1373.     //else if (currentCell == m_idCurrentCell) return;
  1374.     SetSelectedRange(min(m_SelectionStartCell.row, row), min(m_SelectionStartCell.col, col), 
  1375.                      max(m_SelectionStartCell.row, row), max(m_SelectionStartCell.col, col)); 
  1376. }
  1377. void CGridCtrl::OnSelecting(const CCellID& currentCell)
  1378. {
  1379.     if (!m_bEnableSelection) return;
  1380.     switch(m_MouseMode)
  1381.     {
  1382.         case MOUSE_SELECT_ALL:     SelectAllCells();               break;
  1383.         case MOUSE_SELECT_COL:     SelectColumns(currentCell);     break;
  1384.         case MOUSE_SELECT_ROW:     SelectRows(currentCell);        break;
  1385.         case MOUSE_SELECT_CELLS:   SelectCells(currentCell);       break;
  1386.     }
  1387. }
  1388. ////////////////////////////////////////////////////////////////////////////////////////
  1389. // Clipboard functions
  1390. void CGridCtrl::CutSelectedText()
  1391. {
  1392.     if (!IsEditable())
  1393.         return;
  1394.     // Clear contents of selected cells.
  1395.     for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  1396.     {
  1397.         DWORD key;
  1398.         CCellID cell;
  1399.         m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1400.         if (!IsCellEditable(cell))
  1401.             continue;
  1402.         CGridCell* pCell = GetCell(cell.row, cell.col);
  1403.         if (pCell)
  1404.         {
  1405.             EmptyCell(pCell, cell.row, cell.col);
  1406.             SetModified(TRUE);
  1407.         }
  1408.     }
  1409.     Invalidate();
  1410. }
  1411. COleDataSource* CGridCtrl::CopyTextFromGrid()
  1412. {
  1413.     USES_CONVERSION;
  1414.     CCellRange Selection = GetSelectedCellRange();
  1415.     if (!IsValid(Selection)) return NULL;
  1416.     // Write to shared file (REMEBER: CF_TEXT is ANSI, not UNICODE, so we need to convert)
  1417.     CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
  1418.     // Get a tab delimited string to copy to cache
  1419.     CString str;
  1420.     CGridCell *pCell;
  1421.     for (int row = Selection.GetMinRow(); row <= Selection.GetMaxRow(); row++) 
  1422.     {
  1423.         str.Empty();
  1424.         for (int col = Selection.GetMinCol(); col <= Selection.GetMaxCol(); col++)
  1425.         {
  1426.             pCell = GetCell(row,col);
  1427.             if (pCell && (pCell->state  & GVIS_SELECTED))
  1428.             {
  1429.                 if (pCell->szText.IsEmpty())
  1430.                     str += _T(" ");
  1431.                 else 
  1432.                    str += pCell->szText;
  1433.             }
  1434.             if (col != Selection.GetMaxCol()) 
  1435.                 str += _T("t");
  1436.         }
  1437.         if (row != Selection.GetMaxRow()) 
  1438.             str += _T("n");
  1439.         sf.Write(T2A(str.GetBuffer(1)), str.GetLength());
  1440.         str.ReleaseBuffer();
  1441.     }
  1442.     char c = '';
  1443.     sf.Write(&c, 1);
  1444.     DWORD dwLen = sf.GetLength();
  1445.     HGLOBAL hMem = sf.Detach();
  1446.     if (!hMem) 
  1447.         return NULL;
  1448.     hMem = ::GlobalReAlloc(hMem, dwLen, GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
  1449.     if (!hMem) 
  1450.         return NULL;
  1451.     // Cache data
  1452.     COleDataSource* pSource = new COleDataSource();
  1453.     pSource->CacheGlobalData(CF_TEXT, hMem);
  1454.     return pSource;
  1455. }
  1456. BOOL CGridCtrl::PasteTextToGrid(CCellID cell, COleDataObject* pDataObject)
  1457. {
  1458.     if (!IsValid(cell) || !IsCellEditable(cell) || !pDataObject->IsDataAvailable(CF_TEXT))
  1459.         return FALSE;
  1460.     // Get the text from the COleDataObject
  1461.     HGLOBAL hmem = pDataObject->GetGlobalData(CF_TEXT);
  1462.     CMemFile sf((BYTE*) ::GlobalLock(hmem), ::GlobalSize(hmem));
  1463.     // CF_TEXT is ANSI text, so we need to allocate a char* buffer
  1464.     // to hold this.
  1465.     LPSTR szBuffer = new char[::GlobalSize(hmem)];
  1466.     if (!szBuffer)
  1467.         return FALSE;
  1468.     sf.Read(szBuffer, ::GlobalSize(hmem));
  1469.     ::GlobalUnlock(hmem);
  1470.     // Now store in generic TCHAR form so we no longer have to deal with
  1471.     // ANSI/UNICODE problems
  1472.     CString strText = szBuffer;
  1473.     delete szBuffer;
  1474.     // Parse text data and set in cells...
  1475.     strText.LockBuffer();
  1476.     CString strLine = strText;
  1477.     int nLine = 0;
  1478.     // Find the end of the first line
  1479.     int nIndex;
  1480.     do {
  1481.         int nColumn = 0;
  1482.         nIndex = strLine.Find(_T("n"));
  1483.         // Store the remaining chars after the newline
  1484.         CString strNext = (nIndex < 0)? _T("")  : strLine.Mid(nIndex+1);
  1485.         // Remove all chars after the newline
  1486.         if (nIndex >= 0) 
  1487.             strLine = strLine.Left(nIndex);
  1488.         // Make blank entries a "space"
  1489.         if (strLine.IsEmpty() && nIndex >= 0)
  1490.             strLine = _T(" ");
  1491.         LPTSTR szLine = strLine.GetBuffer(1);
  1492.         // Break the current line into tokens (tab or comma delimited)
  1493.         LPTSTR pszCellText = _tcstok(szLine, _T("t,n"));
  1494.         while (pszCellText != NULL)
  1495.         {
  1496.             CCellID TargetCell(cell.row + nLine, cell.col + nColumn);
  1497.             if (IsValid(TargetCell))
  1498.             {
  1499.                 CString strCellText = pszCellText;
  1500.                 strCellText.TrimLeft();  strCellText.TrimRight();
  1501.                 SetItemText(TargetCell.row, TargetCell.col, strCellText);
  1502.                 SetModified(TRUE);
  1503.                 // Make sure cell is not selected to avoid data loss
  1504.                 SetItemState(TargetCell.row, TargetCell.col,
  1505.                              GetItemState(TargetCell.row, TargetCell.col) & ~GVIS_SELECTED);
  1506.             }
  1507.             pszCellText = _tcstok(NULL, _T("t,n"));
  1508.             nColumn++;
  1509.         }
  1510.         strLine.ReleaseBuffer();
  1511.         strLine = strNext;
  1512.         nLine++;
  1513.     } while (nIndex >= 0);
  1514.     strText.UnlockBuffer();
  1515.     Invalidate();
  1516.     return TRUE;
  1517. }
  1518. void CGridCtrl::OnBeginDrag()
  1519. {        
  1520.     if (!m_bAllowDragAndDrop) return;
  1521.     COleDataSource* pSource = CopyTextFromGrid();
  1522.     if (pSource) 
  1523.     {    
  1524.         SendMessageToParent(GetSelectedCellRange().GetTopLeft().row,
  1525.                             GetSelectedCellRange().GetTopLeft().col,
  1526.                             GVN_BEGINDRAG);
  1527.         m_MouseMode = MOUSE_DRAGGING;
  1528.         DROPEFFECT dropEffect = pSource->DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE);
  1529.         if (dropEffect & DROPEFFECT_MOVE)
  1530.             CutSelectedText();
  1531.         if (pSource) 
  1532.             delete pSource;    // Did not pass source to clipboard, so must delete
  1533.     }    
  1534. }
  1535. DROPEFFECT CGridCtrl::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, 
  1536.                                  CPoint point)
  1537. {
  1538.     // Any text data available for us?
  1539.     if (!m_bAllowDragAndDrop || !IsEditable() || !pDataObject->IsDataAvailable(CF_TEXT))
  1540.         return DROPEFFECT_NONE;
  1541.     // Find which cell we are over and drop-highlight it
  1542.     CCellID cell = GetCellFromPt(point, FALSE);
  1543.     // If not valid, set the previously drop-highlighted cell as no longer drop-highlighted
  1544.     if (!IsValid(cell))
  1545.     {
  1546.         OnDragLeave();
  1547.         m_LastDragOverCell = CCellID(-1,-1);
  1548.         return DROPEFFECT_NONE;
  1549.     }
  1550.     if (!IsCellEditable(cell))
  1551.         return DROPEFFECT_NONE;
  1552.     // Have we moved over a different cell than last time?
  1553.     if (cell != m_LastDragOverCell) 
  1554.     {
  1555.         // Set the previously drop-highlighted cell as no longer drop-highlighted
  1556.         if (IsValid(m_LastDragOverCell)) {
  1557.             UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  1558.             SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  1559.                          nState & ~GVIS_DROPHILITED);
  1560.             RedrawCell(m_LastDragOverCell);
  1561.         }
  1562.         m_LastDragOverCell = cell;
  1563.         // Set the new cell as drop-highlighted
  1564.         if (IsValid(m_LastDragOverCell)) {
  1565.             UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  1566.             SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  1567.                          nState | GVIS_DROPHILITED);
  1568.             RedrawCell(m_LastDragOverCell);
  1569.         }
  1570.     }
  1571.     // Return an appropraite value of DROPEFFECT so mouse cursor is set properly
  1572.     if (dwKeyState & MK_CONTROL)
  1573.         return DROPEFFECT_COPY;
  1574.     else
  1575.         return DROPEFFECT_MOVE;
  1576. }
  1577. DROPEFFECT CGridCtrl::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, 
  1578.                                   CPoint point)
  1579. {
  1580.     // Any text data available for us?
  1581.     if (!m_bAllowDragAndDrop || !pDataObject->IsDataAvailable(CF_TEXT))
  1582.         return DROPEFFECT_NONE;
  1583.     // Find which cell we are over and drop-highlight it
  1584.     m_LastDragOverCell = GetCellFromPt(point, FALSE);
  1585.     if (!IsValid(m_LastDragOverCell))
  1586.         return DROPEFFECT_NONE;
  1587.     if (!IsCellEditable(m_LastDragOverCell))
  1588.         return DROPEFFECT_NONE;
  1589.     if (IsValid(m_LastDragOverCell)) 
  1590.     {
  1591.         UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  1592.         SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  1593.                      nState | GVIS_DROPHILITED);
  1594.         RedrawCell(m_LastDragOverCell);
  1595.     }
  1596.     // Return an appropraite value of DROPEFFECT so mouse cursor is set properly
  1597.     if (dwKeyState & MK_CONTROL)
  1598.         return DROPEFFECT_COPY;
  1599.     else
  1600.         return DROPEFFECT_MOVE;
  1601. }
  1602. void CGridCtrl::OnDragLeave()
  1603. {
  1604.     // Set the previously drop-highlighted cell as no longer drop-highlighted
  1605.     if (IsValid(m_LastDragOverCell)) {
  1606.         UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  1607.         SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  1608.                      nState & ~GVIS_DROPHILITED);
  1609.         RedrawCell(m_LastDragOverCell);
  1610.     }
  1611. }
  1612. BOOL CGridCtrl::OnDrop(COleDataObject* pDataObject, DROPEFFECT /*dropEffect*/, 
  1613.                        CPoint /* point */) 
  1614. {
  1615.     BOOL bResult = FALSE;
  1616.     if (!m_bAllowDragAndDrop || !IsCellEditable(m_LastDragOverCell))
  1617.         return bResult;
  1618.     m_MouseMode = MOUSE_NOTHING;
  1619.     OnDragLeave();
  1620.     return PasteTextToGrid(m_LastDragOverCell, pDataObject);
  1621. }
  1622. void CGridCtrl::OnEditCut()
  1623. {
  1624.     if (!IsEditable())
  1625.         return;
  1626.     COleDataSource* pSource = CopyTextFromGrid();
  1627.     if (!pSource) return;
  1628.     pSource->SetClipboard();
  1629.     CutSelectedText();
  1630. }
  1631. void CGridCtrl::OnEditCopy()
  1632. {
  1633.     COleDataSource* pSource = CopyTextFromGrid();
  1634.     if (!pSource) return;
  1635.     pSource->SetClipboard();
  1636. }
  1637. void CGridCtrl::OnEditPaste()
  1638. {
  1639.     if (!IsEditable())
  1640.         return;
  1641.     // Get the Focus cell, or if none, get the topleft (non-fixed) cell
  1642.     CCellID cell = GetFocusCell();  
  1643.     if (!IsValid(cell)) cell = GetTopleftNonFixedCell();  
  1644.     if (!IsValid(cell)) return;
  1645.     // Attach a COleDataObject to the clipboard and paste the data to the grid
  1646.     COleDataObject obj;
  1647.     if (obj.AttachClipboard()) 
  1648.         PasteTextToGrid(cell, &obj);
  1649. }
  1650. void CGridCtrl::OnEditSelectAll() 
  1651. {
  1652.     SelectAllCells();
  1653. }
  1654. void CGridCtrl::OnUpdateEditCopy(CCmdUI* pCmdUI) 
  1655. {
  1656.     CCellRange Selection = GetSelectedCellRange();
  1657.     pCmdUI->Enable(Selection.Count() && IsValid(Selection));
  1658. }
  1659. void CGridCtrl::OnUpdateEditCut(CCmdUI* pCmdUI) 
  1660. {
  1661.     CCellRange Selection = GetSelectedCellRange();
  1662.     pCmdUI->Enable(IsEditable() && Selection.Count() && IsValid(Selection));
  1663. }
  1664. void CGridCtrl::OnUpdateEditPaste(CCmdUI* pCmdUI) 
  1665. {
  1666.     CCellID cell = GetFocusCell();
  1667.     BOOL bCanPaste = IsValid(cell) && IsCellEditable(cell) &&
  1668.                      ::IsClipboardFormatAvailable(CF_TEXT);
  1669.     pCmdUI->Enable(bCanPaste);
  1670. }
  1671. void CGridCtrl::OnUpdateEditSelectAll(CCmdUI* pCmdUI) 
  1672. {
  1673.     pCmdUI->Enable(m_bEnableSelection);
  1674. }
  1675. ////////////////////////////////////////////////////////////////////////////////////////
  1676. // hittest-like functions
  1677. // Get cell from point
  1678. CCellID CGridCtrl::GetCellFromPt(CPoint point, BOOL bAllowFixedCellCheck /*=TRUE*/) const
  1679. {  
  1680.     CCellID idTopLeft = GetTopleftNonFixedCell();
  1681.     CCellID cellID; // return value
  1682.     // calculate column index
  1683.     int fixedColWidth = GetFixedColumnWidth();
  1684.     if (point.x < 0 || (!bAllowFixedCellCheck && point.x < fixedColWidth)) // not in window
  1685.         cellID.col = -1;
  1686.     else if (point.x < fixedColWidth) // in fixed col
  1687.     {
  1688.         int xpos = 0;
  1689.         for (int col = 0; col < m_nFixedCols; col++)
  1690.         {
  1691.             xpos += GetColumnWidth(col);
  1692.             if (xpos > point.x) break;
  1693.         }
  1694.         cellID.col = col;
  1695.     }
  1696.     else    // in non-fixed col
  1697.     {
  1698.         int xpos = fixedColWidth;
  1699.         for (int col = idTopLeft.col; col < GetColumnCount(); col++)
  1700.         {
  1701.             xpos += GetColumnWidth(col);
  1702.             if (xpos > point.x) break;
  1703.         }
  1704.         if (col >= GetColumnCount())
  1705.             cellID.col = -1;
  1706.         else
  1707.             cellID.col = col;
  1708.     }
  1709.     
  1710.     // calculate row index
  1711.     int fixedRowHeight = GetFixedRowHeight();
  1712.     if (point.y < 0 || (!bAllowFixedCellCheck && point.y < fixedRowHeight)) // not in window
  1713.         cellID.row = -1;
  1714.     else if (point.y < fixedRowHeight) // in fixed col
  1715.     {
  1716.         int ypos = 0;
  1717.         for (int row = 0; row < m_nFixedRows; row++)
  1718.         {
  1719.             ypos += GetRowHeight(row);
  1720.             if (ypos > point.y) break;
  1721.         }
  1722.         cellID.row = row;
  1723.     }
  1724.     else
  1725.     {
  1726.         int ypos = fixedRowHeight;
  1727.         for (int row = idTopLeft.row; row < GetRowCount(); row++)
  1728.         {
  1729.             ypos += GetRowHeight(row);
  1730.             if (ypos > point.y) break;
  1731.         }
  1732.         if (row >= GetRowCount())
  1733.             cellID.row = -1;
  1734.         else
  1735.             cellID.row = row;
  1736.     }
  1737.     return cellID;
  1738. }
  1739. ////////////////////////////////////////////////////////////////////////////////
  1740. // CGridCtrl cellrange functions
  1741. CCellID CGridCtrl::GetTopleftNonFixedCell() const
  1742. {
  1743.     int nVertScroll = GetScrollPos(SB_VERT), 
  1744.         nHorzScroll = GetScrollPos(SB_HORZ);
  1745.     int nColumn = m_nFixedCols, nRight = 0;
  1746.     while (nRight < nHorzScroll && nColumn < (GetColumnCount()-1))
  1747.         nRight += GetColumnWidth(nColumn++);
  1748.     int nRow = m_nFixedRows, nTop = 0;
  1749.     while (nTop < nVertScroll && nRow < (GetRowCount()-1))
  1750.         nTop += GetRowHeight(nRow++);
  1751.     //TRACE("TopLeft cell is row %d, col %dn",nRow, nColumn);
  1752.     return CCellID(nRow, nColumn);
  1753. }
  1754. // This gets even partially visible cells
  1755. CCellRange CGridCtrl::GetVisibleNonFixedCellRange(LPRECT pRect /*=NULL*/) const
  1756. {
  1757.     CRect rect;
  1758.     GetClientRect(rect);
  1759.     CCellID idTopLeft = GetTopleftNonFixedCell();
  1760.     // calc bottom
  1761.     int bottom = GetFixedRowHeight();
  1762.     for (int i = idTopLeft.row; i < GetRowCount(); i++)
  1763.     {
  1764.         bottom += GetRowHeight(i);
  1765.         if (bottom >= rect.bottom) {
  1766.             bottom = rect.bottom;
  1767.             break;
  1768.         }
  1769.     }                                
  1770.     int maxVisibleRow = min(i, GetRowCount() - 1);
  1771.     
  1772.     // calc right
  1773.     int right = GetFixedColumnWidth();
  1774.     for (i = idTopLeft.col; i < GetColumnCount(); i++)
  1775.     {
  1776.         right += GetColumnWidth(i);
  1777.         if (right >= rect.right) {
  1778.             right = rect.right;
  1779.             break;
  1780.         }
  1781.     }
  1782.     int maxVisibleCol = min(i, GetColumnCount() - 1);
  1783.     if (pRect) {
  1784.         pRect->left = pRect->top = 0;
  1785.         pRect->right = right;
  1786.         pRect->bottom = bottom;
  1787.     }
  1788.     return CCellRange(idTopLeft.row, idTopLeft.col, maxVisibleRow, maxVisibleCol);
  1789. }
  1790. // used by ResetScrollBars() - This gets only fully visible cells
  1791. CCellRange CGridCtrl::GetUnobstructedNonFixedCellRange() const
  1792. {
  1793.     CRect rect;
  1794.     GetClientRect(rect);
  1795.     CCellID idTopLeft = GetTopleftNonFixedCell();
  1796.     // calc bottom
  1797.     int bottom = GetFixedRowHeight();
  1798.     for (int i = idTopLeft.row; i < GetRowCount(); i++)
  1799.     {
  1800.         bottom += GetRowHeight(i);
  1801.         if (bottom >= rect.bottom) break;
  1802.     }
  1803.     int maxVisibleRow = min(i, GetRowCount() - 1);
  1804.     if (maxVisibleRow > 0 && bottom > rect.bottom) maxVisibleRow--;
  1805.     
  1806.     // calc right
  1807.     int right = GetFixedColumnWidth();
  1808.     for (i = idTopLeft.col; i < GetColumnCount(); i++)
  1809.     {
  1810.         right += GetColumnWidth(i);
  1811.         if (right >= rect.right) break;
  1812.     }
  1813.     int maxVisibleCol = min(i, GetColumnCount() - 1);
  1814.     if (maxVisibleCol > 0 && right > rect.right) maxVisibleCol--;
  1815.     return CCellRange(idTopLeft.row, idTopLeft.col, maxVisibleRow, maxVisibleCol);
  1816. }
  1817. // Returns the minimum bounding range of the current selection
  1818. // If no selection, then the returned CCellRange will be invalid
  1819. CCellRange CGridCtrl::GetSelectedCellRange() const
  1820. {
  1821.     CCellRange Selection(GetRowCount(), GetColumnCount(), -1,-1);
  1822.     for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  1823.     {
  1824.         DWORD key;
  1825.         CCellID cell;
  1826.         m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1827.         Selection.SetMinRow( min(Selection.GetMinRow(), cell.row) );
  1828.         Selection.SetMinCol( min(Selection.GetMinCol(), cell.col) );
  1829.         Selection.SetMaxRow( max(Selection.GetMaxRow(), cell.row) );
  1830.         Selection.SetMaxCol( max(Selection.GetMaxCol(), cell.col) );
  1831.     }
  1832.     return Selection;
  1833. }
  1834. // Returns ALL the cells in the grid
  1835. CCellRange CGridCtrl::GetCellRange() const
  1836. {
  1837.     return CCellRange(0, 0, GetRowCount() - 1, GetColumnCount() - 1);
  1838. }
  1839. void CGridCtrl::ResetSelectedRange()
  1840. {
  1841.     SetSelectedRange(-1,-1,-1,-1);
  1842.     SetFocusCell(-1,-1);
  1843. }
  1844. // Get/Set scroll position using 32 bit functions
  1845. int CGridCtrl::GetScrollPos32(int nBar, BOOL bGetTrackPos /* = FALSE */)
  1846. {
  1847.     SCROLLINFO si;
  1848.     si.cbSize = sizeof(SCROLLINFO);
  1849.     if (bGetTrackPos)
  1850.     {
  1851.         if (GetScrollInfo(nBar, &si, SIF_TRACKPOS))
  1852.             return si.nTrackPos;
  1853.     }
  1854.     else 
  1855.     {
  1856.         if (GetScrollInfo(nBar, &si, SIF_POS))
  1857.             return si.nPos;
  1858.     }
  1859.     return 0;
  1860. }
  1861. BOOL CGridCtrl::SetScrollPos32(int nBar, int nPos, BOOL bRedraw /* = TRUE */)
  1862. {
  1863.     SCROLLINFO si;
  1864.     si.cbSize = sizeof(SCROLLINFO);
  1865.     si.fMask  = SIF_POS;
  1866.     si.nPos   = nPos;
  1867.     return SetScrollInfo(nBar, &si, bRedraw);
  1868. }
  1869. void CGridCtrl::ResetScrollBars()
  1870. {
  1871.     if (!m_bAllowDraw || !::IsWindow(GetSafeHwnd())) 
  1872.         return;
  1873.     CRect rect;
  1874.     GetClientRect(rect);
  1875.     rect.left  += GetFixedColumnWidth();
  1876.     rect.top   += GetFixedRowHeight();
  1877.     if (rect.left >= rect.right || rect.top >= rect.bottom) return;
  1878.     CRect VisibleRect(GetFixedColumnWidth(), GetFixedRowHeight(), rect.right, rect.bottom);
  1879.     CRect VirtualRect(GetFixedColumnWidth(), GetFixedRowHeight(), GetVirtualWidth(), GetVirtualHeight());
  1880.     CCellRange visibleCells = GetUnobstructedNonFixedCellRange();
  1881.     if (!IsValid(visibleCells)) return;
  1882.     SCROLLINFO si;
  1883.     si.cbSize = sizeof(SCROLLINFO);
  1884.     si.fMask  = SIF_PAGE;
  1885.     si.nPage  = VisibleRect.Width();    SetScrollInfo(SB_HORZ, &si, FALSE); 
  1886.     si.nPage  = VisibleRect.Height();   SetScrollInfo(SB_VERT, &si, FALSE); 
  1887.     if (VisibleRect.Height() < VirtualRect.Height())
  1888.         m_nVScrollMax = VirtualRect.Height()-1; //+ GetRowHeight(visibleCells.GetTopLeft().row);
  1889.     else
  1890.         m_nVScrollMax = 0;
  1891.     if (VisibleRect.Width() < VirtualRect.Width())
  1892.         m_nHScrollMax = VirtualRect.Width()-1; //+ GetColumnWidth(visibleCells.GetTopLeft().col);
  1893.     else
  1894.         m_nHScrollMax = 0;
  1895.     ASSERT(m_nVScrollMax < INT_MAX && m_nHScrollMax < INT_MAX); // This should be fine :)
  1896.     SetScrollRange(SB_VERT, 0, m_nVScrollMax, TRUE);
  1897.     SetScrollRange(SB_HORZ, 0, m_nHScrollMax, TRUE);
  1898. }
  1899. ////////////////////////////////////////////////////////////////////////////////////
  1900. // Row/Column position functions
  1901. // returns the top left point of the cell. Returns FALSE if cell not visible.
  1902. BOOL CGridCtrl::GetCellOrigin(int nRow, int nCol, LPPOINT p) const
  1903. {
  1904.     int i;
  1905.     if (!IsValid(nRow, nCol)) return FALSE;
  1906.     CCellID idTopLeft;
  1907.     if (nCol >= m_nFixedCols || nRow >= m_nFixedRows)
  1908.         idTopLeft = GetTopleftNonFixedCell();
  1909.     if ((nRow >= m_nFixedRows && nRow < idTopLeft.row) ||
  1910.         (nCol>= m_nFixedCols && nCol < idTopLeft.col))
  1911.         return FALSE;
  1912.     p->x = 0;
  1913.     if (nCol < m_nFixedCols)                      // is a fixed column
  1914.         for (i = 0; i < nCol; i++)
  1915.             p->x += GetColumnWidth(i);
  1916.     else {                                        // is a scrollable data column
  1917.         for (i = 0; i < m_nFixedCols; i++)
  1918.             p->x += GetColumnWidth(i);
  1919.         for (i = idTopLeft.col; i < nCol; i++)
  1920.             p->x += GetColumnWidth(i);
  1921.     }
  1922.     p->y = 0;
  1923.     if (nRow < m_nFixedRows)                      // is a fixed row
  1924.         for (i = 0; i < nRow; i++)
  1925.             p->y += GetRowHeight(i);
  1926.     else {                                        // is a scrollable data row
  1927.         for (i = 0; i < m_nFixedRows; i++)
  1928.             p->y += GetRowHeight(i);
  1929.         for (i = idTopLeft.row; i < nRow; i++)
  1930.             p->y += GetRowHeight(i);
  1931.     }
  1932.     return TRUE;
  1933. }
  1934. BOOL CGridCtrl::GetCellOrigin(const CCellID& cell, LPPOINT p) const
  1935. {
  1936.     return GetCellOrigin(cell.row, cell.col, p);
  1937. }
  1938. // Returns the bounding box of the cell
  1939. BOOL CGridCtrl::GetCellRect(const CCellID& cell, LPRECT pRect) const
  1940. {
  1941.     return GetCellRect(cell.row, cell.col, pRect);
  1942. }
  1943. BOOL CGridCtrl::GetCellRect(int nRow, int nCol, LPRECT pRect) const
  1944. {    
  1945.     CPoint CellOrigin;
  1946.     if (!GetCellOrigin(nRow, nCol, &CellOrigin)) return FALSE;
  1947.     pRect->left   = CellOrigin.x;
  1948.     pRect->top    = CellOrigin.y;
  1949.     pRect->right  = CellOrigin.x + GetColumnWidth(nCol)-1;
  1950.     pRect->bottom = CellOrigin.y + GetRowHeight(nRow)-1;
  1951.     //TRACE("Row %d, col %d: L %d, T %d, W %d, H %d:  %d,%d - %d,%dn",
  1952.     //      nRow,nCol, CellOrigin.x, CellOrigin.y, GetColumnWidth(nCol), GetRowHeight(nRow),
  1953.     //      pRect->left, pRect->top, pRect->right, pRect->bottom);
  1954.     return TRUE;
  1955. }
  1956. // Returns the bounding box of a range of cells
  1957. BOOL CGridCtrl::GetCellRangeRect(const CCellRange& cellRange, LPRECT lpRect) const
  1958. {
  1959.     CPoint MinOrigin,MaxOrigin;
  1960.     if (!GetCellOrigin(cellRange.GetMinRow(), cellRange.GetMinCol(), &MinOrigin)) 
  1961.         return FALSE;
  1962.     if (!GetCellOrigin(cellRange.GetMaxRow(), cellRange.GetMaxCol(), &MaxOrigin)) 
  1963.         return FALSE;
  1964.     lpRect->left   = MinOrigin.x;
  1965.     lpRect->top    = MinOrigin.y;
  1966.     lpRect->right  = MaxOrigin.x + GetColumnWidth(cellRange.GetMaxCol()-1);
  1967.     lpRect->bottom = MaxOrigin.y + GetRowHeight(cellRange.GetMaxRow()-1);
  1968.     return TRUE;
  1969. }
  1970. ////////////////////////////////////////////////////////////////////////////////////
  1971. // Grid attribute functions
  1972. LRESULT CGridCtrl::OnSetFont(WPARAM hFont, LPARAM /*lParam */)
  1973. {
  1974.     LRESULT result = Default();
  1975.     // Get the logical font
  1976.     LOGFONT lf;
  1977.     if (!GetObject((HFONT) hFont, sizeof(LOGFONT), &lf))
  1978.         return result;
  1979.     
  1980.     // Store font as the global default
  1981.     memcpy(&m_Logfont, &lf, sizeof(LOGFONT));
  1982.     
  1983.     // reset all cells' fonts
  1984.     for (int row = 0; row < GetRowCount(); row++)
  1985.         for (int col = 0; col < GetColumnCount(); col++)
  1986.             SetItemFont(row, col, &lf);
  1987.     // Get the font size and hence the default cell size
  1988.     CDC* pDC = GetDC();
  1989.     if (pDC) 
  1990.     {
  1991.         m_Font.DeleteObject();
  1992.         m_Font.CreateFontIndirect(&m_Logfont);
  1993.         CFont* pOldFont = pDC->SelectObject(&m_Font);
  1994.         
  1995.         TEXTMETRIC tm;
  1996.         pDC->GetTextMetrics(&tm);
  1997.         
  1998.         m_nMargin = pDC->GetTextExtent(_T(" "),1).cx;
  1999.         pDC->SelectObject(pOldFont);
  2000.         ReleaseDC(pDC);
  2001.         m_nDefCellHeight = tm.tmHeight+tm.tmExternalLeading + 2*m_nMargin;
  2002.         m_nDefCellWidth  = tm.tmAveCharWidth*12 + 2*m_nMargin;
  2003.     }
  2004.     if (::IsWindow(GetSafeHwnd())) 
  2005.         Invalidate();
  2006.     return result;
  2007. }
  2008. LRESULT CGridCtrl::OnGetFont(WPARAM /*wParam*/, LPARAM /*lParam*/)
  2009. {
  2010.     return (LRESULT) (HFONT) m_Font;
  2011. }
  2012. BOOL CGridCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
  2013. {
  2014.     if (nHitTest == HTCLIENT)
  2015.     {
  2016.         switch (m_MouseMode) 
  2017.         {
  2018.             case MOUSE_OVER_COL_DIVIDE: SetCursor(::LoadCursor(NULL, IDC_SIZEWE)); break;
  2019.             case MOUSE_OVER_ROW_DIVIDE: SetCursor(::LoadCursor(NULL, IDC_SIZENS)); break;
  2020.             case MOUSE_DRAGGING:        break;
  2021.             default:                    SetCursor(::LoadCursor(NULL, IDC_ARROW));
  2022.         }
  2023.         return TRUE;
  2024.     }
  2025.     return CWnd::OnSetCursor(pWnd, nHitTest, message);
  2026. }
  2027. ////////////////////////////////////////////////////////////////////////////////////
  2028. // Row/Column count functions
  2029. BOOL CGridCtrl::SetFixedRowCount(int nFixedRows)
  2030. {
  2031.     ASSERT(nFixedRows >= 0);
  2032.     if (nFixedRows > GetRowCount()) 
  2033.         if (!SetRowCount(nFixedRows)) return FALSE;
  2034.     if (m_idCurrentCell.row < nFixedRows)
  2035.         SetFocusCell(-1,-1);
  2036.     m_nFixedRows = nFixedRows;
  2037.     if (GetSafeHwnd() && m_bAllowDraw)
  2038.         Invalidate();
  2039.     return TRUE;
  2040. }
  2041. BOOL CGridCtrl::SetFixedColumnCount(int nFixedCols)
  2042. {
  2043.     ASSERT(nFixedCols >= 0);
  2044.     if (nFixedCols > GetColumnCount())
  2045.         if (!SetColumnCount(nFixedCols)) return FALSE;
  2046.     if (m_idCurrentCell.col < nFixedCols)
  2047.         SetFocusCell(-1,-1);
  2048.     m_nFixedCols = nFixedCols;
  2049.     if (GetSafeHwnd() && m_bAllowDraw)
  2050.         Invalidate();
  2051.     return TRUE;
  2052. }
  2053. BOOL CGridCtrl::SetRowCount(int nRows)
  2054. {
  2055.     ASSERT(nRows >= 0);
  2056.     if (nRows == GetRowCount()) return TRUE;
  2057.     if (nRows < m_nFixedRows) 
  2058.         m_nFixedRows = nRows;
  2059.     if (m_idCurrentCell.row >= nRows)
  2060.         SetFocusCell(-1,-1);
  2061.     int addedRows = nRows - GetRowCount();
  2062.     // If we are about to lose rows, then we need to delete the GridCell objects 
  2063.     // in each column within each row
  2064.     if (addedRows < 0) {
  2065.         for (int row = nRows; row < m_nRows; row++)
  2066.         {
  2067.             // Delete cells
  2068.             for (int col = 0; col < m_nCols; col++) 
  2069.             {
  2070.                 CGridCell* pCell = GetCell(row, col);
  2071.                 if (pCell) {
  2072.                     EmptyCell(pCell, row, col);