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

金融证券系统

开发平台:

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