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

金融证券系统

开发平台:

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