GridCtrl.cpp
上传用户:jzscgs158
上传日期:2022-05-25
资源大小:8709k
文件大小:208k
源码类别:

百货/超市行业

开发平台:

Visual C++

  1. // GridCtrl.cpp : implementation file
  2. //
  3. // MFC Grid Control v2.20
  4. //
  5. // Written by Chris Maunder <cmaunder@mail.com>
  6. // Copyright (c) 1998-2000. All Rights Reserved.
  7. //
  8. // The code contained in this file is based on the original
  9. // WorldCom Grid control written by Joe Willcoxson,
  10. //        mailto:chinajoe@aol.com
  11. //        http://users.aol.com/chinajoe
  12. // (These addresses may be out of date) The code has gone through 
  13. // so many modifications that I'm not sure if there is even a single 
  14. // original line of code. In any case Joe's code was a great 
  15. // framework on which to build.
  16. //
  17. // This code may be used in compiled form in any way you desire. This
  18. // file may be redistributed unmodified by any means PROVIDING it is 
  19. // not sold for profit without the authors written consent, and 
  20. // providing that this notice and the authors name and all copyright 
  21. // notices remains intact. 
  22. //
  23. // An email letting me know how you are using it would be nice as well. 
  24. //
  25. // This file is provided "as is" with no expressed or implied warranty.
  26. // The author accepts no liability for any damage/loss of business that
  27. // this product may cause.
  28. //
  29. // Expect bugs!
  30. // 
  31. // Please use and enjoy, and let me know of any bugs/mods/improvements 
  32. // that you have found/implemented and I will fix/incorporate them into 
  33. // this file. 
  34. //
  35. //  History:
  36. //  --------
  37. //  This control is constantly evolving, sometimes due to new features that I
  38. //  feel are necessary, and sometimes due to existing bugs. Where possible I
  39. //  have credited the changes to those who contributed code corrections or
  40. //  enhancements (names in brackets) or code suggestions (suggested by...)
  41. //
  42. //          1.0 - 1.13   20 Feb 1998 - 6 May 1999
  43. //                          First release version. Progressed from being a basic
  44. //                          grid based on the original WorldCom Grid control
  45. //                          written by Joe Willcoxson (mailto:chinajoe@aol.com,
  46. //                          http://users.aol.com/chinajoe) to something a little
  47. //                          more feature rich. Rewritten so many times I doubt
  48. //                          there is a single line of Joe's code left. Many, many,
  49. //                          MANY people sent in bug reports and fixes. Thank you
  50. //                          all.
  51. //
  52. //          2.0         1 Feb 2000
  53. //                          Rewritten to make the grid more object oriented, in
  54. //                          that the CGridCell class now takes care of cell-specific
  55. //                          tasks. This makes the code more robust, but more
  56. //                          importantly it allows the simple insertion of other
  57. //                          types of cells.
  58. //
  59. //          2.01       20 Feb 2000 - Eric Woodruff
  60. //                          Added better support for printing grids and
  61. //                          also fixed some other minor problems.
  62. //
  63. //          2.02       29 Feb 2000 - Brian V. Shifrin, Scot Reed, 
  64. //                          Fixes to reduce flicker, fix font selection bug,
  65. //                          Fixed SetFixed[Row/Col]Count bug
  66. //
  67. //          2.03       28 Mar 2000 - Aqiruse (marked with //FNA)
  68. //                           Titletips now use cell color
  69. //                          
  70. //          2.10       11 Mar 2000 - Ken Bertelson and Chris Maunder
  71. //                          - Additions for virtual CGridCell support of embedded tree 
  72. //                            & cell buttons implementation
  73. //                          - Optional WYSIWYG printing
  74. //                          - Awareness of hidden (0 width/height) rows and columns for 
  75. //                            key movements,  cut, copy, paste, and autosizing
  76. //                          - CGridCell can make title tips display any text rather than 
  77. //                            cell text only
  78. //                          - Minor vis bug fixes
  79. //                          - CGridCtrl now works with CGridCellBase instead of CGridCell
  80. //                            This is a taste of things to come.
  81. //
  82. //          2.11       19 May 2000 - Chris Maunder
  83. //                          - Increasing fixed cells clashed with selected cells (Ivan Ilinov)
  84. //                          - AutoSizeRows obvous bug fixed
  85. //                          - OnLButtonDown fix (Ken Bertelson) 
  86. //                          - ExpandToFit bug fixed (scrollbar space) (Igor Proskuriakov)
  87. //                          - List mode selection/deselection fixed
  88. //                          - Keyboard cell movement improved. You can now see the cells!
  89. //                          - m_nBarState MS madness fixed (Phil K)
  90. //
  91. //          2.12       26 May 2000 - Martin Richter
  92. //                          - If using TRY/CATCH (winCE) instead of try/catch (win32),
  93. //                            e->Delete is not called
  94. //                          - EnsureVisible "fix" was fixed properly.
  95. //
  96. //          2.20       30 Jul 2000 - Chris Maunder
  97. //                          - Font storage optimised (suggested by Martin Richter)
  98. //                          - AutoSizeColumn works on either column header, data or both
  99. //                          - EnsureVisible. The saga continues... (Ken)
  100. //                          - Rewrote exception handling
  101. //                          - Added TrackFocusCell and FrameFocusCell properties, as well as
  102. //                            ExpandLastColumn (suggested by Bruce E. Stemplewski).
  103. //                          - InsertColumn now allows you to insert columns at the end of the
  104. //                            column range (David Weibel)
  105. //                          - Shift-cell-selection more intuitive
  106. //                          - API change: Set/GetGridColor now Set/GetGridLineColor
  107. //                          - API change: Set/GetBkColor now Set/GetGridBkColor
  108. //                          - API change: Set/GetTextColor, Set/GetTextBkColor depricated 
  109. //                          - API change: Set/GetFixedTextColor, Set/GetFixedBkColor depricated 
  110. //                          - Stupid DDX_GridControl workaround removed.
  111. //                          - Added "virtual mode" via Set/GetVirtualMode
  112. //                          - Added SetCallbackFunc to allow callback functions in virtual mode
  113. //                          - Added Set/GetAutoSizeStyle
  114. //                          - AutoSize() bug fixed
  115. //                          - added GVIS_FIXEDROW, GVIS_FIXEDCOL states
  116. //                          - added Get/SetFixed[Row|Column]Selection
  117. //                          - cell "Get" methods now const'd. Sorry folks...
  118. //                          - GetMouseScrollLines now uses win98/W2K friendly code
  119. //                          - WS_EX_CLIENTEDGE style now implicit
  120. //
  121. // TODO:   1) Implement sparse grids (super easy now)
  122. //         2) Fix it so that as you drag select, the speed of selection increases
  123. //            with time.
  124. //         3) Scrolling is still a little dodgy (too much grey area). I know there
  125. //            is a simple fix but it's been a low priority
  126. //         4) Get some sleep
  127. //
  128. // ISSUES: 1) Crashes in the HPC/Pro emulation - but works fine on a device. ???
  129. //
  130. /////////////////////////////////////////////////////////////////////////////
  131. #include "stdafx.h"
  132. #include "MemDC.h"
  133. #include "GridCtrl.h"
  134. // OLE stuff for clipboard operations
  135. #include <afxadv.h>            // For CSharedFile
  136. #include <afxconv.h>           // For LPTSTR -> LPSTR macros
  137. #ifdef _DEBUG
  138. #define new DEBUG_NEW
  139. #undef THIS_FILE
  140. static char THIS_FILE[] = __FILE__;
  141. #endif
  142. // Spit out some messages as a sanity check for programmers
  143. #ifdef GRIDCONTROL_NO_TITLETIPS
  144. #pragma message(" -- CGridCtrl: No titletips for cells with large data")
  145. #endif
  146. #ifdef GRIDCONTROL_NO_DRAGDROP
  147. #pragma message(" -- CGridCtrl: No OLE drag and drop")
  148. #endif
  149. #ifdef GRIDCONTROL_NO_CLIPBOARD
  150. #pragma message(" -- CGridCtrl: No clipboard support")
  151. #endif
  152. #ifdef GRIDCONTROL_NO_PRINTING
  153. #pragma message(" -- CGridCtrl: No printing support")
  154. #endif
  155. IMPLEMENT_DYNCREATE(CGridCtrl, CWnd)
  156. // Get the number of lines to scroll with each mouse wheel notch
  157. // Why doesn't windows give us this function???
  158. UINT GetMouseScrollLines()
  159. {
  160.     int nScrollLines = 3;            // reasonable default
  161. #ifndef _WIN32_WCE
  162.     // Do things the hard way in win95
  163.     OSVERSIONINFO VersionInfo;
  164.     VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  165.     if (!GetVersionEx(&VersionInfo) || 
  166.         (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && VersionInfo.dwMinorVersion == 0))
  167.     {
  168.         HKEY hKey;
  169.         if (RegOpenKeyEx(HKEY_CURRENT_USER,  _T("Control Panel\Desktop"),
  170.             0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
  171.         {
  172.             TCHAR szData[128];
  173.             DWORD dwKeyDataType;
  174.             DWORD dwDataBufSize = sizeof(szData);
  175.             
  176.             if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
  177.                 (LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
  178.             {
  179.                 nScrollLines = _tcstoul(szData, NULL, 10);
  180.             }
  181.             RegCloseKey(hKey);
  182.         }
  183.     }
  184.     // win98 or greater
  185.     else
  186.         SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0, &nScrollLines, 0);
  187. #endif
  188.     return nScrollLines;
  189. }
  190. /////////////////////////////////////////////////////////////////////////////
  191. // CGridCtrl
  192. CGridCtrl::CGridCtrl(int nRows, int nCols, int nFixedRows, int nFixedCols)
  193. {
  194.     RegisterWindowClass();
  195.     // Initialize OLE libraries
  196.     m_bMustUninitOLE = FALSE;
  197. #if !defined(GRIDCONTROL_NO_DRAGDROP) || !defined(GRIDCONTROL_NO_CLIPBOARD)
  198.     _AFX_THREAD_STATE* pState = AfxGetThreadState();
  199.     if (!pState->m_bNeedTerm)
  200.     {
  201.         SCODE sc = ::OleInitialize(NULL);
  202.         if (FAILED(sc))
  203.             AfxMessageBox(_T("OLE initialization failed. Make sure that the OLE libraries are the correct version"));
  204.         else
  205.             m_bMustUninitOLE = TRUE;
  206.     }
  207. #endif
  208.     // Store the system colours in case they change. The gridctrl uses
  209.     // these colours, and in OnSysColorChange we can check to see if
  210.     // the gridctrl colours have been changed from the system colours.
  211.     // If they have, then leave them, otherwise change them to reflect
  212.     // the new system colours.
  213.     m_crWindowText        = ::GetSysColor(COLOR_WINDOWTEXT);
  214.     m_crWindowColour      = ::GetSysColor(COLOR_WINDOW);
  215.     m_cr3DFace            = ::GetSysColor(COLOR_3DFACE);
  216.     m_crShadow            = ::GetSysColor(COLOR_3DSHADOW);
  217.     m_crGridLineColour    = RGB(192,192,192);
  218.     m_nRows               = 0;
  219.     m_nCols               = 0;
  220.     m_nFixedRows          = 0;
  221.     m_nFixedCols          = 0;
  222.     m_bVirtualMode        = FALSE;
  223.     m_pfnCallback         = NULL;
  224.     m_nVScrollMax         = 0;          // Scroll position
  225.     m_nHScrollMax         = 0;
  226.     m_nRowsPerWheelNotch  = GetMouseScrollLines(); // Get the number of lines
  227.                                                    // per mouse wheel notch to scroll
  228.     m_nBarState           = GVL_NONE;
  229.     m_MouseMode           = MOUSE_NOTHING;
  230.     m_nGridLines          = GVL_BOTH;
  231.     m_bEditable           = TRUE;
  232.     m_bListMode           = FALSE;
  233.     m_bSingleRowSelection = FALSE;
  234.     m_bSingleColSelection = FALSE;
  235.     m_bMouseButtonDown    = FALSE;
  236.     m_bAllowDraw          = TRUE;       // allow draw updates
  237.     m_bEnableSelection    = TRUE;
  238.     m_bFixedColumnSelection = TRUE;
  239.     m_bFixedRowSelection  = TRUE;
  240.     m_bAllowRowResize     = TRUE;
  241.     m_bAllowColumnResize  = TRUE;
  242.     m_bSortOnClick        = FALSE;      // Sort on header row click
  243.     m_bHandleTabKey       = TRUE;
  244. #ifdef _WIN32_WCE
  245.     m_bDoubleBuffer       = FALSE;      // Use double buffering to avoid flicker?
  246. #else
  247.     m_bDoubleBuffer       = TRUE;       // Use double buffering to avoid flicker?
  248. #endif
  249.     m_bTitleTips          = TRUE;       // show cell title tips
  250.     m_bWysiwygPrinting    = FALSE;      // use size-to-width printing
  251.     m_bHiddenColUnhide    = TRUE;       // 0-width columns can be expanded via mouse
  252.     m_bHiddenRowUnhide    = TRUE;       // 0-Height rows can be expanded via mouse
  253.     m_bAllowColHide       = TRUE;       // Columns can be contracted to 0-width via mouse
  254.     m_bAllowRowHide       = TRUE;       // Rows can be contracted to 0-height via mouse
  255.     m_bAscending          = TRUE;       // sorting stuff
  256.     m_nSortColumn         = -1;
  257.     m_nAutoSizeColumnStyle = GVS_BOTH;  // Autosize grid using header and data info
  258.     m_nTimerID            = 0;          // For drag-selection
  259.     m_nTimerInterval      = 25;         // (in milliseconds)
  260.     m_nResizeCaptureRange = 3;          // When resizing columns/row, the cursor has to be
  261.                                         // within +/-3 pixels of the dividing line for
  262.                                         // resizing to be possible
  263.     m_pImageList          = NULL;       // Images in the grid
  264.     m_bAllowDragAndDrop   = FALSE;      // for drag and drop - EFW - off by default
  265.     m_bTrackFocusCell     = TRUE;       // Track Focus cell?
  266.     m_bFrameFocus         = TRUE;       // Frame the selected cell?
  267.     m_pRtcDefault = RUNTIME_CLASS(CGridCell);
  268.     SetupDefaultCells();
  269.     SetGridBkColor(m_crShadow);
  270.     // Set up the initial grid size
  271.     SetRowCount(nRows);
  272.     SetColumnCount(nCols);
  273.     SetFixedRowCount(nFixedRows);
  274.     SetFixedColumnCount(nFixedCols);
  275.     SetTitleTipTextClr(CLR_DEFAULT);  //FNA
  276. SetTitleTipBackClr(CLR_DEFAULT); 
  277.     // set initial selection range (ie. none)
  278.     m_SelectedCellMap.RemoveAll();
  279.     m_PrevSelectedCellMap.RemoveAll();
  280. #if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
  281.     // EFW - Added to support shaded/unshaded printout and
  282.     // user-definable margins.
  283.     m_bShadedPrintOut = TRUE;
  284.     SetPrintMarginInfo(2, 2, 4, 4, 1, 1, 1);
  285. #endif
  286. }
  287. CGridCtrl::~CGridCtrl()
  288. {
  289.     DeleteAllItems();
  290. #ifndef GRIDCONTROL_NO_TITLETIPS
  291.     if (m_bTitleTips && ::IsWindow(m_TitleTip.GetSafeHwnd())) 
  292.         m_TitleTip.DestroyWindow();
  293. #endif
  294.     DestroyWindow();
  295. #if !defined(GRIDCONTROL_NO_DRAGDROP) || !defined(GRIDCONTROL_NO_CLIPBOARD)
  296.     // BUG FIX - EFW
  297.     COleDataSource *pSource = COleDataSource::GetClipboardOwner();
  298.     if(pSource)
  299.         COleDataSource::FlushClipboard();
  300.     // Uninitialize OLE support
  301.     if (m_bMustUninitOLE)
  302.         ::OleUninitialize();
  303. #endif
  304. }
  305. // Register the window class if it has not already been registered.
  306. BOOL CGridCtrl::RegisterWindowClass()
  307. {
  308.     WNDCLASS wndcls;
  309.     HINSTANCE hInst = AfxGetInstanceHandle();
  310.     //HINSTANCE hInst = AfxGetResourceHandle();
  311.     if (!(::GetClassInfo(hInst, GRIDCTRL_CLASSNAME, &wndcls)))
  312.     {
  313.         // otherwise we need to register a new class
  314.         wndcls.style            = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  315.         wndcls.lpfnWndProc      = ::DefWindowProc;
  316.         wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
  317.         wndcls.hInstance        = hInst;
  318.         wndcls.hIcon            = NULL;
  319. #ifndef _WIN32_WCE_NO_CURSOR
  320.         wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
  321. #else
  322.         wndcls.hCursor          = 0;
  323. #endif
  324.         wndcls.hbrBackground    = (HBRUSH) (COLOR_3DFACE + 1);
  325.         wndcls.lpszMenuName     = NULL;
  326.         wndcls.lpszClassName    = GRIDCTRL_CLASSNAME;
  327.         if (!AfxRegisterClass(&wndcls))
  328.         {
  329.             AfxThrowResourceException();
  330.             return FALSE;
  331.         }
  332.     }
  333.     return TRUE;
  334. }
  335. BOOL CGridCtrl::Initialise()
  336. {
  337.     // Stop re-entry problems
  338.     static BOOL bInProcedure = FALSE;
  339.     if (bInProcedure)
  340.         return FALSE;
  341.     bInProcedure = TRUE;
  342. #ifndef GRIDCONTROL_NO_DRAGDROP
  343.     m_DropTarget.Register(this);
  344. #endif
  345. #ifndef GRIDCONTROL_NO_TITLETIPS
  346.     m_TitleTip.SetParentWnd(this);
  347. #endif
  348.     
  349.     if (::IsWindow(m_hWnd))
  350.         ModifyStyleEx(0, WS_EX_CLIENTEDGE, 0);
  351.    
  352.     bInProcedure = FALSE;
  353.     return TRUE;
  354. }
  355. // creates the control - use like any other window create control
  356. BOOL CGridCtrl::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
  357. {
  358.     ASSERT(pParentWnd->GetSafeHwnd());
  359.     if (!CWnd::Create(GRIDCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
  360.         return FALSE;
  361.     //Initialise();
  362.     // The number of rows and columns will only be non-zero if the constructor
  363.     // was called with non-zero initialising parameters. If this window was created
  364.     // using a dialog template then the number of rows and columns will be 0 (which
  365.     // means that the code below will not be needed - which is lucky 'cause it ain't
  366.     // gonna get called in a dialog-template-type-situation.
  367.     TRY
  368.     {
  369.         m_arRowHeights.SetSize(m_nRows);    // initialize row heights
  370.         m_arColWidths.SetSize(m_nCols);     // initialize column widths
  371.     }
  372.     CATCH (CMemoryException, e)
  373.     {
  374.         e->ReportError();
  375.         return FALSE;
  376.     }
  377.     END_CATCH
  378.     for (int i = 0; i < m_nRows; i++)
  379.         m_arRowHeights[i] = m_cellDefault.GetHeight();
  380.     for (i = 0; i < m_nCols; i++)
  381.         m_arColWidths[i] = m_cellDefault.GetWidth();
  382.     ResetScrollBars(); //- called in OnSize anyway
  383.     return TRUE;
  384. }
  385. void CGridCtrl::SetupDefaultCells()
  386. {
  387.     m_cellDefault.SetGrid(this);            // Normal editable cell
  388.     m_cellFixedColDef.SetGrid(this);        // Cell for fixed columns
  389.     m_cellFixedRowDef.SetGrid(this);        // Cell for fixed rows
  390.     m_cellFixedRowColDef.SetGrid(this);     // Cell for area overlapped by fixed columns/rows
  391.     m_cellDefault.SetTextClr(m_crWindowText);   
  392.     m_cellDefault.SetBackClr(m_crWindowColour); 
  393.     m_cellFixedColDef.SetTextClr(m_crWindowText);
  394.     m_cellFixedColDef.SetBackClr(m_cr3DFace);
  395.     m_cellFixedRowDef.SetTextClr(m_crWindowText);
  396.     m_cellFixedRowDef.SetBackClr(m_cr3DFace);
  397.     m_cellFixedRowColDef.SetTextClr(m_crWindowText);
  398.     m_cellFixedRowColDef.SetBackClr(m_cr3DFace);
  399. }
  400. void CGridCtrl::PreSubclassWindow()
  401. {
  402.     CWnd::PreSubclassWindow();
  403.     //HFONT hFont = ::CreateFontIndirect(m_cellDefault.GetFont());
  404.     //OnSetFont((LPARAM)hFont, 0);
  405.     //DeleteObject(hFont);
  406.     Initialise();
  407.     // ResetScrollBars(); - called in OnSize anyway
  408. }
  409. // Sends a message to the parent in the form of a WM_NOTIFY message with
  410. // a NM_GRIDVIEW structure attached
  411. LRESULT CGridCtrl::SendMessageToParent(int nRow, int nCol, int nMessage) const
  412. {
  413.     if (!IsWindow(m_hWnd))
  414.         return 0;
  415.     NM_GRIDVIEW nmgv;
  416.     nmgv.iRow         = nRow;
  417.     nmgv.iColumn      = nCol;
  418.     nmgv.hdr.hwndFrom = m_hWnd;
  419.     nmgv.hdr.idFrom   = GetDlgCtrlID();
  420.     nmgv.hdr.code     = nMessage;
  421.     CWnd *pOwner = GetOwner();
  422.     if (pOwner && IsWindow(pOwner->m_hWnd))
  423.         return pOwner->SendMessage(WM_NOTIFY, nmgv.hdr.idFrom, (LPARAM)&nmgv);
  424.     else
  425.         return 0;
  426. }
  427. // Send a request to the parent to return information on a given cell
  428. LRESULT CGridCtrl::SendDisplayRequestToParent(GV_DISPINFO* pDisplayInfo) const
  429. {
  430.     if (!IsWindow(m_hWnd))
  431.         return 0;
  432.     // Fix up the message headers
  433.     pDisplayInfo->hdr.hwndFrom = m_hWnd;
  434.     pDisplayInfo->hdr.idFrom   = GetDlgCtrlID();
  435.     pDisplayInfo->hdr.code     = GVN_GETDISPINFO;
  436.     // Send the message
  437.     CWnd *pOwner = GetOwner();
  438.     if (pOwner && IsWindow(pOwner->m_hWnd))
  439.         return pOwner->SendMessage(WM_NOTIFY, pDisplayInfo->hdr.idFrom, (LPARAM)pDisplayInfo);
  440.     else
  441.         return 0;
  442. }
  443. // Send a hint to the parent about caching information
  444. LRESULT CGridCtrl::SendCacheHintToParent(CCellRange& range) const
  445. {
  446.     if (!IsWindow(m_hWnd))
  447.         return 0;
  448.     GV_CACHEHINT CacheHint;
  449.     // Fix up the message headers
  450.     CacheHint.hdr.hwndFrom = m_hWnd;
  451.     CacheHint.hdr.idFrom   = GetDlgCtrlID();
  452.     CacheHint.hdr.code     = GVN_ODCACHEHINT;
  453.     CacheHint.range = range;
  454.     // Send the message
  455.     CWnd *pOwner = GetOwner();
  456.     if (pOwner && IsWindow(pOwner->m_hWnd))
  457.         return pOwner->SendMessage(WM_NOTIFY, CacheHint.hdr.idFrom, (LPARAM)&CacheHint);
  458.     else
  459.         return 0;
  460. }
  461. BEGIN_MESSAGE_MAP(CGridCtrl, CWnd)
  462. //EFW - Added ON_WM_RBUTTONUP
  463. //{{AFX_MSG_MAP(CGridCtrl)
  464.     ON_WM_PAINT()
  465.     ON_WM_HSCROLL()
  466.     ON_WM_VSCROLL()
  467.     ON_WM_SIZE()
  468.     ON_WM_LBUTTONUP()
  469.     ON_WM_LBUTTONDOWN()
  470.     ON_WM_MOUSEMOVE()
  471.     ON_WM_TIMER()
  472.     ON_WM_GETDLGCODE()
  473.     ON_WM_KEYDOWN()
  474.     ON_WM_CHAR()
  475.     ON_WM_LBUTTONDBLCLK()
  476.     ON_WM_ERASEBKGND()
  477.     ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
  478.     ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
  479.     ON_WM_SYSKEYDOWN()
  480. //}}AFX_MSG_MAP
  481. #ifndef _WIN32_WCE_NO_CURSOR
  482.     ON_WM_SETCURSOR()
  483. #endif
  484. #ifndef _WIN32_WCE
  485.     ON_WM_RBUTTONUP()
  486.     ON_WM_SYSCOLORCHANGE()
  487.     ON_WM_CAPTURECHANGED()
  488. #endif
  489. #ifndef GRIDCONTROL_NO_CLIPBOARD
  490.     ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
  491.     ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
  492.     ON_COMMAND(ID_EDIT_CUT, OnEditCut)
  493.     ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
  494.     ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
  495.     ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
  496. #endif
  497. #if (_WIN32_WCE >= 210)
  498.     ON_WM_SETTINGCHANGE()
  499. #endif
  500. #if !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
  501.     ON_WM_MOUSEWHEEL()
  502. #endif
  503.     ON_MESSAGE(WM_SETFONT, OnSetFont)
  504.     ON_MESSAGE(WM_GETFONT, OnGetFont)
  505.     ON_NOTIFY(GVN_ENDLABELEDIT, IDC_INPLACE_CONTROL, OnEndInPlaceEdit)
  506. END_MESSAGE_MAP()
  507. /////////////////////////////////////////////////////////////////////////////
  508. // CGridCtrl message handlers
  509. void CGridCtrl::OnPaint()
  510. {
  511.     CPaintDC dc(this);      // device context for painting
  512.     if (m_bDoubleBuffer)    // Use a memory DC to remove flicker
  513.     {
  514.         CMemDC MemDC(&dc);
  515.         OnDraw(&MemDC);
  516.     }
  517.     else                    // Draw raw - this helps in debugging vis problems.
  518.         OnDraw(&dc);
  519. }
  520. BOOL CGridCtrl::OnEraseBkgnd(CDC* /*pDC*/)
  521. {
  522.     return TRUE;    // Don't erase the background.
  523. }
  524. // Custom background erasure. This gets called from within the OnDraw function,
  525. // since we will (most likely) be using a memory DC to stop flicker. If we just
  526. // erase the background normally through OnEraseBkgnd, and didn't fill the memDC's
  527. // selected bitmap with colour, then all sorts of vis problems would occur
  528. void CGridCtrl::EraseBkgnd(CDC* pDC)
  529. {
  530.     CRect  VisRect, ClipRect, rect;
  531.     CBrush FixedRowColBack(GetDefaultCell(TRUE, TRUE)->GetBackClr()),
  532.            FixedRowBack(GetDefaultCell(TRUE, FALSE)->GetBackClr()),
  533.            FixedColBack(GetDefaultCell(FALSE, TRUE)->GetBackClr()),
  534.            TextBack(GetDefaultCell(FALSE, FALSE)->GetBackClr());
  535.     CBrush Back(GetGridBkColor()); 
  536.     //CBrush Back(GetTextBkColor());
  537.     if (pDC->GetClipBox(ClipRect) == ERROR)
  538.         return;
  539.     GetVisibleNonFixedCellRange(VisRect);
  540.     int nFixedColumnWidth = GetFixedColumnWidth();
  541.     int nFixedRowHeight = GetFixedRowHeight();
  542.     // Draw Fixed row/column background
  543.     if (ClipRect.left < nFixedColumnWidth && ClipRect.top < nFixedRowHeight)
  544.         pDC->FillRect(CRect(ClipRect.left, ClipRect.top, 
  545.                       nFixedColumnWidth, VisRect.bottom),
  546.                       &FixedRowColBack);
  547.     // Draw Fixed columns background
  548.     if (ClipRect.left < nFixedColumnWidth && ClipRect.top < VisRect.bottom)
  549.         pDC->FillRect(CRect(ClipRect.left, ClipRect.top, 
  550.                       nFixedColumnWidth, VisRect.bottom),
  551.                       &FixedColBack);
  552.         
  553.     // Draw Fixed rows background
  554.     if (ClipRect.top < nFixedRowHeight && 
  555.         ClipRect.right > nFixedColumnWidth && ClipRect.left < VisRect.right)
  556.         pDC->FillRect(CRect(nFixedColumnWidth-1, ClipRect.top,
  557.                       VisRect.right, nFixedRowHeight),
  558.                       &FixedRowBack);
  559.     // Draw non-fixed cell background
  560.     if (rect.IntersectRect(VisRect, ClipRect)) 
  561.     {
  562.         CRect CellRect(max(nFixedColumnWidth, rect.left), 
  563.                        max(nFixedRowHeight, rect.top),
  564.                        rect.right, rect.bottom);
  565.         pDC->FillRect(CellRect, &TextBack);
  566.     }
  567.     // Draw right hand side of window outside grid
  568.     if (VisRect.right < ClipRect.right) 
  569.         pDC->FillRect(CRect(VisRect.right, ClipRect.top, 
  570.                       ClipRect.right, ClipRect.bottom),
  571.                       &Back);
  572.     // Draw bottom of window below grid
  573.     if (VisRect.bottom < ClipRect.bottom && ClipRect.left < VisRect.right) 
  574.         pDC->FillRect(CRect(ClipRect.left, VisRect.bottom,
  575.                       VisRect.right, ClipRect.bottom),
  576.                       &Back);
  577. }
  578. void CGridCtrl::OnSize(UINT nType, int cx, int cy)
  579. {
  580.     static BOOL bAlreadyInsideThisProcedure = FALSE;
  581.     if (bAlreadyInsideThisProcedure)
  582.         return;
  583.     if (!::IsWindow(m_hWnd))
  584.         return;
  585.     // Start re-entry blocking
  586.     bAlreadyInsideThisProcedure = TRUE;
  587.     EndEditing();        // destroy any InPlaceEdit's
  588.     CWnd::OnSize(nType, cx, cy);
  589.     ResetScrollBars();
  590.     // End re-entry blocking
  591.     bAlreadyInsideThisProcedure = FALSE;
  592. }
  593. UINT CGridCtrl::OnGetDlgCode()
  594. {
  595.     UINT nCode = DLGC_WANTARROWS | DLGC_WANTCHARS; // DLGC_WANTALLKEYS; //
  596.     if (m_bHandleTabKey && !IsCTRLpressed())
  597.         nCode |= DLGC_WANTTAB;
  598.     return nCode;
  599. }
  600. #ifndef _WIN32_WCE
  601. // If system colours change, then redo colours
  602. void CGridCtrl::OnSysColorChange()
  603. {
  604.     CWnd::OnSysColorChange();
  605.     if (GetDefaultCell(FALSE, FALSE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  606.         GetDefaultCell(FALSE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  607.     if (GetDefaultCell(FALSE, FALSE)->GetBackClr() == m_crWindowColour)
  608.         GetDefaultCell(FALSE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  609.     if (GetDefaultCell(TRUE, FALSE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  610.         GetDefaultCell(TRUE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  611.     if (GetDefaultCell(TRUE, FALSE)->GetBackClr() == m_crWindowColour)
  612.         GetDefaultCell(TRUE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  613.     if (GetDefaultCell(FALSE, TRUE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  614.         GetDefaultCell(FALSE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  615.     if (GetDefaultCell(FALSE, TRUE)->GetBackClr() == m_crWindowColour)
  616.         GetDefaultCell(FALSE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  617.     if (GetDefaultCell(TRUE, TRUE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  618.         GetDefaultCell(TRUE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  619.     if (GetDefaultCell(TRUE, TRUE)->GetBackClr() == m_crWindowColour)
  620.         GetDefaultCell(TRUE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  621.     if (GetGridBkColor() == m_crShadow)
  622.         SetGridBkColor(::GetSysColor(COLOR_3DSHADOW));
  623.     m_crWindowText   = ::GetSysColor(COLOR_WINDOWTEXT);
  624.     m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
  625.     m_cr3DFace       = ::GetSysColor(COLOR_3DFACE);
  626.     m_crShadow       = ::GetSysColor(COLOR_3DSHADOW);
  627. }
  628. #endif
  629. #ifndef _WIN32_WCE_NO_CURSOR
  630. // If we are drag-selecting cells, or drag and dropping, stop now
  631. void CGridCtrl::OnCaptureChanged(CWnd *pWnd)
  632. {
  633.     if (pWnd->GetSafeHwnd() == GetSafeHwnd())
  634.         return;
  635.     // kill timer if active
  636.     if (m_nTimerID != 0)
  637.     {
  638.         KillTimer(m_nTimerID);
  639.         m_nTimerID = 0;
  640.     }
  641. #ifndef GRIDCONTROL_NO_DRAGDROP
  642.     // Kill drag and drop if active
  643.     if (m_MouseMode == MOUSE_DRAGGING)
  644.         m_MouseMode = MOUSE_NOTHING;
  645. #endif
  646. }
  647. #endif
  648. #if (_MFC_VER >= 0x0421) || (_WIN32_WCE >= 210)
  649. // If system settings change, then redo colours
  650. void CGridCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
  651. {
  652.     CWnd::OnSettingChange(uFlags, lpszSection);
  653.     if (GetDefaultCell(FALSE, FALSE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  654.         GetDefaultCell(FALSE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  655.     if (GetDefaultCell(FALSE, FALSE)->GetBackClr() == m_crWindowColour)
  656.         GetDefaultCell(FALSE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  657.     if (GetDefaultCell(TRUE, FALSE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  658.         GetDefaultCell(TRUE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  659.     if (GetDefaultCell(TRUE, FALSE)->GetBackClr() == m_crWindowColour)
  660.         GetDefaultCell(TRUE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  661.     if (GetDefaultCell(FALSE, TRUE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  662.         GetDefaultCell(FALSE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  663.     if (GetDefaultCell(FALSE, TRUE)->GetBackClr() == m_crWindowColour)
  664.         GetDefaultCell(FALSE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  665.     if (GetDefaultCell(TRUE, TRUE)->GetTextClr() == m_crWindowText)                   // Still using system colours
  666.         GetDefaultCell(TRUE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT));      // set to new system colour
  667.     if (GetDefaultCell(TRUE, TRUE)->GetBackClr() == m_crWindowColour)
  668.         GetDefaultCell(TRUE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
  669.     if (GetGridBkColor() == m_crShadow)
  670.         SetGridBkColor(::GetSysColor(COLOR_3DSHADOW));
  671.     m_crWindowText   = ::GetSysColor(COLOR_WINDOWTEXT);
  672.     m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
  673.     m_cr3DFace       = ::GetSysColor(COLOR_3DFACE);
  674.     m_crShadow       = ::GetSysColor(COLOR_3DSHADOW);
  675.     m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
  676. }
  677. #endif
  678. // For drag-selection. Scrolls hidden cells into view
  679. // TODO: decrease timer interval over time to speed up selection over time
  680. void CGridCtrl::OnTimer(UINT nIDEvent)
  681. {
  682.     ASSERT(nIDEvent == WM_LBUTTONDOWN);
  683.     if (nIDEvent != WM_LBUTTONDOWN)
  684.         return;
  685.     CPoint pt, origPt;
  686. #ifdef _WIN32_WCE
  687.     if (m_MouseMode == MOUSE_NOTHING)
  688.         return;
  689.     origPt = GetMessagePos();
  690. #else
  691.     if (!GetCursorPos(&origPt))
  692.         return;
  693. #endif
  694.     ScreenToClient(&origPt);
  695.     CRect rect;
  696.     GetClientRect(rect);
  697.     int nFixedRowHeight = GetFixedRowHeight();
  698.     int nFixedColWidth = GetFixedColumnWidth();
  699.     pt = origPt;
  700.     if (pt.y > rect.bottom)
  701.     {
  702.         //SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  703.         SendMessage(WM_KEYDOWN, VK_DOWN, 0);
  704.         if (pt.x < rect.left)
  705.             pt.x = rect.left;
  706.         if (pt.x > rect.right)
  707.             pt.x = rect.right;
  708.         pt.y = rect.bottom;
  709.         OnSelecting(GetCellFromPt(pt));
  710.     }
  711.     else if (pt.y < nFixedRowHeight)
  712.     {
  713.         //SendMessage(WM_VSCROLL, SB_LINEUP, 0);
  714.         SendMessage(WM_KEYDOWN, VK_UP, 0);
  715.         if (pt.x < rect.left)
  716.             pt.x = rect.left;
  717.         if (pt.x > rect.right)
  718.             pt.x = rect.right;
  719.         pt.y = nFixedRowHeight + 1;
  720.         OnSelecting(GetCellFromPt(pt));
  721.     }
  722.     pt = origPt;
  723.     if (pt.x > rect.right)
  724.     {
  725.         // SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
  726.         SendMessage(WM_KEYDOWN, VK_RIGHT, 0);
  727.         if (pt.y < rect.top)
  728.             pt.y = rect.top;
  729.         if (pt.y > rect.bottom)
  730.             pt.y = rect.bottom;
  731.         pt.x = rect.right;
  732.         OnSelecting(GetCellFromPt(pt));
  733.     }
  734.     else if (pt.x < nFixedColWidth)
  735.     {
  736.         //SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
  737.         SendMessage(WM_KEYDOWN, VK_LEFT, 0);
  738.         if (pt.y < rect.top)
  739.             pt.y = rect.top;
  740.         if (pt.y > rect.bottom)
  741.             pt.y = rect.bottom;
  742.         pt.x = nFixedColWidth + 1;
  743.         OnSelecting(GetCellFromPt(pt));
  744.     }
  745. }
  746. // move about with keyboard
  747. void CGridCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  748. {
  749.     if (!IsValid(m_idCurrentCell))
  750.     {
  751.         CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  752.         return;
  753.     }
  754.     CCellID next = m_idCurrentCell;
  755.     BOOL bChangeLine = FALSE;
  756.     if (IsCTRLpressed())
  757.     {
  758.         switch (nChar)
  759.         {
  760.         case 'A':
  761.             OnEditSelectAll();
  762.             break;
  763. #ifndef GRIDCONTROL_NO_CLIPBOARD
  764.         case 'X':
  765.             OnEditCut();
  766.             break;
  767.         case VK_INSERT:
  768.         case 'C':
  769.             OnEditCopy();
  770.             break;
  771.         case 'V':
  772.             OnEditPaste();
  773.             break;
  774. #endif
  775.         }
  776.     }
  777. #ifndef GRIDCONTROL_NO_CLIPBOARD
  778.     if (IsSHIFTpressed() &&(nChar == VK_INSERT))
  779.         OnEditPaste();
  780. #endif
  781.     BOOL bFoundVisible;
  782.     int iOrig;
  783.     switch (nChar)
  784.     {
  785.     case VK_DELETE: 
  786.         if (IsCellEditable(m_idCurrentCell.row, m_idCurrentCell.col))
  787.         {
  788.             SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_BEGINLABELEDIT);
  789.             SetItemText(m_idCurrentCell.row, m_idCurrentCell.col, _T(""));
  790.             SetModified(TRUE, m_idCurrentCell.row, m_idCurrentCell.col);
  791.             SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_ENDLABELEDIT);
  792.             RedrawCell(m_idCurrentCell);
  793.         }
  794.         break;
  795.         
  796.     case VK_TAB:    
  797.         if (IsSHIFTpressed())
  798.         {
  799.             if (next.col > m_nFixedCols) 
  800.                 next.col--;
  801.             else if (next.col == m_nFixedCols && next.row > m_nFixedRows) 
  802.             {
  803.                 next.row--; 
  804.                 next.col = GetColumnCount() - 1; 
  805.                 bChangeLine = TRUE;
  806.             }
  807.             else
  808.                 CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  809.         }
  810.         else
  811.         {
  812.             if (next.col <(GetColumnCount() - 1)) 
  813.                 next.col++;
  814.             else if (next.col == (GetColumnCount() - 1) && 
  815.                 next.row <(GetRowCount() - 1))
  816.             {
  817.                 next.row++; 
  818.                 next.col = m_nFixedCols; 
  819.                 bChangeLine = TRUE;
  820.             }
  821.             else
  822.                 CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  823.         } 
  824.         break;
  825.         
  826.     case VK_DOWN:   
  827.             // don't let user go to a hidden row
  828.             bFoundVisible = FALSE;
  829.             iOrig = next.row;
  830.             next.row++;
  831.             while( next.row < GetRowCount())
  832.             {
  833.                 if( GetRowHeight( next.row) > 0)
  834.                 {
  835.                     bFoundVisible = TRUE;
  836.                     break;
  837.                 }
  838.                 next.row++;
  839.             }
  840.             if( !bFoundVisible)
  841.                 next.row = iOrig;
  842.             break;
  843.         
  844.     case VK_UP:     
  845.             // don't let user go to a hidden row
  846.             bFoundVisible = FALSE;
  847.             iOrig = next.row;
  848.             next.row--;
  849.             while( next.row >= m_nFixedRows)
  850.             {
  851.                 if( GetRowHeight( next.row) > 0)
  852.                 {
  853.                     bFoundVisible = TRUE;
  854.                     break;
  855.                 }
  856.                 next.row--;
  857.             }
  858.             if( !bFoundVisible)
  859.                 next.row = iOrig;
  860.             break;
  861.         
  862.     case VK_RIGHT:  
  863.             // don't let user go to a hidden column
  864.             bFoundVisible = FALSE;
  865.             iOrig = next.col;
  866.             next.col++;
  867.             while( next.col < GetColumnCount())
  868.             {
  869.                 if( GetColumnWidth( next.col) > 0)
  870.                 {
  871.                     bFoundVisible = TRUE;
  872.                     break;
  873.                 }
  874.                 next.col++;
  875.             }
  876.             if( !bFoundVisible)
  877.                 next.col = iOrig;
  878.             break;
  879.         
  880.     case VK_LEFT:   
  881.             // don't let user go to a hidden column
  882.             bFoundVisible = FALSE;
  883.             iOrig = next.col;
  884.             next.col--;
  885.             while( next.col >= m_nFixedCols)
  886.             {
  887.                 if( GetColumnWidth( next.col) > 0)
  888.                 {
  889.                     bFoundVisible = TRUE;
  890.                     break;
  891.                 }
  892.                 next.col--;
  893.             }
  894.             if( !bFoundVisible)
  895.                 next.col = iOrig;
  896.             break;
  897.         
  898.     case VK_NEXT:   
  899.         {
  900.             CCellID idOldTopLeft = GetTopleftNonFixedCell();
  901.             SendMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
  902.             CCellID idNewTopLeft = GetTopleftNonFixedCell();
  903.             int increment = idNewTopLeft.row - idOldTopLeft.row;
  904.             if (increment)
  905.             {
  906.                 next.row += increment;
  907.                 if (next.row >(GetRowCount() - 1))
  908.                     next.row = GetRowCount() - 1;
  909.             }
  910.             else
  911.                 next.row = GetRowCount() - 1;
  912.             break;
  913.         }
  914.     case VK_PRIOR:  
  915.         {
  916.             CCellID idOldTopLeft = GetTopleftNonFixedCell();
  917.             SendMessage(WM_VSCROLL, SB_PAGEUP, 0);
  918.             CCellID idNewTopLeft = GetTopleftNonFixedCell();
  919.             
  920.             int increment = idNewTopLeft.row - idOldTopLeft.row;
  921.             if (increment) 
  922.             {
  923.                 next.row += increment;
  924.                 if (next.row < m_nFixedRows) 
  925.                     next.row = m_nFixedRows;
  926.             }
  927.             else
  928.                 next.row = m_nFixedRows;
  929.             break;
  930.         }
  931.         
  932.     case VK_HOME:   
  933.             // Home and Ctrl-Home work more like Excel
  934.             //  and don't let user go to a hidden cell
  935.             if (IsCTRLpressed())
  936.             {
  937.                 SendMessage(WM_VSCROLL, SB_TOP, 0);
  938.                 SendMessage(WM_HSCROLL, SB_LEFT, 0);
  939.                 next.row = m_nFixedRows;
  940.                 next.col = m_nFixedCols;
  941.             }
  942.             else
  943.             {
  944.                 SendMessage(WM_HSCROLL, SB_LEFT, 0);
  945.                 next.col = m_nFixedCols;
  946.             }
  947.             // adjust column to avoid hidden columns and rows
  948.             while( next.col < GetColumnCount() - 1)
  949.             {
  950.                 if( GetColumnWidth( next.col) > 0)
  951.                     break;
  952.                 next.col++;
  953.             }
  954.             while( next.row < GetRowCount() - 1)
  955.             {
  956.                 if( GetRowHeight( next.row) > 0)
  957.                     break;
  958.                 next.row++;
  959.             }
  960.             break;
  961.         
  962.     case VK_END:    
  963.         // End and Ctrl-End work more like Excel
  964.         //  and don't let user go to a hidden cell
  965.         if (IsCTRLpressed())
  966.         {
  967.             SendMessage(WM_VSCROLL, SB_BOTTOM, 0);
  968.             SendMessage(WM_HSCROLL, SB_RIGHT, 0);
  969.             next.row = GetRowCount() - 1;
  970.             next.col = GetColumnCount() - 1;
  971.         }
  972.         else
  973.         {
  974.             SendMessage(WM_HSCROLL, SB_RIGHT, 0);
  975.             next.col = GetColumnCount() - 1;
  976.         }
  977.         // adjust column to avoid hidden columns and rows
  978.         while( next.col > m_nFixedCols + 1)
  979.         {
  980.             if( GetColumnWidth( next.col) > 0)
  981.                 break;
  982.             next.col--;
  983.         }
  984.         while( next.row > m_nFixedRows + 1)
  985.         {
  986.             if( GetRowHeight( next.row) > 0)
  987.                 break;
  988.             next.row--;
  989.         }
  990.         break;
  991.         
  992.     case VK_F2:
  993.         OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, CPoint( -1, -1), VK_LBUTTON);
  994.         break;
  995.     default:
  996.         CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  997.         return;
  998.     }
  999.     if (next != m_idCurrentCell)
  1000.     {
  1001.         // While moving with the Cursorkeys the current ROW/CELL will get selected
  1002.         // OR Selection will get expanded when SHIFT is pressed
  1003.         // Cut n paste from OnLButtonDown - Franco Bez
  1004.         // Added check for NULL mouse mode - Chris Maunder.
  1005.         if (m_MouseMode == MOUSE_NOTHING)
  1006.         {
  1007.             m_PrevSelectedCellMap.RemoveAll();
  1008.             m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
  1009.             if (!IsSHIFTpressed() || nChar == VK_TAB)
  1010.                 m_SelectionStartCell = next;
  1011.             OnSelecting(next);
  1012.             m_MouseMode = MOUSE_NOTHING;
  1013.         }
  1014.         SetFocusCell(next);
  1015.         if (!IsCellVisible(next))
  1016.         {
  1017.             switch (nChar)
  1018.             {
  1019.             case VK_RIGHT:  
  1020.                 SendMessage(WM_HSCROLL, SB_LINERIGHT, 0); 
  1021.                 break;
  1022.                 
  1023.             case VK_LEFT:   
  1024.                 SendMessage(WM_HSCROLL, SB_LINELEFT, 0);  
  1025.                 break;
  1026.                 
  1027.             case VK_DOWN:   
  1028.                 SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);  
  1029.                 break;
  1030.                 
  1031.             case VK_UP:     
  1032.                 SendMessage(WM_VSCROLL, SB_LINEUP, 0);    
  1033.                 break;                
  1034.                 
  1035.             case VK_TAB:    
  1036.                 if (IsSHIFTpressed())
  1037.                 {
  1038.                     if (bChangeLine) 
  1039.                     {
  1040.                         SendMessage(WM_VSCROLL, SB_LINEUP, 0);
  1041.                         SetScrollPos32(SB_HORZ, m_nHScrollMax);
  1042.                         break;
  1043.                     }
  1044.                     else 
  1045.                         SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
  1046.                 }
  1047.                 else
  1048.                 {
  1049.                     if (bChangeLine) 
  1050.                     {
  1051.                         SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
  1052.                         SetScrollPos32(SB_HORZ, 0);
  1053.                         break;
  1054.                     }
  1055.                     else 
  1056.                         SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
  1057.                 }
  1058.                 break;
  1059.             }
  1060.             EnsureVisible(next); // Make sure cell is visible
  1061.             Invalidate();
  1062.         }
  1063.         EnsureVisible(next); // Make sure cell is visible
  1064.     }
  1065. }
  1066. void CGridCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  1067. {
  1068. #ifdef GRIDCONTROL_USE_TITLETIPS
  1069.     m_TitleTip.Hide();  // hide any titletips
  1070. #endif
  1071. CWnd::OnSysKeyDown(nChar, nRepCnt, nFlags);
  1072. }
  1073. // Instant editing of cells when keys are pressed
  1074. void CGridCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
  1075. {
  1076.     // EFW - BUG FIX
  1077.     if (!IsCTRLpressed() && m_MouseMode == MOUSE_NOTHING && nChar != VK_ESCAPE)
  1078.     {
  1079.         if (!m_bHandleTabKey || (m_bHandleTabKey && nChar != VK_TAB))
  1080.             OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, CPoint( -1, -1), nChar);
  1081.     }
  1082.     CWnd::OnChar(nChar, nRepCnt, nFlags);
  1083. }
  1084. // Callback from any CInPlaceEdits that ended. This just calls OnEndEditCell,
  1085. // refreshes the edited cell and moves onto next cell if the return character
  1086. // from the edit says we should.
  1087. void CGridCtrl::OnEndInPlaceEdit(NMHDR* pNMHDR, LRESULT* pResult)
  1088. {
  1089.     GV_DISPINFO *pgvDispInfo = (GV_DISPINFO *)pNMHDR;
  1090.     GV_ITEM     *pgvItem = &pgvDispInfo->item;
  1091.     // In case OnEndInPlaceEdit called as window is being destroyed
  1092.     if (!IsWindow(GetSafeHwnd()))
  1093.         return;
  1094.     OnEndEditCell(pgvItem->row, pgvItem->col, pgvItem->strText);
  1095.     //InvalidateCellRect(CCellID(pgvItem->row, pgvItem->col));
  1096.     SendMessageToParent(pgvItem->row, pgvItem->col, GVN_ENDLABELEDIT);
  1097.     switch (pgvItem->lParam)
  1098.     {
  1099.         case VK_TAB:
  1100.         case VK_DOWN:
  1101.         case VK_UP:
  1102.         case VK_RIGHT:
  1103.         case VK_LEFT:
  1104.         case VK_NEXT:
  1105.         case VK_PRIOR:
  1106.         case VK_HOME:
  1107.         case VK_END:
  1108.             OnKeyDown(pgvItem->lParam, 0, 0);
  1109.             OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, CPoint( -1, -1), pgvItem->lParam);
  1110.     }
  1111.     *pResult = 0;
  1112. }
  1113. // Handle horz scrollbar notifications
  1114. void CGridCtrl::OnHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
  1115. {
  1116.     EndEditing();
  1117. #ifndef GRIDCONTROL_NO_TITLETIPS
  1118.     m_TitleTip.Hide();  // hide any titletips
  1119. #endif
  1120.     int scrollPos = GetScrollPos32(SB_HORZ);
  1121.     CCellID idTopLeft = GetTopleftNonFixedCell();
  1122.     CRect rect;
  1123.     GetClientRect(rect);
  1124.     switch (nSBCode)
  1125.     {
  1126.     case SB_LINERIGHT:
  1127.         if (scrollPos < m_nHScrollMax)
  1128.         {
  1129.             // may have contiguous hidden columns.  Blow by them
  1130.             while (idTopLeft.col < (GetColumnCount()-1)
  1131.                     && GetColumnWidth( idTopLeft.col) < 1 )
  1132.             {
  1133.                 idTopLeft.col++;
  1134.             }
  1135.             int xScroll = GetColumnWidth(idTopLeft.col);
  1136.             SetScrollPos32(SB_HORZ, scrollPos + xScroll);
  1137.             if (GetScrollPos32(SB_HORZ) == scrollPos)
  1138.                 break;          // didn't work
  1139.             rect.left = GetFixedColumnWidth();
  1140.             //rect.left = GetFixedColumnWidth() + xScroll;
  1141.             //ScrollWindow(-xScroll, 0, rect);
  1142.             //rect.left = rect.right - xScroll;
  1143.             InvalidateRect(rect);
  1144.         }
  1145.         break;
  1146.     case SB_LINELEFT:
  1147.         if (scrollPos > 0 && idTopLeft.col > GetFixedColumnCount())
  1148.         {
  1149.             int iColToUse = idTopLeft.col-1;
  1150.             // may have contiguous hidden columns.  Blow by them
  1151.             while(  iColToUse > GetFixedColumnCount()
  1152.                     && GetColumnWidth( iColToUse) < 1 )
  1153.             {
  1154.                 iColToUse--;
  1155.             }
  1156.             int xScroll = GetColumnWidth(iColToUse);
  1157.             SetScrollPos32(SB_HORZ, max(0, scrollPos - xScroll));
  1158.             rect.left = GetFixedColumnWidth();
  1159.             //ScrollWindow(xScroll, 0, rect);
  1160.             //rect.right = rect.left + xScroll;
  1161.             InvalidateRect(rect);
  1162.         }
  1163.         break;
  1164.     case SB_PAGERIGHT:
  1165.         if (scrollPos < m_nHScrollMax)
  1166.         {
  1167.             rect.left = GetFixedColumnWidth();
  1168.             int offset = rect.Width();
  1169.             int pos = min(m_nHScrollMax, scrollPos + offset);
  1170.             SetScrollPos32(SB_HORZ, pos);
  1171.             rect.left = GetFixedColumnWidth();
  1172.             InvalidateRect(rect);
  1173.         }
  1174.         break;
  1175.         
  1176.     case SB_PAGELEFT:
  1177.         if (scrollPos > 0)
  1178.         {
  1179.             rect.left = GetFixedColumnWidth();
  1180.             int offset = -rect.Width();
  1181.             int pos = max(0, scrollPos + offset);
  1182.             SetScrollPos32(SB_HORZ, pos);
  1183.             rect.left = GetFixedColumnWidth();
  1184.             InvalidateRect(rect);
  1185.         }
  1186.         break;
  1187.         
  1188.     case SB_THUMBPOSITION:
  1189.     case SB_THUMBTRACK:
  1190.         {
  1191.             SetScrollPos32(SB_HORZ, GetScrollPos32(SB_HORZ, TRUE));
  1192.             m_idTopLeftCell.row = -1;
  1193.             CCellID idNewTopLeft = GetTopleftNonFixedCell();
  1194.             if (idNewTopLeft != idTopLeft)
  1195.             {
  1196.                 rect.left = GetFixedColumnWidth();
  1197.                 InvalidateRect(rect);
  1198.             }
  1199.         }
  1200.         break;
  1201.         
  1202.     case SB_LEFT:
  1203.         if (scrollPos > 0)
  1204.         {
  1205.             SetScrollPos32(SB_HORZ, 0);
  1206.             Invalidate();
  1207.         }
  1208.         break;
  1209.         
  1210.     case SB_RIGHT:
  1211.         if (scrollPos < m_nHScrollMax)
  1212.         {
  1213.             SetScrollPos32(SB_HORZ, m_nHScrollMax);
  1214.             Invalidate();
  1215.         }
  1216.         break;
  1217.         
  1218.         
  1219.     default: 
  1220.         break;
  1221.     }
  1222. }
  1223. // Handle vert scrollbar notifications
  1224. void CGridCtrl::OnVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
  1225. {
  1226.     EndEditing();
  1227. #ifndef GRIDCONTROL_NO_TITLETIPS
  1228.     m_TitleTip.Hide();  // hide any titletips
  1229. #endif
  1230.     // Get the scroll position ourselves to ensure we get a 32 bit value
  1231.     int scrollPos = GetScrollPos32(SB_VERT);
  1232.     CCellID idTopLeft = GetTopleftNonFixedCell();
  1233.     CRect rect;
  1234.     GetClientRect(rect);
  1235.     switch (nSBCode)
  1236.     {
  1237.     case SB_LINEDOWN:
  1238.         if (scrollPos < m_nVScrollMax)
  1239.         {
  1240.             // may have contiguous hidden rows.  Blow by them
  1241.             while(  idTopLeft.row < (GetRowCount()-1)
  1242.                     && GetRowHeight( idTopLeft.row) < 1 )
  1243.             {
  1244.                 idTopLeft.row++;
  1245.             }
  1246.             int yScroll = GetRowHeight(idTopLeft.row);
  1247.             SetScrollPos32(SB_VERT, scrollPos + yScroll);
  1248.             if (GetScrollPos32(SB_VERT) == scrollPos)
  1249.                 break;          // didn't work
  1250.             rect.top = GetFixedRowHeight();
  1251.             //rect.top = GetFixedRowHeight() + yScroll;
  1252.             //ScrollWindow(0, -yScroll, rect);
  1253.             //rect.top = rect.bottom - yScroll;
  1254.             InvalidateRect(rect);
  1255.         }
  1256.         break;
  1257.         
  1258.     case SB_LINEUP:
  1259.         if (scrollPos > 0 && idTopLeft.row > GetFixedRowCount())
  1260.         {
  1261.             int iRowToUse = idTopLeft.row-1;
  1262.             // may have contiguous hidden rows.  Blow by them
  1263.             while(  iRowToUse > GetFixedRowCount()
  1264.                     && GetRowHeight( iRowToUse) < 1 )
  1265.             {
  1266.                 iRowToUse--;
  1267.             }
  1268.             int yScroll = GetRowHeight( iRowToUse);
  1269.             SetScrollPos32(SB_VERT, max(0, scrollPos - yScroll));
  1270.             rect.top = GetFixedRowHeight();
  1271.             //ScrollWindow(0, yScroll, rect);
  1272.             //rect.bottom = rect.top + yScroll;
  1273.             InvalidateRect(rect);
  1274.         }
  1275.         break;
  1276.         
  1277.     case SB_PAGEDOWN:
  1278.         if (scrollPos < m_nVScrollMax)
  1279.         {
  1280.             rect.top = GetFixedRowHeight();
  1281.             scrollPos = min(m_nVScrollMax, scrollPos + rect.Height());
  1282.             SetScrollPos32(SB_VERT, scrollPos);
  1283.             rect.top = GetFixedRowHeight();
  1284.             InvalidateRect(rect);
  1285.         }
  1286.         break;
  1287.         
  1288.     case SB_PAGEUP:
  1289.         if (scrollPos > 0)
  1290.         {
  1291.             rect.top = GetFixedRowHeight();
  1292.             int offset = -rect.Height();
  1293.             int pos = max(0, scrollPos + offset);
  1294.             SetScrollPos32(SB_VERT, pos);
  1295.             rect.top = GetFixedRowHeight();
  1296.             InvalidateRect(rect);
  1297.         }
  1298.         break;
  1299.         
  1300.     case SB_THUMBPOSITION:
  1301.     case SB_THUMBTRACK:
  1302.         {
  1303.             SetScrollPos32(SB_VERT, GetScrollPos32(SB_VERT, TRUE));
  1304.             m_idTopLeftCell.row = -1;
  1305.             CCellID idNewTopLeft = GetTopleftNonFixedCell();
  1306.             if (idNewTopLeft != idTopLeft)
  1307.             {
  1308.                 rect.top = GetFixedRowHeight();
  1309.                 InvalidateRect(rect);
  1310.             }
  1311.         }
  1312.         break;
  1313.         
  1314.     case SB_TOP:
  1315.         if (scrollPos > 0)
  1316.         {
  1317.             SetScrollPos32(SB_VERT, 0);
  1318.             Invalidate();
  1319.         }
  1320.         break;
  1321.         
  1322.     case SB_BOTTOM:
  1323.         if (scrollPos < m_nVScrollMax)
  1324.         {
  1325.             SetScrollPos32(SB_VERT, m_nVScrollMax);
  1326.             Invalidate();
  1327.         }
  1328.         
  1329.     default: 
  1330.         break;
  1331.     }
  1332. }
  1333. /////////////////////////////////////////////////////////////////////////////
  1334. // CGridCtrl implementation functions
  1335. void CGridCtrl::OnDraw(CDC* pDC)
  1336. {
  1337.     if (!m_bAllowDraw)
  1338.         return;
  1339.     CRect clipRect;
  1340.     if (pDC->GetClipBox(&clipRect) == ERROR)
  1341.         return;
  1342.     EraseBkgnd(pDC);            // OnEraseBkgnd does nothing, so erase bkgnd here.
  1343.     // This necessary since we may be using a Memory DC.
  1344.     CRect rect;
  1345.     int row, col;
  1346.     CGridCellBase* pCell;
  1347.     int nFixedRowHeight = GetFixedRowHeight();
  1348.     int nFixedColWidth  = GetFixedColumnWidth();
  1349.     CCellID idTopLeft = GetTopleftNonFixedCell();
  1350.     int minVisibleRow = idTopLeft.row,
  1351.         minVisibleCol = idTopLeft.col;
  1352.     CRect VisRect;
  1353.     CCellRange VisCellRange = GetVisibleNonFixedCellRange(VisRect);
  1354.     int maxVisibleRow = VisCellRange.GetMaxRow(),
  1355.         maxVisibleCol = VisCellRange.GetMaxCol();
  1356.     if (GetVirtualMode())
  1357.         SendCacheHintToParent(VisCellRange);
  1358.     // draw top-left cells 0..m_nFixedRows-1, 0..m_nFixedCols-1
  1359.     rect.bottom = -1;
  1360.     for (row = 0; row < m_nFixedRows; row++)
  1361.     {
  1362.         rect.top = rect.bottom+1;
  1363.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1364.         rect.right = -1;
  1365.         for (col = 0; col < m_nFixedCols; col++)
  1366.         {
  1367.             rect.left = rect.right+1;
  1368.             rect.right = rect.left + GetColumnWidth(col)-1;
  1369.             pCell = GetCell(row, col);
  1370.             if (pCell)
  1371.                 pCell->Draw(pDC, row, col, rect, FALSE);
  1372.         }
  1373.     }
  1374.     // draw fixed column cells:  m_nFixedRows..n, 0..m_nFixedCols-1
  1375.     rect.bottom = nFixedRowHeight-1;
  1376.     for (row = minVisibleRow; row <= maxVisibleRow; row++)
  1377.     {
  1378.         rect.top = rect.bottom+1;
  1379.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1380.         // rect.bottom = bottom pixel of previous row
  1381.         if (rect.top > clipRect.bottom)
  1382.             break;                // Gone past cliprect
  1383.         if (rect.bottom < clipRect.top)
  1384.             continue;             // Reached cliprect yet?
  1385.         rect.right = -1;
  1386.         for (col = 0; col < m_nFixedCols; col++)
  1387.         {
  1388.             rect.left = rect.right+1;
  1389.             rect.right = rect.left + GetColumnWidth(col)-1;
  1390.             if (rect.left > clipRect.right)
  1391.                 break;            // gone past cliprect
  1392.             if (rect.right < clipRect.left)
  1393.                 continue;         // Reached cliprect yet?
  1394.             pCell = GetCell(row, col);
  1395.             if (pCell)
  1396.                 pCell->Draw(pDC, row, col, rect, FALSE);
  1397.         }
  1398.     }
  1399.     // draw fixed row cells  0..m_nFixedRows, m_nFixedCols..n
  1400.     rect.bottom = -1;
  1401.     for (row = 0; row < m_nFixedRows; row++)
  1402.     {
  1403.         rect.top = rect.bottom+1;
  1404.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1405.         // rect.bottom = bottom pixel of previous row
  1406.         if (rect.top > clipRect.bottom)
  1407.             break;                // Gone past cliprect
  1408.         if (rect.bottom < clipRect.top)
  1409.             continue;             // Reached cliprect yet?
  1410.         rect.right = nFixedColWidth-1;
  1411.         for (col = minVisibleCol; col <= maxVisibleCol; col++)
  1412.         {
  1413.             rect.left = rect.right+1;
  1414.             rect.right = rect.left + GetColumnWidth(col)-1;
  1415.             if (rect.left > clipRect.right)
  1416.                 break;        // gone past cliprect
  1417.             if (rect.right < clipRect.left)
  1418.                 continue;     // Reached cliprect yet?
  1419.             pCell = GetCell(row, col);
  1420.             if (pCell)
  1421.                 pCell->Draw(pDC, row, col, rect, FALSE);
  1422.         }
  1423.     }
  1424.     // draw rest of non-fixed cells
  1425.     rect.bottom = nFixedRowHeight-1;
  1426.     for (row = minVisibleRow; row <= maxVisibleRow; row++)
  1427.     {
  1428.         rect.top = rect.bottom+1;
  1429.         rect.bottom = rect.top + GetRowHeight(row)-1;
  1430.         // rect.bottom = bottom pixel of previous row
  1431.         if (rect.top > clipRect.bottom)
  1432.             break;                // Gone past cliprect
  1433.         if (rect.bottom < clipRect.top)
  1434.             continue;             // Reached cliprect yet?
  1435.         rect.right = nFixedColWidth-1;
  1436.         for (col = minVisibleCol; col <= maxVisibleCol; col++)
  1437.         {
  1438.             rect.left = rect.right+1;
  1439.             rect.right = rect.left + GetColumnWidth(col)-1;
  1440.             if (rect.left > clipRect.right)
  1441.                 break;        // gone past cliprect
  1442.             if (rect.right < clipRect.left)
  1443.                 continue;     // Reached cliprect yet?
  1444.             pCell = GetCell(row, col);
  1445.             // TRACE(_T("Cell %d,%d type: %sn"), row, col, pCell->GetRuntimeClass()->m_lpszClassName);
  1446.             if (pCell)
  1447.                 pCell->Draw(pDC, row, col, rect, FALSE);
  1448.         }
  1449.     }
  1450.     CPen pen;
  1451.     pen.CreatePen(PS_SOLID, 0, m_crGridLineColour);
  1452.     pDC->SelectObject(&pen);
  1453.     // draw vertical lines (drawn at ends of cells)
  1454.     if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
  1455.     {
  1456.         int x = nFixedColWidth;
  1457.         for (col = minVisibleCol; col <= maxVisibleCol; col++)
  1458.         {
  1459.             x += GetColumnWidth(col);
  1460.             pDC->MoveTo(x-1, nFixedRowHeight);
  1461.             pDC->LineTo(x-1, VisRect.bottom);
  1462.         }
  1463.     }
  1464.     // draw horizontal lines (drawn at bottom of each cell)
  1465.     if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
  1466.     {
  1467.         int y = nFixedRowHeight;
  1468.         for (row = minVisibleRow; row <= maxVisibleRow; row++)
  1469.         {
  1470.             y += GetRowHeight(row);
  1471.             pDC->MoveTo(nFixedColWidth, y-1);
  1472.             pDC->LineTo(VisRect.right,  y-1);
  1473.         }
  1474.     }
  1475.     pDC->SelectStockObject(NULL_PEN);
  1476. }
  1477. ////////////////////////////////////////////////////////////////////////////////////////
  1478. // CGridCtrl Cell selection stuff
  1479. // Is a given cell designation valid (ie within the bounds of our number
  1480. // of columns/rows)?
  1481. BOOL CGridCtrl::IsValid(int nRow, int nCol) const
  1482. {
  1483.     return (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols);
  1484. }
  1485. BOOL CGridCtrl::IsValid(const CCellID& cell) const
  1486. {
  1487.     return IsValid(cell.row, cell.col);
  1488. }
  1489. // Is a given cell range valid (ie within the bounds of our number
  1490. // of columns/rows)?
  1491. BOOL CGridCtrl::IsValid(const CCellRange& range) const
  1492. {
  1493.     return (range.GetMinRow() >= 0 && range.GetMinCol() >= 0 &&
  1494.         range.GetMaxRow() >= 0 && range.GetMaxCol() >= 0 &&
  1495.         range.GetMaxRow() < m_nRows && range.GetMaxCol() < m_nCols &&
  1496.         range.GetMinRow() <= range.GetMaxRow() && range.GetMinCol() <= range.GetMaxCol());
  1497. }
  1498. // Enables/Disables redraw for certain operations like columns auto-sizing etc,
  1499. // but not for user caused things such as selection changes.
  1500. void CGridCtrl::SetRedraw(BOOL bAllowDraw, BOOL bResetScrollBars /* = FALSE */)
  1501. {
  1502. //    TRACE(_T("%s: Setting redraw to %sn"),
  1503. //             GetRuntimeClass()->m_lpszClassName, bAllowDraw? _T("TRUE") : _T("FALSE"));
  1504.     if (bAllowDraw && !m_bAllowDraw)
  1505.     {
  1506.         m_bAllowDraw = TRUE;
  1507.         Refresh();
  1508.     }
  1509.     if (bResetScrollBars)
  1510.         ResetScrollBars();
  1511. }
  1512. // Forces a redraw of a cell immediately (using a direct DC construction,
  1513. // or the supplied dc)
  1514. BOOL CGridCtrl::RedrawCell(const CCellID& cell, CDC* pDC /* = NULL */)
  1515. {
  1516.     return RedrawCell(cell.row, cell.col, pDC);
  1517. }
  1518. BOOL CGridCtrl::RedrawCell(int nRow, int nCol, CDC* pDC /* = NULL */)
  1519. {
  1520.     BOOL bResult = TRUE;
  1521.     BOOL bMustReleaseDC = FALSE;
  1522.     if (!m_bAllowDraw || !IsCellVisible(nRow, nCol))
  1523.         return FALSE;
  1524.     CRect rect;
  1525.     if (!GetCellRect(nRow, nCol, rect))
  1526.         return FALSE;
  1527.     if (!pDC)
  1528.     {
  1529.         pDC = GetDC();
  1530.         if (pDC)
  1531.             bMustReleaseDC = TRUE;
  1532.     }
  1533.     if (pDC)
  1534.     {
  1535.         // Redraw cells directly
  1536.         if (nRow < m_nFixedRows || nCol < m_nFixedCols)
  1537.         {
  1538.             CGridCellBase* pCell = GetCell(nRow, nCol);
  1539.             if (pCell)
  1540.                 bResult = pCell->Draw(pDC, nRow, nCol, rect, TRUE);
  1541.         }
  1542.         else
  1543.         {
  1544.             CGridCellBase* pCell = GetCell(nRow, nCol);
  1545.             if (pCell)
  1546.                 bResult = pCell->Draw(pDC, nRow, nCol, rect, TRUE);
  1547.             // Since we have erased the background, we will need to redraw the gridlines
  1548.             CPen pen;
  1549.             pen.CreatePen(PS_SOLID, 0, m_crGridLineColour);
  1550.             CPen* pOldPen = (CPen*) pDC->SelectObject(&pen);
  1551.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
  1552.             {
  1553.                 pDC->MoveTo(rect.left,    rect.bottom);
  1554.                 pDC->LineTo(rect.right + 1, rect.bottom);
  1555.             }
  1556.             if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
  1557.             {
  1558.                 pDC->MoveTo(rect.right, rect.top);
  1559.                 pDC->LineTo(rect.right, rect.bottom + 1);
  1560.             }
  1561.             pDC->SelectObject(pOldPen);
  1562.         }
  1563.     } else
  1564.         InvalidateRect(rect, TRUE);     // Could not get a DC - invalidate it anyway
  1565.     // and hope that OnPaint manages to get one
  1566.     if (bMustReleaseDC)
  1567.         ReleaseDC(pDC);
  1568.     return bResult;
  1569. }
  1570. // redraw a complete row
  1571. BOOL CGridCtrl::RedrawRow(int row)
  1572. {
  1573.     BOOL bResult = TRUE;
  1574.     CDC* pDC = GetDC();
  1575.     for (int col = 0; col < GetColumnCount(); col++)
  1576.         bResult = RedrawCell(row, col, pDC) && bResult;
  1577.     if (pDC)
  1578.         ReleaseDC(pDC);
  1579.     return bResult;
  1580. }
  1581. // redraw a complete column
  1582. BOOL CGridCtrl::RedrawColumn(int col)
  1583. {
  1584.     BOOL bResult = TRUE;
  1585.     CDC* pDC = GetDC();
  1586.     for (int row = 0; row < GetRowCount(); row++)
  1587.         bResult = RedrawCell(row, col, pDC) && bResult;
  1588.     if (pDC)
  1589.         ReleaseDC(pDC);
  1590.     return bResult;
  1591. }
  1592. // Sets the currently selected cell, returning the previous current cell
  1593. CCellID CGridCtrl::SetFocusCell(int nRow, int nCol)
  1594. {
  1595.     return SetFocusCell(CCellID(nRow, nCol));
  1596. }
  1597. CCellID CGridCtrl::SetFocusCell(CCellID cell)
  1598. {
  1599.     if (cell == m_idCurrentCell)
  1600.         return m_idCurrentCell;
  1601.     CCellID idPrev = m_idCurrentCell;
  1602.     // EFW - Bug Fix - Force focus to be in a non-fixed cell
  1603.     if(cell.row != -1 && cell.row < GetFixedRowCount())
  1604.         cell.row = GetFixedRowCount();
  1605.     if(cell.col != -1 && cell.col < GetFixedColumnCount())
  1606.         cell.col = GetFixedColumnCount();
  1607.     m_idCurrentCell = cell;
  1608.     if (IsValid(idPrev))
  1609.     {
  1610.         SendMessageToParent(idPrev.row, idPrev.col, GVN_SELCHANGING);
  1611.         SetItemState(idPrev.row, idPrev.col,
  1612.             GetItemState(idPrev.row, idPrev.col) & ~GVIS_FOCUSED);
  1613.         RedrawCell(idPrev); // comment to reduce flicker
  1614.         if (GetTrackFocusCell() && idPrev.col != m_idCurrentCell.col)
  1615.             for (int row = 0; row < m_nFixedRows; row++)
  1616.                 RedrawCell(row, idPrev.col);
  1617.         if (GetTrackFocusCell() && idPrev.row != m_idCurrentCell.row)
  1618.             for (int col = 0; col < m_nFixedCols; col++)
  1619.                 RedrawCell(idPrev.row, col);
  1620.     }
  1621.     if (IsValid(m_idCurrentCell))
  1622.     {
  1623.         SetItemState(m_idCurrentCell.row, m_idCurrentCell.col,
  1624.             GetItemState(m_idCurrentCell.row, m_idCurrentCell.col) | GVIS_FOCUSED);
  1625.         RedrawCell(m_idCurrentCell); // comment to reduce flicker
  1626.         if (GetTrackFocusCell() && idPrev.col != m_idCurrentCell.col)
  1627.             for (int row = 0; row < m_nFixedRows; row++)
  1628.                 RedrawCell(row, m_idCurrentCell.col);
  1629.         if (GetTrackFocusCell() && idPrev.row != m_idCurrentCell.row)
  1630.             for (int col = 0; col < m_nFixedCols; col++)
  1631.                 RedrawCell(m_idCurrentCell.row, col);
  1632.         SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_SELCHANGED);
  1633.         // EFW - New addition.  If in list mode, make sure the selected
  1634.         // row highlight follows the cursor.
  1635.         // Removed by C Maunder 27 May
  1636.         //if (m_bListMode)
  1637.         //{
  1638.         //    m_PrevSelectedCellMap.RemoveAll();
  1639.         //    m_MouseMode = MOUSE_SELECT_ROW;
  1640.         //    OnSelecting(m_idCurrentCell);
  1641.             // Leave this off so that you can still drag the highlight around
  1642.             // without selecting rows.
  1643.             // m_MouseMode = MOUSE_NOTHING;
  1644.         //}
  1645.     }
  1646.     return idPrev;
  1647. }
  1648. // Sets the range of currently selected cells
  1649. void CGridCtrl::SetSelectedRange(const CCellRange& Range,
  1650.                                  BOOL bForceRepaint /* = FALSE */, BOOL bSelectCells/*=TRUE*/)
  1651. {
  1652.     SetSelectedRange(Range.GetMinRow(), Range.GetMinCol(),
  1653.                      Range.GetMaxRow(), Range.GetMaxCol(),
  1654.                      bForceRepaint, bSelectCells);
  1655. }
  1656. void CGridCtrl::SetSelectedRange(int nMinRow, int nMinCol, int nMaxRow, int nMaxCol,
  1657.                                  BOOL bForceRepaint /* = FALSE */, BOOL bSelectCells/*=TRUE*/)
  1658. {
  1659.     if (!m_bEnableSelection)
  1660.         return;
  1661.     CDC* pDC = NULL;
  1662.     if (bForceRepaint)
  1663.         pDC = GetDC();
  1664.     // EFW - Bug fix - Don't allow selection of fixed rows
  1665.     if(nMinRow >= 0 && nMinRow < GetFixedRowCount())
  1666.         nMinRow = GetFixedRowCount();
  1667.     if(nMaxRow >= 0 && nMaxRow < GetFixedRowCount())
  1668.         nMaxRow = GetFixedRowCount();
  1669.     if(nMinCol >= 0 && nMinCol < GetFixedColumnCount())
  1670.         nMinCol = GetFixedColumnCount();
  1671.     if(nMaxCol >= 0 && nMaxCol < GetFixedColumnCount())
  1672.         nMaxCol = GetFixedColumnCount();
  1673.     // If we are selecting cells, then first clear out the list of currently selected cells, then
  1674.     if (bSelectCells)
  1675.     {
  1676.        // Unselect all previously selected cells
  1677.         for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  1678.         {
  1679.             DWORD key;
  1680.             CCellID cell;
  1681.             m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1682.             // Reset the selection flag on the cell
  1683.             if (IsValid(cell))
  1684.             {
  1685.                 // This will remove the cell from the m_SelectedCellMap map
  1686.                 SetItemState(cell.row, cell.col,
  1687.                     GetItemState(cell.row, cell.col) & ~GVIS_SELECTED);
  1688.                 // If this is to be reselected, continue on past the redraw
  1689.                 if (nMinRow <= cell.row && cell.row <= nMaxRow &&
  1690.                     nMinCol <= cell.col && cell.col <= nMaxCol)
  1691.                     continue;
  1692.                 if (bForceRepaint && pDC)                    // Redraw NOW
  1693.                     RedrawCell(cell.row, cell.col, pDC);
  1694.                 else
  1695.                     InvalidateCellRect(cell);                // Redraw at leisure
  1696.             }
  1697.             else
  1698.             {
  1699.                 m_SelectedCellMap.RemoveKey( key);  // if it's not valid, get rid of it!
  1700.             }
  1701.         }
  1702.         // if we are selecting cells, and there are previous selected cells to be retained 
  1703.         // (eg Ctrl is being held down) then copy them to the newly created list, and mark 
  1704.         // all these cells as selected
  1705.         // Note that if we are list mode, single row selection, the we won't be adding 
  1706.         // the previous cells. Only the current row of cells will be added (see below)
  1707.         if (!GetSingleRowSelection() &&
  1708.             nMinRow >= 0 && nMinCol >= 0 && nMaxRow >= 0 && nMaxCol >= 0)
  1709.         {
  1710.             for (pos = m_PrevSelectedCellMap.GetStartPosition(); pos != NULL; /* nothing */)
  1711.             {
  1712.                 DWORD key;
  1713.                 CCellID cell;
  1714.                 m_PrevSelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1715.                 if (!IsValid(cell))
  1716.                     continue;
  1717.                 int nState = GetItemState(cell.row, cell.col);
  1718.                 // Set state as Selected. This will add the cell to m_SelectedCellMap
  1719.                 SetItemState(cell.row, cell.col, nState | GVIS_SELECTED);
  1720.                 // Redraw (immediately or at leisure)
  1721.                 if (bForceRepaint && pDC)
  1722.                     RedrawCell(cell.row, cell.col, pDC);
  1723.                 else
  1724.                     InvalidateCellRect(cell);
  1725.             }
  1726.         }
  1727.     }
  1728.     // Now select/deselect all cells in the cell range specified. If selecting, and the cell 
  1729.     // has already been marked as selected (above) then ignore it. If we are deselecting and
  1730.     // the cell isn't selected, then ignore
  1731.     if (nMinRow >= 0 && nMinCol >= 0 && nMaxRow >= 0 && nMaxCol >= 0 &&
  1732.         nMaxRow < m_nRows && nMaxCol < m_nCols &&
  1733.         nMinRow <= nMaxRow && nMinCol <= nMaxCol)
  1734.     {
  1735.         for (int row = nMinRow; row <= nMaxRow; row++)
  1736.             for (int col = nMinCol; col <= nMaxCol; col++)
  1737.             {
  1738.                 int nState = GetItemState(row, col);
  1739.                 
  1740.                 // Why is there no XOR operator in C??? We have bitwise XOR ^, but what about ^^??
  1741.                 if ( (bSelectCells && (nState & GVIS_SELECTED)) ||
  1742.                      (!bSelectCells && !(nState & GVIS_SELECTED)) )
  1743.                     continue;    // Already selected or deselected - ignore
  1744.                 // Set the selected state. This will add/remove the cell to m_SelectedCellMap
  1745.                 if (bSelectCells)
  1746.                     SetItemState(row, col, nState | GVIS_SELECTED);
  1747.                 else
  1748.                     SetItemState(row, col, GetItemState(row, col) & ~GVIS_SELECTED);
  1749.                 // Redraw (immediately or at leisure)
  1750.                 if (bForceRepaint && pDC)
  1751.                     RedrawCell(row, col, pDC);
  1752.                 else
  1753.                     InvalidateCellRect(row, col);
  1754.             }
  1755.     }
  1756.     //    TRACE(_T("%d cells selected.n"), m_SelectedCellMap.GetCount());
  1757.     if (pDC != NULL)
  1758.         ReleaseDC(pDC);
  1759. }
  1760. // selects all cells
  1761. void CGridCtrl::SelectAllCells()
  1762. {
  1763.     if (!m_bEnableSelection)
  1764.         return;
  1765.     SetSelectedRange(m_nFixedRows, m_nFixedCols, GetRowCount()-1, GetColumnCount()-1);
  1766. }
  1767. // selects columns
  1768. void CGridCtrl::SelectColumns(CCellID currentCell, 
  1769.                               BOOL bForceRedraw /*=FALSE*/, BOOL bSelectCells /*=TRUE*/)
  1770. {
  1771.     if (!m_bEnableSelection)
  1772.         return;
  1773.     //if (currentCell.col == m_idCurrentCell.col) return;
  1774.     if (currentCell.col < m_nFixedCols)
  1775.         return;
  1776.     if (!IsValid(currentCell))
  1777.         return;
  1778.     if (GetSingleColSelection())
  1779.         SetSelectedRange(GetFixedRowCount(), currentCell.col,
  1780.                          GetRowCount()-1,    currentCell.col,
  1781.                          bForceRedraw, bSelectCells);
  1782.     else
  1783.         SetSelectedRange(GetFixedRowCount(),
  1784.                          min(m_SelectionStartCell.col, currentCell.col),
  1785.                          GetRowCount()-1,
  1786.                          max(m_SelectionStartCell.col, currentCell.col),
  1787.                          bForceRedraw, bSelectCells);
  1788. }
  1789. // selects rows
  1790. void CGridCtrl::SelectRows(CCellID currentCell, 
  1791.                            BOOL bForceRedraw /*=FALSE*/, BOOL bSelectCells /*=TRUE*/)
  1792. {
  1793.     if (!m_bEnableSelection)
  1794.         return;
  1795.     //if (currentCell.row; == m_idCurrentCell.row) return;
  1796.     if (currentCell.row < m_nFixedRows)
  1797.         return;
  1798.     if (!IsValid(currentCell))
  1799.         return;
  1800.     if (GetSingleRowSelection())
  1801.         SetSelectedRange(currentCell.row, GetFixedColumnCount(),
  1802.                          currentCell.row, GetColumnCount()-1, 
  1803.                          bForceRedraw, bSelectCells);
  1804.     else
  1805.         SetSelectedRange(min(m_SelectionStartCell.row, currentCell.row),
  1806.                          GetFixedColumnCount(),
  1807.                          max(m_SelectionStartCell.row, currentCell.row),
  1808.                          GetColumnCount()-1,
  1809.                          bForceRedraw, bSelectCells);
  1810. }
  1811. // selects cells
  1812. void CGridCtrl::SelectCells(CCellID currentCell, 
  1813.                             BOOL bForceRedraw /*=FALSE*/, BOOL bSelectCells /*=TRUE*/)
  1814. {
  1815.     if (!m_bEnableSelection)
  1816.         return;
  1817.     int row = currentCell.row;
  1818.     int col = currentCell.col;
  1819.     if (row < m_nFixedRows || col < m_nFixedCols)
  1820.         return;
  1821.     if (!IsValid(currentCell))
  1822.         return;
  1823.     // Prevent unnecessary redraws
  1824.     //if (currentCell == m_LeftClickDownCell)  return;
  1825.     //else if (currentCell == m_idCurrentCell) return;
  1826.     SetSelectedRange(min(m_SelectionStartCell.row, row),
  1827.                      min(m_SelectionStartCell.col, col),
  1828.                      max(m_SelectionStartCell.row, row),
  1829.                      max(m_SelectionStartCell.col, col),
  1830.                      bForceRedraw, bSelectCells);
  1831. }
  1832. // Called when mouse/keyboard selection is a-happening.
  1833. void CGridCtrl::OnSelecting(const CCellID& currentCell)
  1834. {
  1835.     if (!m_bEnableSelection)
  1836.         return;
  1837.     switch (m_MouseMode)
  1838.     {
  1839.     case MOUSE_SELECT_ALL:
  1840.         SelectAllCells();
  1841.         break;
  1842.     case MOUSE_SELECT_COL:
  1843.         SelectColumns(currentCell, FALSE);
  1844.         break;
  1845.     case MOUSE_SELECT_ROW:
  1846.         SelectRows(currentCell, FALSE);
  1847.         break;
  1848.     case MOUSE_SELECT_CELLS:
  1849.         SelectCells(currentCell, FALSE);
  1850.         break;
  1851.     }
  1852.     // EFW - Bug fix [REMOVED CJM: this will cause infinite loop in list mode]
  1853.     // SetFocusCell(max(currentCell.row, m_nFixedRows), max(currentCell.col, m_nFixedCols));
  1854. }
  1855. #ifndef GRIDCONTROL_NO_CLIPBOARD
  1856. ////////////////////////////////////////////////////////////////////////////////////////
  1857. // Clipboard functions
  1858. // Deletes the contents from the selected cells
  1859. void CGridCtrl::CutSelectedText()
  1860. {
  1861.     if (!IsEditable())
  1862.         return;
  1863.     // Clear contents of selected cells.
  1864.     for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  1865.     {
  1866.         DWORD key;
  1867.         CCellID cell;
  1868.         m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  1869.         if (!IsCellEditable(cell))
  1870.             continue;
  1871.         CGridCellBase* pCell = GetCell(cell.row, cell.col);
  1872.         if (pCell)
  1873.         {
  1874.             // don't clear hidden cells
  1875.             if( m_arRowHeights[ cell.row] > 0
  1876.                 && m_arColWidths[cell.col] > 0 )
  1877.             {
  1878.                 SendMessageToParent(cell.row, cell.col, GVN_BEGINLABELEDIT);
  1879.                 pCell->SetText(_T(""));
  1880.                 SetModified(TRUE, cell.row, cell.col);
  1881.         SendMessageToParent(cell.row, cell.col, GVN_ENDLABELEDIT);
  1882.             }
  1883.         }
  1884.     }
  1885.     Refresh();
  1886. }
  1887. // Copies text from the selected cells to the clipboard
  1888. COleDataSource* CGridCtrl::CopyTextFromGrid()
  1889. {
  1890.     USES_CONVERSION;
  1891.     CCellRange Selection = GetSelectedCellRange();
  1892.     if (!IsValid(Selection))
  1893.         return NULL;
  1894.     // Write to shared file (REMEBER: CF_TEXT is ANSI, not UNICODE, so we need to convert)
  1895.     CSharedFile sf(GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT);
  1896.     // Get a tab delimited string to copy to cache
  1897.     CString str;
  1898.     CGridCellBase *pCell;
  1899.     for (int row = Selection.GetMinRow(); row <= Selection.GetMaxRow(); row++)
  1900.     {
  1901.         // don't copy hidden cells
  1902.         if( m_arRowHeights[row] <= 0 )
  1903.             continue;
  1904.         str.Empty();
  1905.         for (int col = Selection.GetMinCol(); col <= Selection.GetMaxCol(); col++)
  1906.         {
  1907.             // don't copy hidden cells
  1908.             if( m_arColWidths[col] <= 0 )
  1909.                 continue;
  1910.             pCell = GetCell(row, col);
  1911.             if (pCell &&(pCell->GetState() & GVIS_SELECTED))
  1912.             {
  1913.                 // if (!pCell->GetText())
  1914.                 //    str += _T(" ");
  1915.                 // else 
  1916.                 str += pCell->GetText();
  1917.             }
  1918.             if (col != Selection.GetMaxCol()) 
  1919.                 str += _T("t");
  1920.         }
  1921.         if (row != Selection.GetMaxRow()) 
  1922.             str += _T("n");
  1923.         
  1924.         sf.Write(T2A(str.GetBuffer(1)), str.GetLength());
  1925.         str.ReleaseBuffer();
  1926.     }
  1927.     
  1928.     char c = '';
  1929.     sf.Write(&c, 1);
  1930.     DWORD dwLen = sf.GetLength();
  1931.     HGLOBAL hMem = sf.Detach();
  1932.     if (!hMem)
  1933.         return NULL;
  1934.     hMem = ::GlobalReAlloc(hMem, dwLen, GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT);
  1935.     if (!hMem)
  1936.         return NULL;
  1937.     // Cache data
  1938.     COleDataSource* pSource = new COleDataSource();
  1939.     pSource->CacheGlobalData(CF_TEXT, hMem);
  1940.     return pSource;
  1941. }
  1942. // Pastes text from the clipboard to the selected cells
  1943. BOOL CGridCtrl::PasteTextToGrid(CCellID cell, COleDataObject* pDataObject)
  1944. {
  1945.     if (!IsValid(cell) || !IsCellEditable(cell) || !pDataObject->IsDataAvailable(CF_TEXT))
  1946.         return FALSE;
  1947.     // Get the text from the COleDataObject
  1948.     HGLOBAL hmem = pDataObject->GetGlobalData(CF_TEXT);
  1949.     CMemFile sf((BYTE*) ::GlobalLock(hmem), ::GlobalSize(hmem));
  1950.     // CF_TEXT is ANSI text, so we need to allocate a char* buffer
  1951.     // to hold this.
  1952.     LPSTR szBuffer = new char[::GlobalSize(hmem)];
  1953.     if (!szBuffer)
  1954.         return FALSE;
  1955.     sf.Read(szBuffer, ::GlobalSize(hmem));
  1956.     ::GlobalUnlock(hmem);
  1957.     // Now store in generic TCHAR form so we no longer have to deal with
  1958.     // ANSI/UNICODE problems
  1959.     CString strText = szBuffer;
  1960.     delete szBuffer;
  1961.     // Parse text data and set in cells...
  1962.     strText.LockBuffer();
  1963.     CString strLine = strText;
  1964.     int nLine = 0;
  1965.     // Find the end of the first line
  1966.     int nIndex;
  1967.     do
  1968.     {
  1969.         int nColumn = 0;
  1970.         nIndex = strLine.Find(_T("n"));
  1971.         // Store the remaining chars after the newline
  1972.         CString strNext = (nIndex < 0)? _T("")  : strLine.Mid(nIndex + 1);
  1973.         // Remove all chars after the newline
  1974.         if (nIndex >= 0)
  1975.             strLine = strLine.Left(nIndex);
  1976.         // Make blank entries a "space"
  1977.         if (strLine.IsEmpty() && nIndex >= 0)
  1978.             strLine = _T(" ");
  1979.         LPTSTR szLine = strLine.GetBuffer(1);
  1980.         // Break the current line into tokens (tab or comma delimited)
  1981.         LPTSTR pszCellText = _tcstok(szLine, _T("t,n"));
  1982.         // skip hidden rows
  1983.         int iRowVis = cell.row + nLine;
  1984.         while( iRowVis < GetRowCount())
  1985.         {
  1986.             if( GetRowHeight( iRowVis) > 0)
  1987.                 break;
  1988.             nLine++;
  1989.             iRowVis++;
  1990.         }
  1991.         while (pszCellText != NULL)
  1992.         {
  1993.             // skip hidden columns
  1994.             int iColVis = cell.col + nColumn;
  1995.             while( iColVis < GetColumnCount())
  1996.             {
  1997.                 if( GetColumnWidth( iColVis) > 0)
  1998.                     break;
  1999.                 nColumn++;
  2000.                 iColVis++;
  2001.             }
  2002.             CCellID TargetCell(iRowVis, iColVis);
  2003.             if (IsValid(TargetCell))
  2004.             {
  2005.                 CString strCellText = pszCellText;
  2006.                 strCellText.TrimLeft();
  2007.                 strCellText.TrimRight();
  2008.                 SendMessageToParent(TargetCell.row, TargetCell.col, GVN_BEGINLABELEDIT);
  2009.                 SetItemText(TargetCell.row, TargetCell.col, strCellText);
  2010.                 SetModified(TRUE, TargetCell.row, TargetCell.col);
  2011.                 SendMessageToParent(TargetCell.row, TargetCell.col, GVN_ENDLABELEDIT);
  2012.                 // Make sure cell is not selected to avoid data loss
  2013.                 SetItemState(TargetCell.row, TargetCell.col,
  2014.                     GetItemState(TargetCell.row, TargetCell.col) & ~GVIS_SELECTED);
  2015.             }
  2016.             pszCellText = _tcstok(NULL, _T("t,n"));
  2017.             nColumn++;
  2018.         }
  2019.         strLine.ReleaseBuffer();
  2020.         strLine = strNext;
  2021.         nLine++;
  2022.     } while (nIndex >= 0);
  2023.     strText.UnlockBuffer();
  2024.     Refresh();
  2025.     return TRUE;
  2026. }
  2027. #endif
  2028. #ifndef GRIDCONTROL_NO_DRAGDROP
  2029. // Start drag n drop
  2030. void CGridCtrl::OnBeginDrag()
  2031. {
  2032.     if (!m_bAllowDragAndDrop)
  2033.         return;
  2034.     COleDataSource* pSource = CopyTextFromGrid();
  2035.     if (pSource)
  2036.     {
  2037.         SendMessageToParent(GetSelectedCellRange().GetTopLeft().row,
  2038.             GetSelectedCellRange().GetTopLeft().col,
  2039.             GVN_BEGINDRAG);
  2040.         m_MouseMode = MOUSE_DRAGGING;
  2041.         DROPEFFECT dropEffect = pSource->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE);
  2042.         if (dropEffect & DROPEFFECT_MOVE)
  2043.             CutSelectedText();
  2044.         if (pSource)
  2045.             delete pSource;    // Did not pass source to clipboard, so must delete
  2046.     }
  2047. }
  2048. // Handle drag over grid
  2049. DROPEFFECT CGridCtrl::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState,
  2050.                                  CPoint point)
  2051. {
  2052.     // Any text data available for us?
  2053.     if (!m_bAllowDragAndDrop || !IsEditable() || !pDataObject->IsDataAvailable(CF_TEXT))
  2054.         return DROPEFFECT_NONE;
  2055.     // Find which cell we are over and drop-highlight it
  2056.     CCellID cell = GetCellFromPt(point, FALSE);
  2057.     // If not valid, set the previously drop-highlighted cell as no longer drop-highlighted
  2058.     if (!IsValid(cell))
  2059.     {
  2060.         OnDragLeave();
  2061.         m_LastDragOverCell = CCellID(-1,-1);
  2062.         return DROPEFFECT_NONE;
  2063.     }
  2064.     if (!IsCellEditable(cell))
  2065.         return DROPEFFECT_NONE;
  2066.     // Have we moved over a different cell than last time?
  2067.     if (cell != m_LastDragOverCell)
  2068.     {
  2069.         // Set the previously drop-highlighted cell as no longer drop-highlighted
  2070.         if (IsValid(m_LastDragOverCell))
  2071.         {
  2072.             UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  2073.             SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  2074.                 nState & ~GVIS_DROPHILITED);
  2075.             RedrawCell(m_LastDragOverCell);
  2076.         }
  2077.         m_LastDragOverCell = cell;
  2078.         // Set the new cell as drop-highlighted
  2079.         if (IsValid(m_LastDragOverCell))
  2080.         {
  2081.             UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  2082.             SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  2083.                 nState | GVIS_DROPHILITED);
  2084.             RedrawCell(m_LastDragOverCell);
  2085.         }
  2086.     }
  2087.     // Return an appropraite value of DROPEFFECT so mouse cursor is set properly
  2088.     if (dwKeyState & MK_CONTROL)
  2089.         return DROPEFFECT_COPY;
  2090.     else
  2091.         return DROPEFFECT_MOVE;
  2092. }
  2093. // Something has just been dragged onto the grid
  2094. DROPEFFECT CGridCtrl::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState,
  2095.                                   CPoint point)
  2096. {
  2097.     // Any text data available for us?
  2098.     if (!m_bAllowDragAndDrop || !pDataObject->IsDataAvailable(CF_TEXT))
  2099.         return DROPEFFECT_NONE;
  2100.     // Find which cell we are over and drop-highlight it
  2101.     m_LastDragOverCell = GetCellFromPt(point, FALSE);
  2102.     if (!IsValid(m_LastDragOverCell))
  2103.         return DROPEFFECT_NONE;
  2104.     if (!IsCellEditable(m_LastDragOverCell))
  2105.         return DROPEFFECT_NONE;
  2106.     if (IsValid(m_LastDragOverCell))
  2107.     {
  2108.         UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  2109.         SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  2110.             nState | GVIS_DROPHILITED);
  2111.         RedrawCell(m_LastDragOverCell);
  2112.     }
  2113.     // Return an appropraite value of DROPEFFECT so mouse cursor is set properly
  2114.     if (dwKeyState & MK_CONTROL)
  2115.         return DROPEFFECT_COPY;
  2116.     else
  2117.         return DROPEFFECT_MOVE;
  2118. }
  2119. // Something has just been dragged away from the grid
  2120. void CGridCtrl::OnDragLeave()
  2121. {
  2122.     // Set the previously drop-highlighted cell as no longer drop-highlighted
  2123.     if (IsValid(m_LastDragOverCell))
  2124.     {
  2125.         UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
  2126.         SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
  2127.             nState & ~GVIS_DROPHILITED);
  2128.         RedrawCell(m_LastDragOverCell);
  2129.     }
  2130. }
  2131. // Something has just been dropped onto the grid
  2132. BOOL CGridCtrl::OnDrop(COleDataObject* pDataObject, DROPEFFECT /*dropEffect*/,
  2133.                        CPoint /* point */)
  2134. {
  2135.     if (!m_bAllowDragAndDrop || !IsCellEditable(m_LastDragOverCell))
  2136.         return FALSE;
  2137.     m_MouseMode = MOUSE_NOTHING;
  2138.     OnDragLeave();
  2139.     return PasteTextToGrid(m_LastDragOverCell, pDataObject);
  2140. }
  2141. #endif
  2142. #ifndef GRIDCONTROL_NO_CLIPBOARD
  2143. void CGridCtrl::OnEditCut()
  2144. {
  2145.     if (!IsEditable())
  2146.         return;
  2147.     COleDataSource* pSource = CopyTextFromGrid();
  2148.     if (!pSource)
  2149.         return;
  2150.     pSource->SetClipboard();
  2151.     CutSelectedText();
  2152. }
  2153. void CGridCtrl::OnEditCopy()
  2154. {
  2155.     COleDataSource* pSource = CopyTextFromGrid();
  2156.     if (!pSource)
  2157.         return;
  2158.     pSource->SetClipboard();
  2159. }
  2160. void CGridCtrl::OnEditPaste()
  2161. {
  2162.     if (!IsEditable())
  2163.         return;
  2164.     // Get the Focus cell, or if none, get the topleft (non-fixed) cell
  2165.     CCellID cell = GetFocusCell();
  2166.     if (!IsValid(cell))
  2167.         cell = GetTopleftNonFixedCell();
  2168.     if (!IsValid(cell))
  2169.         return;
  2170.     // Attach a COleDataObject to the clipboard and paste the data to the grid
  2171.     COleDataObject obj;
  2172.     if (obj.AttachClipboard())
  2173.         PasteTextToGrid(cell, &obj);
  2174. }
  2175. #endif
  2176. void CGridCtrl::OnEditSelectAll()
  2177. {
  2178.     SelectAllCells();
  2179. }
  2180. #ifndef GRIDCONTROL_NO_CLIPBOARD
  2181. void CGridCtrl::OnUpdateEditCopy(CCmdUI* pCmdUI)
  2182. {
  2183.     CCellRange Selection = GetSelectedCellRange();
  2184.     pCmdUI->Enable(Selection.Count() && IsValid(Selection));
  2185. }
  2186. void CGridCtrl::OnUpdateEditCut(CCmdUI* pCmdUI)
  2187. {
  2188.     CCellRange Selection = GetSelectedCellRange();
  2189.     pCmdUI->Enable(IsEditable() && Selection.Count() && IsValid(Selection));
  2190. }
  2191. void CGridCtrl::OnUpdateEditPaste(CCmdUI* pCmdUI)
  2192. {
  2193.     CCellID cell = GetFocusCell();
  2194.     BOOL bCanPaste = IsValid(cell) && IsCellEditable(cell) &&
  2195.         ::IsClipboardFormatAvailable(CF_TEXT);
  2196.     pCmdUI->Enable(bCanPaste);
  2197. }
  2198. #endif
  2199. void CGridCtrl::OnUpdateEditSelectAll(CCmdUI* pCmdUI)
  2200. {
  2201.     pCmdUI->Enable(m_bEnableSelection);
  2202. }
  2203. ////////////////////////////////////////////////////////////////////////////////////////
  2204. // hittest-like functions
  2205. // TRUE if the mouse is over a row resize area
  2206. BOOL CGridCtrl::MouseOverRowResizeArea(CPoint& point)
  2207. {
  2208.     if (point.x >= GetFixedColumnWidth())
  2209.         return FALSE;
  2210.     CCellID idCurrentCell = GetCellFromPt(point);
  2211.     CPoint start;
  2212.     if (!GetCellOrigin(idCurrentCell, &start))
  2213.         return FALSE;
  2214.     int endy = start.y + GetRowHeight(idCurrentCell.row);
  2215.     if ((point.y - start.y <= m_nResizeCaptureRange && idCurrentCell.row != 0) ||
  2216.         endy - point.y <= m_nResizeCaptureRange)
  2217.     {
  2218.         return TRUE;
  2219.     }
  2220.     else
  2221.         return FALSE;
  2222. }
  2223. // TRUE if the mouse is over a column resize area. point is in Client coords
  2224. BOOL CGridCtrl::MouseOverColumnResizeArea(CPoint& point)
  2225. {
  2226.     if (point.y >= GetFixedRowHeight())
  2227.         return FALSE;
  2228.     CCellID idCurrentCell = GetCellFromPt(point);
  2229.     CPoint start;
  2230.     if (!GetCellOrigin(idCurrentCell, &start))
  2231.         return FALSE;
  2232.     int endx = start.x + GetColumnWidth(idCurrentCell.col);
  2233.     if ((point.x - start.x <= m_nResizeCaptureRange && idCurrentCell.col != 0) ||
  2234.         endx - point.x <= m_nResizeCaptureRange)
  2235.     {
  2236.         return TRUE;
  2237.     }
  2238.     else
  2239.         return FALSE;
  2240. }
  2241. // Get cell from point
  2242. CCellID CGridCtrl::GetCellFromPt(CPoint point, BOOL bAllowFixedCellCheck /*=TRUE*/)
  2243. {
  2244.     CCellID cellID; // return value
  2245.     CCellID idTopLeft = GetTopleftNonFixedCell();
  2246.     if (!IsValid(idTopLeft))
  2247.         return cellID;
  2248.     // calculate column index
  2249.     int fixedColWidth = GetFixedColumnWidth();
  2250.     if (point.x < 0 || (!bAllowFixedCellCheck && point.x < fixedColWidth)) // not in window
  2251.         cellID.col = -1;
  2252.     else if (point.x < fixedColWidth) // in fixed col
  2253.     {
  2254.         int xpos = 0;
  2255.         for (int col = 0; col < m_nFixedCols; col++)
  2256.         {
  2257.             xpos += GetColumnWidth(col);
  2258.             if (xpos > point.x)
  2259.                 break;
  2260.         }
  2261.         cellID.col = col;
  2262.     }
  2263.     else    // in non-fixed col
  2264.     {
  2265.         int xpos = fixedColWidth;
  2266.         for (int col = idTopLeft.col; col < GetColumnCount(); col++)
  2267.         {
  2268.             xpos += GetColumnWidth(col);
  2269.             if (xpos > point.x)
  2270.                 break;
  2271.         }
  2272.         if (col >= GetColumnCount())
  2273.             cellID.col = -1;
  2274.         else
  2275.             cellID.col = col;
  2276.     }
  2277.     // calculate row index
  2278.     int fixedRowHeight = GetFixedRowHeight();
  2279.     if (point.y < 0 || (!bAllowFixedCellCheck && point.y < fixedRowHeight)) // not in window
  2280.         cellID.row = -1;
  2281.     else if (point.y < fixedRowHeight) // in fixed col
  2282.     {
  2283.         int ypos = 0;
  2284.         for (int row = 0; row < m_nFixedRows; row++)
  2285.         {
  2286.             ypos += GetRowHeight(row);
  2287.             if (ypos > point.y)
  2288.                 break;
  2289.         }
  2290.         cellID.row = row;
  2291.     }
  2292.     else
  2293.     {
  2294.         int ypos = fixedRowHeight;
  2295.         for (int row = idTopLeft.row; row < GetRowCount(); row++)
  2296.         {
  2297.             ypos += GetRowHeight(row);
  2298.             if (ypos > point.y)
  2299.                 break;
  2300.         }
  2301.         if (row >= GetRowCount())
  2302.             cellID.row = -1;
  2303.         else
  2304.             cellID.row = row;
  2305.     }
  2306.     return cellID;
  2307. }
  2308. ////////////////////////////////////////////////////////////////////////////////
  2309. // CGridCtrl cellrange functions
  2310. // Gets the first non-fixed cell ID
  2311. CCellID CGridCtrl::GetTopleftNonFixedCell()
  2312. {
  2313.     // Used cached value if possible
  2314.     if (m_idTopLeftCell.IsValid())
  2315.         return m_idTopLeftCell;
  2316.     int nVertScroll = GetScrollPos(SB_VERT), 
  2317.         nHorzScroll = GetScrollPos(SB_HORZ);
  2318.     m_idTopLeftCell.col = m_nFixedCols;
  2319.     int nRight = 0;
  2320.     while (nRight < nHorzScroll && m_idTopLeftCell.col < (GetColumnCount()-1))
  2321.         nRight += GetColumnWidth(m_idTopLeftCell.col++);
  2322.     m_idTopLeftCell.row = m_nFixedRows;
  2323.     int nTop = 0;
  2324.     while (nTop < nVertScroll && m_idTopLeftCell.row < (GetRowCount()-1))
  2325.         nTop += GetRowHeight(m_idTopLeftCell.row++);
  2326.     //TRACE2("TopLeft cell is row %d, col %dn",m_idTopLeftCell.row, m_idTopLeftCell.col);
  2327.     return m_idTopLeftCell;
  2328. }
  2329. // This gets even partially visible cells
  2330. CCellRange CGridCtrl::GetVisibleNonFixedCellRange(LPRECT pRect /*=NULL*/)
  2331. {
  2332.     CRect rect;
  2333.     GetClientRect(rect);
  2334.     CCellID idTopLeft = GetTopleftNonFixedCell();
  2335.     // calc bottom
  2336.     int bottom = GetFixedRowHeight();
  2337.     for (int i = idTopLeft.row; i < GetRowCount(); i++)
  2338.     {
  2339.         bottom += GetRowHeight(i);
  2340.         if (bottom >= rect.bottom)
  2341.         {
  2342.             bottom = rect.bottom;
  2343.             break;
  2344.         }
  2345.     }
  2346.     int maxVisibleRow = min(i, GetRowCount() - 1);
  2347.     // calc right
  2348.     int right = GetFixedColumnWidth();
  2349.     for (i = idTopLeft.col; i < GetColumnCount(); i++)
  2350.     {
  2351.         right += GetColumnWidth(i);
  2352.         if (right >= rect.right)
  2353.         {
  2354.             right = rect.right;
  2355.             break;
  2356.         }
  2357.     }
  2358.     int maxVisibleCol = min(i, GetColumnCount() - 1);
  2359.     if (pRect)
  2360.     {
  2361.         pRect->left = pRect->top = 0;
  2362.         pRect->right = right;
  2363.         pRect->bottom = bottom;
  2364.     }
  2365.     return CCellRange(idTopLeft.row, idTopLeft.col, maxVisibleRow, maxVisibleCol);
  2366. }
  2367. // used by ResetScrollBars() - This gets only fully visible cells
  2368. CCellRange CGridCtrl::GetUnobstructedNonFixedCellRange()
  2369. {
  2370.     CRect rect;
  2371.     GetClientRect(rect);
  2372.     CCellID idTopLeft = GetTopleftNonFixedCell();
  2373.     // calc bottom
  2374.     int bottom = GetFixedRowHeight();
  2375.     for (int i = idTopLeft.row; i < GetRowCount(); i++)
  2376.     {
  2377.         bottom += GetRowHeight(i);
  2378.         if (bottom >= rect.bottom)
  2379.             break;
  2380.     }
  2381.     int maxVisibleRow = min(i, GetRowCount() - 1);
  2382.     if (maxVisibleRow > 0 && bottom > rect.bottom)
  2383.         maxVisibleRow--;
  2384.     // calc right
  2385.     int right = GetFixedColumnWidth();
  2386.     for (i = idTopLeft.col; i < GetColumnCount(); i++)
  2387.     {
  2388.         right += GetColumnWidth(i);
  2389.         if (right >= rect.right)
  2390.             break;
  2391.     }
  2392.     int maxVisibleCol = min(i, GetColumnCount() - 1);
  2393.     if (maxVisibleCol > 0 && right > rect.right)
  2394.         maxVisibleCol--;
  2395.     return CCellRange(idTopLeft.row, idTopLeft.col, maxVisibleRow, maxVisibleCol);
  2396. }
  2397. // Returns the minimum bounding range of the current selection
  2398. // If no selection, then the returned CCellRange will be invalid
  2399. CCellRange CGridCtrl::GetSelectedCellRange() const
  2400. {
  2401.     CCellRange Selection(GetRowCount(), GetColumnCount(), -1,-1);
  2402.     for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
  2403.     {
  2404.         DWORD key;
  2405.         CCellID cell;
  2406.         m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
  2407.         Selection.SetMinRow( min(Selection.GetMinRow(), cell.row) );
  2408.         Selection.SetMinCol( min(Selection.GetMinCol(), cell.col) );
  2409.         Selection.SetMaxRow( max(Selection.GetMaxRow(), cell.row) );
  2410.         Selection.SetMaxCol( max(Selection.GetMaxCol(), cell.col) );
  2411.     }
  2412.     return Selection;
  2413. }
  2414. // Returns ALL the cells in the grid
  2415. CCellRange CGridCtrl::GetCellRange() const
  2416. {
  2417.     return CCellRange(0, 0, GetRowCount() - 1, GetColumnCount() - 1);
  2418. }
  2419. // Resets the selected cell range to the empty set.
  2420. void CGridCtrl::ResetSelectedRange()
  2421. {
  2422.     m_PrevSelectedCellMap.RemoveAll();
  2423.     SetSelectedRange(-1,-1,-1,-1);
  2424.     SetFocusCell(-1,-1);
  2425. }
  2426. // Get/Set scroll position using 32 bit functions
  2427. int CGridCtrl::GetScrollPos32(int nBar, BOOL bGetTrackPos /* = FALSE */)
  2428. {
  2429.     SCROLLINFO si;
  2430.     si.cbSize = sizeof(SCROLLINFO);
  2431.     if (bGetTrackPos)
  2432.     {
  2433.         if (GetScrollInfo(nBar, &si, SIF_TRACKPOS))
  2434.             return si.nTrackPos;
  2435.     }
  2436.     else
  2437.     {
  2438.         if (GetScrollInfo(nBar, &si, SIF_POS))
  2439.             return si.nPos;
  2440.     }
  2441.     return 0;
  2442. }
  2443. BOOL CGridCtrl::SetScrollPos32(int nBar, int nPos, BOOL bRedraw /* = TRUE */)
  2444. {
  2445.     m_idTopLeftCell.row = -1;
  2446.     SCROLLINFO si;
  2447.     si.cbSize = sizeof(SCROLLINFO);
  2448.     si.fMask  = SIF_POS;
  2449.     si.nPos   = nPos;
  2450.     return SetScrollInfo(nBar, &si, bRedraw);
  2451. }
  2452. void CGridCtrl::EnableScrollBars(int nBar, BOOL bEnable /*=TRUE*/)
  2453. {
  2454.     if (bEnable)
  2455.     {
  2456.         if (!IsVisibleHScroll() && (nBar == SB_HORZ || nBar == SB_BOTH))
  2457.         {
  2458.             CWnd::EnableScrollBarCtrl(SB_HORZ, bEnable);
  2459.             m_nBarState |= GVL_HORZ;
  2460.         }
  2461.         
  2462.         if (!IsVisibleVScroll() && (nBar == SB_VERT || nBar == SB_BOTH))
  2463.         {
  2464.             CWnd::EnableScrollBarCtrl(SB_VERT, bEnable);
  2465.             m_nBarState |= GVL_VERT;
  2466.        }
  2467.     }
  2468.     else
  2469.     {
  2470.         if ( IsVisibleHScroll() && (nBar == SB_HORZ || nBar == SB_BOTH))
  2471.         {
  2472.             CWnd::EnableScrollBarCtrl(SB_HORZ, bEnable);
  2473.             m_nBarState &= ~GVL_HORZ; 
  2474.         }
  2475.         
  2476.         if ( IsVisibleVScroll() && (nBar == SB_VERT || nBar == SB_BOTH))
  2477.         {
  2478.             CWnd::EnableScrollBarCtrl(SB_VERT, bEnable);
  2479.             m_nBarState &= ~GVL_VERT;
  2480.         }
  2481.     }
  2482. }
  2483. // If resizing or cell counts/sizes change, call this - it'll fix up the scroll bars
  2484. void CGridCtrl::ResetScrollBars()
  2485. {
  2486.     // Force a refresh. 
  2487.     m_idTopLeftCell.row = -1;
  2488.     if (!m_bAllowDraw || !::IsWindow(GetSafeHwnd())) 
  2489.         return;
  2490.     
  2491.     CRect rect;
  2492.     
  2493.     // This would have caused OnSize event - Brian 
  2494.     //EnableScrollBars(SB_BOTH, FALSE); 
  2495.     
  2496.     GetClientRect(rect);
  2497.     
  2498.     if (rect.left == rect.right || rect.top == rect.bottom)
  2499.         return;
  2500.     
  2501.     if (IsVisibleVScroll())
  2502.         rect.right += GetSystemMetrics(SM_CXVSCROLL) + GetSystemMetrics(SM_CXBORDER);
  2503.     
  2504.     if (IsVisibleHScroll())
  2505.         rect.bottom += GetSystemMetrics(SM_CYHSCROLL) + GetSystemMetrics(SM_CYBORDER);
  2506.     
  2507.     rect.left += GetFixedColumnWidth();
  2508.     rect.top += GetFixedRowHeight();
  2509.     
  2510.     
  2511.     if (rect.left >= rect.right || rect.top >= rect.bottom)
  2512.     {
  2513.         EnableScrollBarCtrl(SB_BOTH, FALSE);
  2514.         return;
  2515.     }
  2516.     
  2517.     CRect VisibleRect(GetFixedColumnWidth(), GetFixedRowHeight(), rect.right, rect.bottom);
  2518.     CRect VirtualRect(GetFixedColumnWidth(), GetFixedRowHeight(), GetVirtualWidth(), GetVirtualHeight());
  2519.     
  2520.     
  2521.     // Removed to fix single row scrollbar problem (Pontus Goffe)
  2522.     // CCellRange visibleCells = GetUnobstructedNonFixedCellRange();
  2523.     // if (!IsValid(visibleCells)) return;
  2524.         
  2525.     //TRACE(_T("Visible: %d x %d, Virtual %d x %d.  H %d, V %dn"), 
  2526.     //      VisibleRect.Width(), VisibleRect.Height(),
  2527.     //      VirtualRect.Width(), VirtualRect.Height(),
  2528.     //      IsVisibleHScroll(), IsVisibleVScroll());
  2529.     // If vertical scroll bar, horizontal space is reduced
  2530.     if (VisibleRect.Height() < VirtualRect.Height())
  2531.         VisibleRect.right -= ::GetSystemMetrics(SM_CXVSCROLL);
  2532.     // If horz scroll bar, vert space is reduced
  2533.     if (VisibleRect.Width() < VirtualRect.Width())
  2534.         VisibleRect.bottom -= ::GetSystemMetrics(SM_CYHSCROLL);
  2535.     
  2536.     // Recheck vertical scroll bar
  2537.     //if (VisibleRect.Height() < VirtualRect.Height())
  2538.     // VisibleRect.right -= ::GetSystemMetrics(SM_CXVSCROLL);
  2539.     
  2540.     if (VisibleRect.Height() < VirtualRect.Height())
  2541.     {
  2542.         EnableScrollBars(SB_VERT, TRUE); 
  2543.         m_nVScrollMax = VirtualRect.Height() - 1;
  2544.     }
  2545.     else
  2546.     {
  2547.         EnableScrollBars(SB_VERT, FALSE); 
  2548.         m_nVScrollMax = 0;
  2549.     }
  2550.     if (VisibleRect.Width() < VirtualRect.Width())
  2551.     {
  2552.         EnableScrollBars(SB_HORZ, TRUE); 
  2553.         m_nHScrollMax = VirtualRect.Width() - 1;
  2554.     }
  2555.     else
  2556.     {
  2557.         EnableScrollBars(SB_HORZ, FALSE); 
  2558.         m_nHScrollMax = 0;
  2559.     }
  2560.     ASSERT(m_nVScrollMax < INT_MAX && m_nHScrollMax < INT_MAX); // This should be fine
  2561.     SCROLLINFO si;
  2562.     si.cbSize = sizeof(SCROLLINFO);
  2563.     si.fMask = SIF_PAGE;
  2564.     si.nPage = (m_nHScrollMax>0)? VisibleRect.Width() : 0;
  2565.     SetScrollInfo(SB_HORZ, &si, FALSE); 
  2566.     si.nPage = (m_nVScrollMax>0)? VisibleRect.Height() : 0;
  2567.     SetScrollInfo(SB_VERT, &si, FALSE);
  2568.     SetScrollRange(SB_VERT, 0, m_nVScrollMax, TRUE);
  2569.     SetScrollRange(SB_HORZ, 0, m_nHScrollMax, TRUE);
  2570.     //TRACE(_T("H scroll: %d, V Scroll %dn"), m_nHScrollMax, m_nVScrollMax);
  2571. }
  2572. ////////////////////////////////////////////////////////////////////////////////////
  2573. // Row/Column position functions
  2574. // returns the top left point of the cell. Returns FALSE if cell not visible.
  2575. BOOL CGridCtrl::GetCellOrigin(int nRow, int nCol, LPPOINT p)
  2576. {
  2577.     int i;
  2578.     if (!IsValid(nRow, nCol))
  2579.         return FALSE;
  2580.     CCellID idTopLeft;
  2581.     if (nCol >= m_nFixedCols || nRow >= m_nFixedRows)
  2582.         idTopLeft = GetTopleftNonFixedCell();
  2583.     if ((nRow >= m_nFixedRows && nRow < idTopLeft.row) ||
  2584.         (nCol>= m_nFixedCols && nCol < idTopLeft.col))
  2585.         return FALSE;
  2586.     p->x = 0;
  2587.     if (nCol < m_nFixedCols)                      // is a fixed column
  2588.         for (i = 0; i < nCol; i++)
  2589.             p->x += GetColumnWidth(i);
  2590.         else 
  2591.         {                                        // is a scrollable data column
  2592.             for (i = 0; i < m_nFixedCols; i++)
  2593.                 p->x += GetColumnWidth(i);
  2594.             for (i = idTopLeft.col; i < nCol; i++)
  2595.                 p->x += GetColumnWidth(i);
  2596.         }
  2597.         
  2598.         p->y = 0;
  2599.         if (nRow < m_nFixedRows)                      // is a fixed row
  2600.             for (i = 0; i < nRow; i++)
  2601.                 p->y += GetRowHeight(i);
  2602.             else 
  2603.             {                                        // is a scrollable data row
  2604.                 for (i = 0; i < m_nFixedRows; i++)
  2605.                     p->y += GetRowHeight(i);
  2606.                 for (i = idTopLeft.row; i < nRow; i++)
  2607.                     p->y += GetRowHeight(i);
  2608.             }
  2609.             
  2610.             return TRUE;
  2611. }
  2612. BOOL CGridCtrl::GetCellOrigin(const CCellID& cell, LPPOINT p)
  2613. {
  2614.     return GetCellOrigin(cell.row, cell.col, p);
  2615. }
  2616. // Returns the bounding box of the cell
  2617. BOOL CGridCtrl::GetCellRect(const CCellID& cell, LPRECT pRect)
  2618. {
  2619.     return GetCellRect(cell.row, cell.col, pRect);
  2620. }
  2621. BOOL CGridCtrl::GetCellRect(int nRow, int nCol, LPRECT pRect)
  2622. {
  2623.     CPoint CellOrigin;
  2624.     if (!GetCellOrigin(nRow, nCol, &CellOrigin))
  2625.         return FALSE;
  2626.     pRect->left   = CellOrigin.x;
  2627.     pRect->top    = CellOrigin.y;
  2628.     pRect->right  = CellOrigin.x + GetColumnWidth(nCol)-1;
  2629.     pRect->bottom = CellOrigin.y + GetRowHeight(nRow)-1;
  2630.     //TRACE("Row %d, col %d: L %d, T %d, W %d, H %d:  %d,%d - %d,%dn",
  2631.     //      nRow,nCol, CellOrigin.x, CellOrigin.y, GetColumnWidth(nCol), GetRowHeight(nRow),
  2632.     //      pRect->left, pRect->top, pRect->right, pRect->bottom);
  2633.     return TRUE;
  2634. }
  2635. BOOL CGridCtrl::GetTextRect(const CCellID& cell, LPRECT pRect)
  2636. {
  2637.     return GetTextRect(cell.row, cell.col, pRect);
  2638. }
  2639. BOOL CGridCtrl::GetTextRect(int nRow, int nCol, LPRECT pRect)
  2640. {
  2641.     CGridCellBase* pCell = GetCell( nRow, nCol);
  2642.     if( pCell == NULL)
  2643.         return FALSE;
  2644.     
  2645.     if( !GetCellRect( nRow, nCol, pRect) )
  2646.         return FALSE;
  2647.     return pCell->GetTextRect( pRect);
  2648. }
  2649. // Returns the bounding box of a range of cells
  2650. BOOL CGridCtrl::GetCellRangeRect(const CCellRange& cellRange, LPRECT lpRect)
  2651. {
  2652.     CPoint MinOrigin,MaxOrigin;
  2653.     if (!GetCellOrigin(cellRange.GetMinRow(), cellRange.GetMinCol(), &MinOrigin))
  2654.         return FALSE;
  2655.     if (!GetCellOrigin(cellRange.GetMaxRow(), cellRange.GetMaxCol(), &MaxOrigin))
  2656.         return FALSE;
  2657.     lpRect->left   = MinOrigin.x;
  2658.     lpRect->top    = MinOrigin.y;
  2659.     lpRect->right  = MaxOrigin.x + GetColumnWidth(cellRange.GetMaxCol()) - 1;
  2660.     lpRect->bottom = MaxOrigin.y + GetRowHeight(cellRange.GetMaxRow()) - 1;
  2661.     return TRUE;
  2662. }
  2663. ////////////////////////////////////////////////////////////////////////////////////
  2664. // Grid attribute functions
  2665. LRESULT CGridCtrl::OnSetFont(WPARAM hFont, LPARAM /*lParam */)
  2666. {
  2667.     LRESULT result = Default();
  2668.     // Get the logical font
  2669.     LOGFONT lf;
  2670.     if (!GetObject((HFONT) hFont, sizeof(LOGFONT), &lf))
  2671.         return result;
  2672.     m_cellDefault.SetFont(&lf);
  2673.     m_cellFixedColDef.SetFont(&lf);
  2674.     m_cellFixedRowDef.SetFont(&lf);
  2675.     m_cellFixedRowColDef.SetFont(&lf);
  2676.     Refresh();
  2677.     return result;
  2678. }
  2679. LRESULT CGridCtrl::OnGetFont(WPARAM /*wParam*/, LPARAM /*lParam*/)
  2680. {
  2681.     //LOGFONT lf;
  2682.     //m_cellDefault.GetFontObject()->GetLogFont(&lf);
  2683.     return (LRESULT) m_cellDefault.GetFontObject()->GetSafeHandle();
  2684. }
  2685. #ifndef _WIN32_WCE_NO_CURSOR
  2686. BOOL CGridCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  2687. {
  2688.     if (nHitTest == HTCLIENT)
  2689.     {
  2690.         switch (m_MouseMode)
  2691.         {
  2692.         case MOUSE_OVER_COL_DIVIDE:
  2693.             SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
  2694.             break;
  2695.         case MOUSE_OVER_ROW_DIVIDE:
  2696.             SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
  2697.             break;
  2698. #ifndef GRIDCONTROL_NO_DRAGDROP
  2699.         case MOUSE_DRAGGING:
  2700.             break;
  2701. #endif
  2702.         default:
  2703.             if (!GetVirtualMode())
  2704.             {
  2705.                 CPoint pt(GetMessagePos());
  2706.                 ScreenToClient(&pt);
  2707.                 CCellID cell = GetCellFromPt(pt);
  2708.                 if (IsValid(cell))
  2709.                 {
  2710.                     CGridCellBase* pCell = GetCell(cell.row, cell.col);
  2711.                     if (pCell)
  2712.                         return pCell->OnSetCursor();
  2713.                 }
  2714.             }
  2715.             SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  2716.         }
  2717.         return TRUE;
  2718.     }
  2719.     return CWnd::OnSetCursor(pWnd, nHitTest, message);
  2720. }
  2721. #endif
  2722. ////////////////////////////////////////////////////////////////////////////////////
  2723. // Row/Column count functions
  2724. BOOL CGridCtrl::SetFixedRowCount(int nFixedRows)
  2725. {
  2726.     if (m_nFixedRows == nFixedRows)
  2727.         return TRUE;
  2728.     ASSERT(nFixedRows >= 0);
  2729.     ResetSelectedRange();
  2730.     // Force recalculation
  2731.     m_idTopLeftCell.col = -1;
  2732.     if (nFixedRows > GetRowCount())
  2733.         if (!SetRowCount(nFixedRows))
  2734.             return FALSE;
  2735.         
  2736.         if (m_idCurrentCell.row < nFixedRows)
  2737.             SetFocusCell(-1, - 1);
  2738.         
  2739.         if (!GetVirtualMode())
  2740.         {
  2741.             if (nFixedRows > m_nFixedRows)
  2742.             {
  2743.                 for (int i = m_nFixedRows; i < nFixedRows; i++)
  2744.                     for (int j = 0; j < GetColumnCount(); j++)
  2745.                     {
  2746.                         SetItemState(i, j, GetItemState(i, j) | GVIS_FIXED | GVIS_FIXEDROW);
  2747.                         SetItemBkColour(i, j, CLR_DEFAULT );
  2748.                         SetItemFgColour(i, j, CLR_DEFAULT );
  2749.                     }
  2750.             }
  2751.             else
  2752.             {
  2753.                 for (int i = nFixedRows; i < m_nFixedRows; i++)
  2754.                 {
  2755.                     for (int j = 0; j < GetFixedColumnCount(); j++)
  2756.                         SetItemState(i, j, GetItemState(i, j) & ~GVIS_FIXEDROW );
  2757.                     for (j = GetFixedColumnCount(); j < GetColumnCount(); j++)
  2758.                     {
  2759.                         SetItemState(i, j, GetItemState(i, j) & ~(GVIS_FIXED | GVIS_FIXEDROW) );
  2760.                         SetItemBkColour(i, j, CLR_DEFAULT );
  2761.                         SetItemFgColour(i, j, CLR_DEFAULT );
  2762.                     }
  2763.                 }
  2764.             }
  2765.         }
  2766.         m_nFixedRows = nFixedRows;
  2767.         
  2768.         Refresh();
  2769.         
  2770.         return TRUE;
  2771. }
  2772. BOOL CGridCtrl::SetFixedColumnCount(int nFixedCols)
  2773. {
  2774.     if (m_nFixedCols == nFixedCols)
  2775.         return TRUE;
  2776.     ASSERT(nFixedCols >= 0);
  2777.     if (nFixedCols > GetColumnCount())
  2778.         if (!SetColumnCount(nFixedCols))
  2779.             return FALSE;
  2780.     if (m_idCurrentCell.col < nFixedCols)
  2781.         SetFocusCell(-1, - 1);
  2782.     ResetSelectedRange();
  2783.     // Force recalculation
  2784.     m_idTopLeftCell.col = -1;
  2785.     if (!GetVirtualMode())
  2786.     {
  2787.         if (nFixedCols > m_nFixedCols)
  2788.         {
  2789.             for (int i = 0; i < GetRowCount(); i++)
  2790.                 for (int j = m_nFixedCols; j < nFixedCols; j++)
  2791.                 {
  2792.                     SetItemState(i, j, GetItemState(i, j) | GVIS_FIXED | GVIS_FIXEDCOL);
  2793.                     SetItemBkColour(i, j, CLR_DEFAULT );
  2794.                     SetItemFgColour(i, j, CLR_DEFAULT );
  2795.                 }
  2796.         }
  2797.         else
  2798.         {
  2799.             for (int i = 0; i < GetFixedRowCount(); i++)
  2800.                 for (int j = nFixedCols; j < m_nFixedCols; j++)
  2801.                     SetItemState(i, j, GetItemState(i, j) & ~GVIS_FIXEDCOL );
  2802.             for (i = GetFixedRowCount(); i < GetRowCount(); i++)
  2803.                 for (int j = nFixedCols; j < m_nFixedCols; j++)
  2804.                 {
  2805.                     SetItemState(i, j, GetItemState(i, j) & ~(GVIS_FIXED | GVIS_FIXEDCOL) );
  2806.                     SetItemBkColour(i, j, CLR_DEFAULT );
  2807.                     SetItemFgColour(i, j, CLR_DEFAULT );
  2808.                 }
  2809.         }
  2810.     }
  2811.         
  2812.     m_nFixedCols = nFixedCols;
  2813.         
  2814.     Refresh();
  2815.         
  2816.     return TRUE;
  2817. }
  2818. BOOL CGridCtrl::SetRowCount(int nRows)
  2819. {
  2820.     BOOL bResult = TRUE;
  2821.     ASSERT(nRows >= 0);
  2822.     if (nRows == GetRowCount())
  2823.         return bResult;
  2824.     // Force recalculation
  2825.     m_idTopLeftCell.col = -1;
  2826.     if (nRows < m_nFixedRows)
  2827.         m_nFixedRows = nRows;
  2828.     if (m_idCurrentCell.row >= nRows)
  2829.         SetFocusCell(-1, - 1);
  2830.     int addedRows = nRows - GetRowCount();
  2831.     // If we are about to lose rows, then we need to delete the GridCell objects
  2832.     // in each column within each row
  2833.     if (addedRows < 0)
  2834.     {
  2835.         if (!GetVirtualMode())
  2836.         {
  2837.             for (int row = nRows; row < m_nRows; row++)
  2838.             {
  2839.                 // Delete cells
  2840.                 for (int col = 0; col < m_nCols; col++)
  2841.                     DestroyCell(row, col);
  2842.             
  2843.                 // Delete rows
  2844.                 GRID_ROW* pRow = m_RowData[row];
  2845.                 if (pRow)
  2846.                     delete pRow;
  2847.             }
  2848.         }
  2849.         m_nRows = nRows;
  2850.     }
  2851.     
  2852.     TRY
  2853.     {
  2854.         m_arRowHeights.SetSize(nRows);
  2855.         if (GetVirtualMode())
  2856.         {
  2857.             m_nRows = nRows;
  2858.             if (addedRows > 0)
  2859.             {
  2860.                 int startRow = nRows - addedRows;
  2861.                 for (int row = startRow; row < nRows; row++)
  2862.                     m_arRowHeights[row] = m_cellDefault.GetHeight();
  2863.             }
  2864.         }
  2865.         else
  2866.         {
  2867.             // Change the number of rows.
  2868.             m_RowData.SetSize(nRows);
  2869.             // If we have just added rows, we need to construct new elements for each cell
  2870.             // and set the default row height
  2871.             if (addedRows > 0)
  2872.             {
  2873.                 // initialize row heights and data
  2874.                 int startRow = nRows - addedRows;
  2875.                 for (int row = startRow; row < nRows; row++)
  2876.                 {
  2877.                     m_arRowHeights[row] = m_cellDefault.GetHeight();
  2878.                     m_RowData[row] = new GRID_ROW;
  2879.                     m_RowData[row]->SetSize(m_nCols);
  2880.                     for (int col = 0; col < m_nCols; col++)
  2881.                     {
  2882.                         GRID_ROW* pRow = m_RowData[row];
  2883.                         if (pRow && !GetVirtualMode())
  2884.                             pRow->SetAt(col, CreateCell(row, col));
  2885.                     }
  2886.                     m_nRows++;
  2887.                 }
  2888.             }
  2889.         }
  2890.     }
  2891.     CATCH (CMemoryException, e)
  2892.     {
  2893.         e->ReportError();
  2894.         bResult = FALSE;
  2895.     }
  2896.     END_CATCH
  2897.     SetModified();
  2898.     ResetScrollBars();
  2899.     Refresh();
  2900.     return TRUE;
  2901. }
  2902. BOOL CGridCtrl::SetColumnCount(int nCols)
  2903. {
  2904.     BOOL bResult = TRUE;
  2905.     ASSERT(nCols >= 0);
  2906.     if (nCols == GetColumnCount())
  2907.         return bResult;
  2908.     // Force recalculation
  2909.     m_idTopLeftCell.col = -1;
  2910.     if (nCols < m_nFixedCols)
  2911.         m_nFixedCols = nCols;
  2912.     if (m_idCurrentCell.col >= nCols)
  2913.         SetFocusCell(-1, - 1);
  2914.     int addedCols = nCols - GetColumnCount();
  2915.     // If we are about to lose columns, then we need to delete the GridCell objects
  2916.     // within each column
  2917.     if (addedCols < 0 && !GetVirtualMode())
  2918.     {
  2919.         for (int row = 0; row < m_nRows; row++)
  2920.             for (int col = nCols; col < GetColumnCount(); col++)
  2921.                 DestroyCell(row, col);
  2922.     }
  2923.     TRY 
  2924.     {
  2925.         // Change the number of columns.
  2926.         m_arColWidths.SetSize(nCols);
  2927.     
  2928.         // Change the number of columns in each row.
  2929.         if (!GetVirtualMode())
  2930.             for (int i = 0; i < m_nRows; i++)
  2931.                 if (m_RowData[i])
  2932.                     m_RowData[i]->SetSize(nCols);
  2933.         
  2934.         // If we have just added columns, we need to construct new elements for each cell
  2935.         // and set the default column width
  2936.         if (addedCols > 0)
  2937.         {
  2938.             // initialized column widths