GRIDCTRL.CPP
上传用户:asikq0571
上传日期:2014-07-12
资源大小:528k
文件大小:156k
- // GridCtrl.cpp : implementation file
- //
- // MFC Grid Control
- //
- // Written by Chris Maunder
- // mailto:chrismaunder@codeguru.com
- //
- // Copyright (c) 1998.
- //
- // The code contained in this file is based on the original
- // WorldCom Grid control written by Joe Willcoxson,
- // mailto:chinajoe@aol.com
- // http://users.aol.com/chinajoe
- //
- // This code may be used in compiled form in any way you desire. This
- // file may be redistributed unmodified by any means PROVIDING it is
- // not sold for profit without the authors written consent, and
- // providing that this notice and the authors name and all copyright
- // notices remains intact. If the source code in this file is used in
- // any commercial application then a statement along the lines of
- // "Portions copyright (c) Chris Maunder, 1998" must be included in
- // the startup banner, "About" box or printed documentation. An email
- // letting me know that you are using it would be nice as well. That's
- // not much to ask considering the amount of work that went into this.
- //
- // This file is provided "as is" with no expressed or implied warranty.
- // The author accepts no liability for any damage/loss of business that
- // this product may cause.
- //
- // Expect bugs!
- //
- // Please use and enjoy, and let me know of any bugs/mods/improvements
- // that you have found/implemented and I will fix/incorporate them into
- // this file.
- //
- // History:
- // --------
- // This control is constantly evolving, sometimes due to new features that I
- // feel are necessary, and sometimes due to existing bugs. Where possible I
- // have credited the changes to those who contributed code corrections or
- // enhancements (names in brackets) or code suggestions (suggested by...)
- //
- // 1.0 20 Feb 1998 First release version.
- // 1.01 24 Feb 1998 Memory leak fix (Jens Bohlmann)
- // Fixec typo (my fault!) in CMemDC.h - Claus Arend-Schneider)
- // Bug in GetSelectedCount (Lyn Newton)
- // 1.02 4 Mar 1998 Scrolling a little neater (less dead area)
- // Cell selection via OnTimer correctly updates Focus cell (Suggested by Lyn Newton)
- // 1.03 17 Mar 1998 Clipboard functions added, Intellimouse support
- // Using 32 bit scroll pos functions instead of 16 bit ("cronos")
- // Added OLE drag and drop.
- // 1.04 6 Apr 1998 Added Ctrl-A = Select All, fixed CGridDropTarget
- // problem, minor bug in CopyTextFromGrid (assert on
- // empty string). Cleaned up reponse to m_bEditable
- // (OnDrop and Ctrl-X disabled)
- // 1.05 10 May 1998 Memory leak fixed. (Yuheng Zhao)
- // Changed OLE initialisation (Carlo Comino)
- // Added separate fore + background cell colours (Suggested by John Crane)
- // ExpandToFit etc cleaned up - now decreases and
- // increases cell sizes to fit client area.
- // Added notification messages for the grid's parent (Suggested by
- // Added GVIS_READONLY state
- // 1.06 20 May 1998 Added TAB key handling. (Daniela Rybarova)
- // Intellimouse code correction for whole page scrolling (Paul Grant)
- // Fixed 16 bit thumb track problems (now 32 bit) (Paul Grant)
- // Fixed accelerator key problem in CInPlaceEdit (Matt Weagle)
- // Fixed Stupid ClassWizard code parsing problem (Michael A. Barnhart)
- // Double buffering now programmatically selectable
- // Workaround for win95 drag and drop registration problem
- // Corrected UNICODE implementation of clipboard stuff
- // Dragging and dropping from a selection onto itself no
- // no longer causes the cells to be emptied
- // 1.07 28 Jul 1998 Added EnsureVisible. (Roelf Werkman)
- // Fixed delete key problem on read-only cells. (Serge Weinstock)
- // OnEndInPlaceEdit sends notification AFTER storing
- // the modified text in the cell.
- // Added CreateInPlaceEditControl to make it easier to
- // change the way cells are edited. (suggested by Chris Clark)
- // Added Set/GetGridColor.
- // CopyTextToClipboard and PasteTextToGrid problem with
- // blank cells fixed, and CopyTextToClipboard tweaked.
- // SetModified called when cutting text or hitting DEL. (Jonathan Watters)
- // Focus cell made visible when editing begins.
- // Blank lines now treated correctly when pasting data.
- // Removed ES_MULTILINE style from the default edit control.
- // Added virtual CreateCell(row, col) function.
- // Fonts now specified on a per-cell basis using Get/SetItemFont.
- // 1.08 6 Aug 1998 Ctrl+arrows now allows cell navigation. Modified
- // CreateInPlaceEditControl to accept ID of control.
- // Added Titletips to grid cells. (Added EnableTitleTips / GetTitleTips)
- // 1.09 12 Sep 1998 When printing, parent window title is printed in header - Gert Rijs
- // GetNextItem search with GVNI_DROPHILITED now returns
- // cells with GVIS_DROPHILITED set, instead of GVIS_FOCUSED (Franco Bez)
- // (Also fixed minor bug in GetNextItem) (Franco Bez)
- // Cell selection using Shift+arrows works - Franco Bez
- // SetModified called after edits ONLY if contents changed (Franco Bez)
- // Cell colours now dithered in 256 colour screens.
- // Support for MSVC 4.2 (Graham Cheetham)
- // 1.10 30 Nov 1998 Titletips now disappear on a scroll event. Compiler errors
- // fixed. Grid lines drawing fixed (Graham Cheetham).
- // Cell focus fix on Isert Row/Col (Jochen Kauffmann)
- // Added DeleteNonFixedRows() (John Rackley)
- // Message #define conflict fixed (Oskar Wieland)
- // Titletips & cell insert/delete fix (Ramesh Dhar)
- // Titletips repeat-creation bug fixed.
- // GVN_SELCHANGED message now sends current cell ID
- // Font resource leak in GetTextExtent fixed (Gavin Jerman)
- // More TAB fixes (Andreas Ruh)
- // 1.11 1 Dec 1998 GetNextItem bug fix (suggested by Francis Fu)
- // InsertColumn (-1) fix (Roy Hopkins)
- // Was too liberal with the "IsEditable"'s. oops. (Michel Hete)
- //
- // TODO:
- // - OnOutOfMemory function instead of exceptions
- // - Decrease timer interval over time to speed up selection over time
- //
- // NOTE: Grid data is stored row-by-row, so all operations on large numbers
- // of cells should be done row-by-row as well.
- //
- // KNOWN ISSUES TO BE ADDRESSED (Please don't send bug reports):
- // * Killfocus comes to late when a command is selected by the Menu.
- // When you are editing a cell and choose a Menuitem that searches for all the
- // modified cells it is not found. When you chose the menu a second time it is
- // found. I assume that the Menu command is executed before the cell receives the
- // KillFocus event. Expect similar Problems with accelerators. (Franco Bez)
- // * When you select a cell and move the mouse around (with the Left button down
- // i.e continuing with your selection) - if the mouse is over the Fixed column
- // or Row the drawing of the selected region is strange - in particular as you
- // move up and down say the Left Fixed Column notice the behaviour of the Focus
- // Cell - it is out of sync. (Vinay Desai)
- //
- /////////////////////////////////////////////////////////////////////////////
-
- #include "stdafx.h"
- #include "MemDC.h"
- #include "GridCtrl.h"
- #include "InPlaceEdit.h"
- #include "..PublicFunc.h"
- // OLE stuff for clipboard operations
- #include <afxadv.h> // For CSharedFile
- #include <afxole.h> // For COleDataSource
- #include <afxconv.h> // For LPTSTR -> LPSTR macros
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
- #define HEADER_HEIGHT 2 // For printing
- #define FOOTER_HEIGHT 2
- #define LEFT_MARGIN 4
- #define RIGHT_MARGIN 4
- #define TOP_MARGIN 1
- #define BOTTOM_MARGIN 1
- #define GAP 1
- #define SELECTED_CELL_FONT_WEIGHT 600 // wieght of text for selected items
- #define IDC_INPLACE_EDIT 8 // ID of inplace edit controls
- IMPLEMENT_DYNCREATE(CGridCtrl, CWnd)
- void AFXAPI DDX_GridControl(CDataExchange* pDX, int nIDC, CGridCtrl& rControl)
- {
- if (rControl.GetSafeHwnd() == NULL) // not subclassed yet
- {
- ASSERT(!pDX->m_bSaveAndValidate);
- HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
- if (!rControl.SubclassWindow(hWndCtrl))
- {
- ASSERT(FALSE); // possibly trying to subclass twice?
- AfxThrowNotSupportedException();
- }
- #ifndef _AFX_NO_OCC_SUPPORT
- else
- {
- // If the control has reparented itself (e.g., invisible control),
- // make sure that the CWnd gets properly wired to its control site.
- if (pDX->m_pDlgWnd->GetSafeHwnd() != ::GetParent(rControl.GetSafeHwnd()))
- rControl.AttachControlSite(pDX->m_pDlgWnd);
- }
- #endif //!_AFX_NO_OCC_SUPPORT
- }
- }
- // Get the number of lines to scroll with each mouse wheel notch
- // Why doesn't windows give us this function???
- UINT GetMouseScrollLines()
- {
- int nScrollLines = 3; // reasonable default
- HKEY hKey;
- if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\Desktop"),
- 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
- {
- TCHAR szData[128];
- DWORD dwKeyDataType;
- DWORD dwDataBufSize = sizeof(szData);
- if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
- (LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
- {
- nScrollLines = _tcstoul(szData, NULL, 10);
- }
- RegCloseKey(hKey);
- }
- return nScrollLines;
- }
- /////////////////////////////////////////////////////////////////////////////
- // CGridCtrl
- CGridCtrl::CGridCtrl(int nRows, int nCols, int nFixedRows, int nFixedCols)
- {
- RegisterWindowClass();
- // Initialize OLE libraries
- m_bIsYXGrid = FALSE;
- m_bMustUninitOLE = FALSE;
- #ifdef GRIDCONTROL_USE_OLE_DRAGDROP
- _AFX_THREAD_STATE* pState = AfxGetThreadState();
- if (!pState->m_bNeedTerm)
- {
- SCODE sc = ::OleInitialize(NULL);
- if (FAILED(sc))
- AfxMessageBox(_T("OLE initialization failed. Make sure that the OLE libraries are the correct version"));
- else
- m_bMustUninitOLE = TRUE;
- }
- #endif
- // Store the system colours in case they change. The gridctrl uses
- // these colours, and in OnSysColorChange we can check to see if
- // the gridctrl colours have been changed from the system colours.
- // If they have, then leave them, otherwise change them to reflect
- // the new system colours.
- m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
- m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
- m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
- m_crShadow = ::GetSysColor(COLOR_3DSHADOW);
- m_crGridColour = RGB(0,0,0);
- m_nRows = 0;
- m_nCols = 0;
- m_nFixedRows = 0;
- m_nFixedCols = 0;
- m_nDefCellHeight = 10; // These will get changed to something meaningful
- m_nDefCellHeight = 30; // when the window is created or subclassed
- m_nVScrollMax = 0; // Scroll position
- m_nHScrollMax = 0;
- m_nMargin = 0; // cell padding
- m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
- // per mouse wheel notch to scroll
- m_MouseMode = MOUSE_NOTHING;
- m_nGridLines = GVL_BOTH;
- m_bEditable = TRUE;
- m_bListMode = FALSE;
- m_bAllowDraw = TRUE; // allow draw updates
- m_bEnableSelection = TRUE;
- m_bAllowRowResize = TRUE;
- m_bAllowColumnResize = TRUE;
- m_bSortOnClick = TRUE; // Sort on header row click if in list mode
- m_bHandleTabKey = TRUE;
- m_bDoubleBuffer = TRUE; // Use double buffering to avoid flicker?
- m_bTitleTips = TRUE; // show cell title tips
- m_bAscending = TRUE; // sorting stuff
- m_SortColumn = -1;
- m_nTimerID = 0; // For drag-selection
- m_nTimerInterval = 25; // (in milliseconds)
- m_nResizeCaptureRange = 3; // When resizing columns/row, the cursor has to be
- // within +/-3 pixels of the dividing line for
- // resizing to be possible
- m_pImageList = NULL;
- m_bAllowDragAndDrop = FALSE; // for drag and drop
- // Initially use the system message font for the GridCtrl font
- NONCLIENTMETRICS ncm;
- ncm.cbSize = sizeof(NONCLIENTMETRICS);
- VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
- memcpy(&m_Logfont, &(ncm.lfMessageFont), sizeof(LOGFONT));
- // Set up the initial grid size
- SetRowCount(nRows);
- SetColumnCount(nCols);
- SetFixedRowCount(nFixedRows);
- SetFixedColumnCount(nFixedCols);
- // Set the colours
- SetTextColor(m_crWindowText);
- SetTextBkColor(m_crWindowColour);
- SetBkColor(m_crShadow);
- SetFixedTextColor(m_crWindowText);
- SetFixedBkColor(m_cr3DFace);
- // set initial selection range (ie. none)
- m_SelectedCellMap.RemoveAll();
- m_PrevSelectedCellMap.RemoveAll();
- }
- CGridCtrl::~CGridCtrl()
- {
- DeleteAllItems();
- DestroyWindow();
- m_Font.DeleteObject();
- // Uninitialize OLE support
- if (m_bMustUninitOLE)
- ::OleUninitialize();
- }
- // Register the window class if it has not already been registered.
- BOOL CGridCtrl::RegisterWindowClass()
- {
- WNDCLASS wndcls;
- HINSTANCE hInst = AfxGetResourceHandle();
- if (!(::GetClassInfo(hInst, GRIDCTRL_CLASSNAME, &wndcls)))
- {
- // otherwise we need to register a new class
- wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
- wndcls.lpfnWndProc = ::DefWindowProc;
- wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
- wndcls.hInstance = hInst;
- wndcls.hIcon = NULL;
- wndcls.hCursor = NULL;
- wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
- wndcls.lpszMenuName = NULL;
- wndcls.lpszClassName = GRIDCTRL_CLASSNAME;
- if (!AfxRegisterClass(&wndcls)) {
- AfxThrowResourceException();
- return FALSE;
- }
- }
- return TRUE;
- }
- BOOL CGridCtrl::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
- {
- ASSERT(pParentWnd->GetSafeHwnd());
- if (!CWnd::Create(GRIDCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
- return FALSE;
- #ifdef GRIDCONTROL_USE_OLE_DRAGDROP
- m_DropTarget.Register(this);
- #endif
- // Create titletips
- #ifdef GRIDCONTROL_USE_TITLETIPS
- if (m_bTitleTips)
- m_TitleTip.Create(this);
- #endif
- // The number of rows and columns will only be non-zero if the constructor
- // was called with non-zero initialising parameters. If this window was created
- // using a dialog template then the number of rows and columns will be 0 (which
- // means that the code below will not be needed - which is lucky 'cause it ain't
- // gonna get called in a dialog-template-type-situation.
- try {
- m_arRowHeights.SetSize(m_nRows); // initialize row heights
- m_arColWidths.SetSize(m_nCols); // initialize column widths
- }
- catch (CMemoryException *e) {
- e->ReportError();
- e->Delete();
- return FALSE;
- }
- for (int i = 0; i < m_nRows; i++) m_arRowHeights[i] = m_nDefCellHeight;
- for (i = 0; i < m_nCols; i++) m_arColWidths[i] = m_nDefCellWidth;
- ResetScrollBars();
- return TRUE;
- }
- void CGridCtrl::PreSubclassWindow()
- {
- CWnd::PreSubclassWindow();
- HFONT hFont = ::CreateFontIndirect(&m_Logfont);
- OnSetFont((LPARAM)hFont, 0);
- DeleteObject(hFont);
-
- ResetScrollBars();
- }
- BOOL CGridCtrl::SubclassWindow(HWND hWnd)
- {
- if (!CWnd::SubclassWindow(hWnd)) return FALSE;
- #ifdef GRIDCONTROL_USE_OLE_DRAGDROP
- m_DropTarget.Register(this);
- #endif
- #ifdef GRIDCONTROL_USE_TITLETIPS
- if (m_bTitleTips && !IsWindow(m_TitleTip.m_hWnd))
- m_TitleTip.Create(this);
- #endif
- return TRUE;
- }
- LRESULT CGridCtrl::SendMessageToParent(int nRow, int nCol, int nMessage)
- {
- if (!IsWindow(m_hWnd))
- return 0;
- NM_GRIDVIEW nmgv;
- nmgv.iRow = nRow;
- nmgv.iColumn = nCol;
- nmgv.hdr.hwndFrom = m_hWnd;
- nmgv.hdr.idFrom = GetDlgCtrlID();
- nmgv.hdr.code = nMessage;
- CWnd *pOwner = GetOwner();
- if (pOwner && IsWindow(pOwner->m_hWnd))
- return pOwner->SendMessage(WM_NOTIFY, nmgv.hdr.idFrom, (LPARAM)&nmgv);
- else
- return 0;
- }
- BEGIN_MESSAGE_MAP(CGridCtrl, CWnd)
- //{{AFX_MSG_MAP(CGridCtrl)
- ON_WM_PAINT()
- ON_WM_HSCROLL()
- ON_WM_VSCROLL()
- ON_WM_SIZE()
- ON_WM_LBUTTONUP()
- ON_WM_LBUTTONDOWN()
- ON_WM_MOUSEMOVE()
- ON_WM_TIMER()
- ON_WM_GETDLGCODE()
- ON_WM_KEYDOWN()
- ON_WM_CHAR()
- ON_WM_LBUTTONDBLCLK()
- ON_WM_ERASEBKGND()
- ON_WM_SETCURSOR()
- ON_WM_SYSCOLORCHANGE()
- ON_WM_CAPTURECHANGED()
- ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
- ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
- ON_COMMAND(ID_EDIT_CUT, OnEditCut)
- ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
- ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
- ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
- ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
- ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
- //}}AFX_MSG_MAP
- #if _MFC_VER >= 0x0421
- ON_WM_MOUSEWHEEL()
- ON_WM_SETTINGCHANGE()
- #endif
- ON_MESSAGE(WM_SETFONT, OnSetFont)
- ON_MESSAGE(WM_GETFONT, OnGetFont)
- ON_NOTIFY(GVN_ENDLABELEDIT, IDC_INPLACE_EDIT, OnEndInPlaceEdit)
- END_MESSAGE_MAP()
- /////////////////////////////////////////////////////////////////////////////
- // CGridCtrl message handlers
- void CGridCtrl::OnPaint()
- {
- CPaintDC dc(this); // device context for painting
- if (m_bDoubleBuffer) // Use a memory DC to remove flicker
- {
- CMemDC MemDC(&dc);
- OnDraw(&MemDC);
- }
- else // Draw raw - this helps in debugging vis problems.
- OnDraw(&dc);
- }
- BOOL CGridCtrl::OnEraseBkgnd(CDC* /*pDC*/)
- {
- return TRUE; // Don't erase the background.
- }
- // Custom background erasure. This gets called from within the OnDraw function,
- // since we will (most likely) be using a memory DC to stop flicker. If we just
- // erase the background normally through OnEraseBkgnd, and didn't fill the memDC's
- // selected bitmap with colour, then all sorts of vis problems would occur
- void CGridCtrl::EraseBkgnd(CDC* pDC)
- {
- CRect VisRect, ClipRect, rect;
- CBrush FixedBack(GetFixedBkColor()),
- TextBack(GetTextBkColor()),
- Back(GetBkColor());
- if (pDC->GetClipBox(ClipRect) == ERROR) return;
- GetVisibleNonFixedCellRange(VisRect);
- // Draw Fixed columns background
- int nFixedColumnWidth = GetFixedColumnWidth();
- if (ClipRect.left < nFixedColumnWidth && ClipRect.top < VisRect.bottom)
- pDC->FillRect(CRect(ClipRect.left, ClipRect.top, nFixedColumnWidth, VisRect.bottom),
- &FixedBack);
-
- // Draw Fixed rows background
- int nFixedRowHeight = GetFixedRowHeight();
- if (ClipRect.top < nFixedRowHeight &&
- ClipRect.right > nFixedColumnWidth && ClipRect.left < VisRect.right)
- pDC->FillRect(CRect(nFixedColumnWidth-1, ClipRect.top, VisRect.right, nFixedRowHeight),
- &FixedBack);
- // Draw non-fixed cell background
- if (rect.IntersectRect(VisRect, ClipRect))
- {
- CRect CellRect(max(nFixedColumnWidth, rect.left), max(nFixedRowHeight, rect.top),
- rect.right, rect.bottom);
- pDC->FillRect(CellRect, &TextBack);
- }
- // Draw right hand side of window outside grid
- if (VisRect.right < ClipRect.right)
- pDC->FillRect(CRect(VisRect.right, ClipRect.top, ClipRect.right, ClipRect.bottom),
- &Back);
- // Draw bottom of window below grid
- if (VisRect.bottom < ClipRect.bottom && ClipRect.left < VisRect.right)
- pDC->FillRect(CRect(ClipRect.left, VisRect.bottom, VisRect.right, ClipRect.bottom),
- &Back);
- }
- void CGridCtrl::OnSize(UINT nType, int cx, int cy)
- {
- // if (::IsWindow(GetSafeHwnd()) && GetFocus()->GetSafeHwnd() != GetSafeHwnd())
- SetFocus(); // Auto-destroy any InPlaceEdit's
- CWnd::OnSize(nType, cx, cy);
- ResetScrollBars();
- }
- UINT CGridCtrl::OnGetDlgCode()
- {
- UINT nCode = DLGC_WANTARROWS | DLGC_WANTCHARS;
- if (m_bHandleTabKey && !IsCTRLpressed())
- nCode |= DLGC_WANTTAB;
- return nCode;
- }
- // If system colours change, then redo colours
- void CGridCtrl::OnSysColorChange()
- {
- CWnd::OnSysColorChange();
-
- if (GetTextColor() == m_crWindowText) // Still using system colours
- SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
- if (GetTextBkColor() == m_crWindowColour)
- SetTextBkColor(::GetSysColor(COLOR_WINDOW));
- if (GetBkColor() == m_crShadow)
- SetBkColor(::GetSysColor(COLOR_3DSHADOW));
- if (GetFixedTextColor() == m_crWindowText)
- SetFixedTextColor(::GetSysColor(COLOR_WINDOWTEXT));
- if (GetFixedBkColor() == m_cr3DFace)
- SetFixedBkColor(::GetSysColor(COLOR_3DFACE));
- m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
- m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
- m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
- m_crShadow = ::GetSysColor(COLOR_3DSHADOW);
- }
- // If we are drag-selecting cells, or drag and dropping, stop now
- void CGridCtrl::OnCaptureChanged(CWnd *pWnd)
- {
- if (pWnd->GetSafeHwnd() == GetSafeHwnd()) return;
- // kill timer if active
- if (m_nTimerID != 0)
- {
- KillTimer(m_nTimerID);
- m_nTimerID = 0;
- }
- // Kill drag and drop if active
- if (m_MouseMode == MOUSE_DRAGGING)
- m_MouseMode = MOUSE_NOTHING;
- }
- #if _MFC_VER >= 0x0421
- // If system settings change, then redo colours
- void CGridCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
- {
- CWnd::OnSettingChange(uFlags, lpszSection);
-
- if (GetTextColor() == m_crWindowText) // Still using system colours
- SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
- if (GetTextBkColor() == m_crWindowColour)
- SetTextBkColor(::GetSysColor(COLOR_WINDOW));
- if (GetBkColor() == m_crShadow)
- SetBkColor(::GetSysColor(COLOR_3DSHADOW));
- if (GetFixedTextColor() == m_crWindowText)
- SetFixedTextColor(::GetSysColor(COLOR_WINDOWTEXT));
- if (GetFixedBkColor() == m_cr3DFace)
- SetFixedBkColor(::GetSysColor(COLOR_3DFACE));
- m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
- m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
- m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
- m_crShadow = ::GetSysColor(COLOR_3DSHADOW);
- m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
- }
- #endif
- // For drag-selection. Scrolls hidden cells into view
- // TODO: decrease timer interval over time to speed up selection over time
- void CGridCtrl::OnTimer(UINT nIDEvent)
- {
- ASSERT(nIDEvent == WM_LBUTTONDOWN);
- if (nIDEvent != WM_LBUTTONDOWN) return;
- CPoint pt, origPt;
- if (!GetCursorPos(&origPt)) return;
- ScreenToClient(&origPt);
- CRect rect;
- GetClientRect(rect);
- int nFixedRowHeight = GetFixedRowHeight();
- int nFixedColWidth = GetFixedColumnWidth();
- pt = origPt;
- if (pt.y > rect.bottom)
- {
- //SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
- SendMessage(WM_KEYDOWN, VK_DOWN, 0);
- if (pt.x < rect.left) pt.x = rect.left;
- if (pt.x > rect.right) pt.x = rect.right;
- pt.y = rect.bottom;
- OnSelecting(GetCellFromPt(pt));
- }
- else if (pt.y < nFixedRowHeight)
- {
- //SendMessage(WM_VSCROLL, SB_LINEUP, 0);
- SendMessage(WM_KEYDOWN, VK_UP, 0);
- if (pt.x < rect.left) pt.x = rect.left;
- if (pt.x > rect.right) pt.x = rect.right;
- pt.y = nFixedRowHeight + 1;
- OnSelecting(GetCellFromPt(pt));
- }
- pt = origPt;
- if (pt.x > rect.right)
- {
- // SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
- SendMessage(WM_KEYDOWN, VK_RIGHT, 0);
- if (pt.y < rect.top) pt.y = rect.top;
- if (pt.y > rect.bottom) pt.y = rect.bottom;
- pt.x = rect.right;
- OnSelecting(GetCellFromPt(pt));
- }
- else if (pt.x < nFixedColWidth)
- {
- //SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
- SendMessage(WM_KEYDOWN, VK_LEFT, 0);
- if (pt.y < rect.top) pt.y = rect.top;
- if (pt.y > rect.bottom) pt.y = rect.bottom;
- pt.x = nFixedColWidth + 1;
- OnSelecting(GetCellFromPt(pt));
- }
- }
- // move about with keyboard
- void CGridCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
- {
- if (!IsValid(m_idCurrentCell))
- {
- CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
- return;
- }
- CCellID next = m_idCurrentCell;
- BOOL bChangeLine = FALSE;
- if (IsCTRLpressed())
- {
- switch (nChar)
- {
- case 'A': OnEditSelectAll(); break;
- case 'X': OnEditCut(); break;
- case 'C': OnEditCopy(); break;
- case 'V': OnEditPaste(); break;
- }
- }
- switch (nChar)
- {
- case VK_DELETE:
- if (IsCellEditable(m_idCurrentCell.row, m_idCurrentCell.col))
- {
- SetItemText(m_idCurrentCell.row, m_idCurrentCell.col, _T(""));
- RedrawCell(m_idCurrentCell);
- SetModified(TRUE);
- }
- break;
- case VK_TAB:
- if (IsSHIFTpressed())
- {
- if (next.col > m_nFixedCols)
- next.col--;
- else if (next.col == m_nFixedCols && next.row > m_nFixedRows)
- {
- next.row--;
- next.col = GetColumnCount() - 1;
- bChangeLine = TRUE;
- }
- else
- CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
- }
- else
- {
- if (next.col < (GetColumnCount() - 1))
- next.col++;
- else if (next.col == (GetColumnCount() - 1) &&
- next.row < (GetRowCount() - 1) )
- {
- next.row++;
- next.col = m_nFixedCols;
- bChangeLine = TRUE;
- }
- else
- CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
- }
- break;
- case VK_DOWN:
- if (next.row < (GetRowCount() - 1))
- next.row++;
- break;
-
- case VK_UP:
- if (next.row > m_nFixedRows)
- next.row--;
- break;
- case VK_RIGHT:
- if (next.col < (GetColumnCount() - 1))
- next.col++;
- break;
-
- case VK_LEFT:
- if (next.col > m_nFixedCols)
- next.col--;
- break;
- case VK_NEXT:
- {
- CCellID idOldTopLeft = GetTopleftNonFixedCell();
- SendMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
- CCellID idNewTopLeft = GetTopleftNonFixedCell();
- int increment = idNewTopLeft.row - idOldTopLeft.row;
- if (increment) {
- next.row += increment;
- if (next.row > (GetRowCount() - 1))
- next.row = GetRowCount() - 1;
- }
- else
- next.row = GetRowCount() - 1;
- break;
- }
-
- case VK_PRIOR:
- {
- CCellID idOldTopLeft = GetTopleftNonFixedCell();
- SendMessage(WM_VSCROLL, SB_PAGEUP, 0);
- CCellID idNewTopLeft = GetTopleftNonFixedCell();
- int increment = idNewTopLeft.row - idOldTopLeft.row;
- if (increment)
- {
- next.row += increment;
- if (next.row < m_nFixedRows)
- next.row = m_nFixedRows;
- } else
- next.row = m_nFixedRows;
- break;
- }
-
- case VK_HOME:
- SendMessage(WM_VSCROLL, SB_TOP, 0);
- next.row = m_nFixedRows;
- break;
-
- case VK_END:
- SendMessage(WM_VSCROLL, SB_BOTTOM, 0);
- next.row = GetRowCount() - 1;
- break;
-
- default:
- CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
- }
-
- if (next != m_idCurrentCell)
- {
- // While moving with the Cursorkeys the current ROW/CELL will get selected
- // OR Selection will get expanded when SHIFT is pressed
- // Cut n paste from OnLButtonDown - Franco Bez
- // Added check for NULL mouse mode - Chris Maunder.
- if (m_MouseMode == MOUSE_NOTHING)
- {
- m_PrevSelectedCellMap.RemoveAll();
- m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
- if (!IsSHIFTpressed() || nChar == VK_TAB)
- m_SelectionStartCell = next;
- OnSelecting(next);
- m_MouseMode = MOUSE_NOTHING;
- }
- SetFocusCell(next);
- if (!IsCellVisible(next))
- {
- EnsureVisible(next); // Make sure cell is visible
- switch (nChar) {
- case VK_RIGHT:
- SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
- break;
- case VK_LEFT:
- SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
- break;
- case VK_DOWN:
- SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
- break;
-
- case VK_UP:
- SendMessage(WM_VSCROLL, SB_LINEUP, 0);
- break;
-
- case VK_TAB:
- if (IsSHIFTpressed())
- {
- if (bChangeLine)
- {
- SendMessage(WM_VSCROLL, SB_LINEUP, 0);
- SetScrollPos32(SB_HORZ, m_nHScrollMax);
- break;
- }
- else
- SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
- }
- else
- {
- if (bChangeLine)
- {
- SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
- SetScrollPos32(SB_HORZ, 0);
- break;
- }
- else
- SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
- }
- break;
- }
- Invalidate();
- }
- }
- }
- // Instant editing of cells when keys are pressed
- void CGridCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
- {
- if (!IsCTRLpressed() && m_MouseMode == MOUSE_NOTHING)
- {
- if (!m_bHandleTabKey || (m_bHandleTabKey && nChar != VK_TAB))
- OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, nChar);
- }
- CWnd::OnChar(nChar, nRepCnt, nFlags);
- }
- // Callback from any CInPlaceEdits that ended. This just calls OnEndEditCell,
- // refreshes the edited cell and moves onto next cell if the return character
- // from the edit says we should.
- void CGridCtrl::OnEndInPlaceEdit(NMHDR* pNMHDR, LRESULT* pResult)
- {
- GV_DISPINFO *pgvDispInfo = (GV_DISPINFO *)pNMHDR;
- GV_ITEM *pgvItem = &pgvDispInfo->item;
- // In case OnEndInPlaceEdit called as window is being destroyed
- if (!IsWindow(GetSafeHwnd()))
- return;
- // Only set as modified if (a) it actually was, and (b) ESC was not hit.
- if (pgvItem->lParam != VK_ESCAPE &&
- GetItemText(pgvItem->row, pgvItem->col) != pgvItem->szText)
- {
- SetModified(TRUE);
- }
- OnEndEditCell(pgvItem->row, pgvItem->col, pgvItem->szText);
- InvalidateCellRect(CCellID(pgvItem->row, pgvItem->col));
- SendMessageToParent(pgvItem->row, pgvItem->col, GVN_ENDLABELEDIT);
- switch (pgvItem->lParam)
- {
- case VK_DOWN:
- case VK_UP:
- case VK_RIGHT:
- case VK_LEFT:
- case VK_NEXT:
- case VK_PRIOR:
- case VK_HOME:
- case VK_END: OnKeyDown(pgvItem->lParam, 0, 0);
- OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, pgvItem->lParam);
- }
- *pResult = 0;
- }
- // Handle horz scrollbar notifications
- void CGridCtrl::OnHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
- {
- if (GetFocus()->GetSafeHwnd() != GetSafeHwnd())
- SetFocus(); // Auto-destroy any InPlaceEdit's
- #ifdef GRIDCONTROL_USE_TITLETIPS
- m_TitleTip.Hide(); // hide any titletips
- #endif
- int scrollPos = GetScrollPos32(SB_HORZ);
- CCellID idTopLeft = GetTopleftNonFixedCell();
- CRect rect;
- GetClientRect(rect);
- switch (nSBCode)
- {
- case SB_LINERIGHT:
- if (scrollPos < m_nHScrollMax)
- {
- int xScroll = GetColumnWidth(idTopLeft.col);
- SetScrollPos32(SB_HORZ, scrollPos + xScroll);
- if (GetScrollPos32(SB_HORZ) == scrollPos) break; // didn't work
- rect.left = GetFixedColumnWidth() + xScroll;
- ScrollWindow(-xScroll, 0, rect);
- rect.left = rect.right - xScroll;
- InvalidateRect(rect);
- }
- break;
- case SB_LINELEFT:
- if (scrollPos > 0 && idTopLeft.col > GetFixedColumnCount())
- {
- int xScroll = GetColumnWidth(idTopLeft.col-1);
- SetScrollPos32(SB_HORZ, max(0,scrollPos - xScroll));
- rect.left = GetFixedColumnWidth();
- ScrollWindow(xScroll, 0, rect);
- rect.right = rect.left + xScroll;
- InvalidateRect(rect);
- }
- break;
- case SB_PAGERIGHT:
- if (scrollPos < m_nHScrollMax)
- {
- rect.left = GetFixedColumnWidth();
- int offset = rect.Width();
- int pos = min(m_nHScrollMax, scrollPos + offset);
- SetScrollPos32(SB_HORZ, pos);
- rect.left = GetFixedColumnWidth();
- InvalidateRect(rect);
- }
- break;
- case SB_PAGELEFT:
- if (scrollPos > 0)
- {
- rect.left = GetFixedColumnWidth();
- int offset = -rect.Width();
- int pos = max(0, scrollPos + offset);
- SetScrollPos32(SB_HORZ, pos);
- rect.left = GetFixedColumnWidth();
- InvalidateRect(rect);
- }
- break;
- case SB_THUMBPOSITION:
- case SB_THUMBTRACK:
- {
- SetScrollPos32(SB_HORZ, GetScrollPos32(SB_HORZ, TRUE));
- rect.left = GetFixedColumnWidth();
- InvalidateRect(rect);
- }
- break;
- case SB_LEFT:
- if (scrollPos > 0)
- {
- SetScrollPos32(SB_HORZ, 0);
- Invalidate();
- }
- break;
- case SB_RIGHT:
- if (scrollPos < m_nHScrollMax)
- {
- SetScrollPos32(SB_HORZ, m_nHScrollMax);
- Invalidate();
- }
- break;
- default: break;
- }
- }
- // Handle vert scrollbar notifications
- void CGridCtrl::OnVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
- {
- if (GetFocus()->GetSafeHwnd() != GetSafeHwnd())
- SetFocus(); // Auto-destroy any InPlaceEdit's
- #ifdef GRIDCONTROL_USE_TITLETIPS
- m_TitleTip.Hide(); // hide any titletips
- #endif
- // Get the scroll position ourselves to ensure we get a 32 bit value
- int scrollPos = GetScrollPos32(SB_VERT);
- CCellID idTopLeft = GetTopleftNonFixedCell();
- CRect rect;
- GetClientRect(rect);
- switch (nSBCode)
- {
- case SB_LINEDOWN:
- if (scrollPos < m_nVScrollMax)
- {
- int yScroll = GetRowHeight(idTopLeft.row);
- SetScrollPos32(SB_VERT, scrollPos + yScroll);
- if (GetScrollPos32(SB_VERT) == scrollPos) break; // didn't work
- rect.top = GetFixedRowHeight() + yScroll;
- ScrollWindow( 0, -yScroll, rect);
- rect.top = rect.bottom - yScroll;
- InvalidateRect(rect);
- }
- break;
- case SB_LINEUP:
- if (scrollPos > 0 && idTopLeft.row > GetFixedRowCount())
- {
- int yScroll = GetRowHeight(idTopLeft.row-1);
- SetScrollPos32(SB_VERT, max(0, scrollPos - yScroll));
- rect.top = GetFixedRowHeight();
- ScrollWindow(0, yScroll, rect);
- rect.bottom = rect.top + yScroll;
- InvalidateRect(rect);
- }
- break;
- case SB_PAGEDOWN:
- if (scrollPos < m_nVScrollMax)
- {
- rect.top = GetFixedRowHeight();
- scrollPos = min(m_nVScrollMax, scrollPos + rect.Height());
- SetScrollPos32(SB_VERT, scrollPos);
- rect.top = GetFixedRowHeight();
- InvalidateRect(rect);
- }
- break;
- case SB_PAGEUP:
- if (scrollPos > 0)
- {
- rect.top = GetFixedRowHeight();
- int offset = -rect.Height();
- int pos = max(0, scrollPos + offset);
- SetScrollPos32(SB_VERT, pos);
- rect.top = GetFixedRowHeight();
- InvalidateRect(rect);
- }
- break;
- case SB_THUMBPOSITION:
- case SB_THUMBTRACK:
- {
- SetScrollPos32(SB_VERT, GetScrollPos32(SB_VERT, TRUE));
- rect.top = GetFixedRowHeight();
- InvalidateRect(rect);
- }
- break;
- case SB_TOP:
- if (scrollPos > 0)
- {
- SetScrollPos32(SB_VERT, 0);
- Invalidate();
- }
- break;
- case SB_BOTTOM:
- if (scrollPos < m_nVScrollMax)
- {
- SetScrollPos32(SB_VERT, m_nVScrollMax);
- Invalidate();
- }
- default: break;
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // CGridCtrl implementation functions
- void CGridCtrl::OnDraw(CDC* pDC)
- {
- CRect rect;
- int row,col;
- CRect clipRect;
- if (pDC->GetClipBox(&clipRect) == ERROR) return;
- EraseBkgnd(pDC); // OnEraseBkgnd does nothing, so erase bkgnd here.
- // This necessary since we may be using a Memory DC.
- int nFixedRowHeight = GetFixedRowHeight();
- int nFixedColWidth = GetFixedColumnWidth();
- CCellID idTopLeft = GetTopleftNonFixedCell();
- int minVisibleRow = idTopLeft.row,
- minVisibleCol = idTopLeft.col;
- CRect VisRect;
- CCellRange VisCellRange = GetVisibleNonFixedCellRange(VisRect);
- int maxVisibleRow = VisCellRange.GetMaxRow(),
- maxVisibleCol = VisCellRange.GetMaxCol();
- // draw top-left cells 0..m_nFixedRows-1, 0..m_nFixedCols-1
- rect.bottom = -1;
- for (row = 0; row < m_nFixedRows; row++)
- {
- rect.top = rect.bottom+1;
- rect.bottom = rect.top + GetRowHeight(row)-1;
- rect.right = -1;
- for (col = 0; col < m_nFixedCols; col++)
- {
- rect.left = rect.right+1;
- rect.right = rect.left + GetColumnWidth(col)-1;
- DrawFixedCell(pDC, row, col, rect);
- }
- }
-
- // draw fixed column cells: m_nFixedRows..n, 0..m_nFixedCols-1
- rect.bottom = nFixedRowHeight-1;
- for (row = minVisibleRow; row <= maxVisibleRow; row++)
- {
- rect.top = rect.bottom+1;
- rect.bottom = rect.top + GetRowHeight(row)-1;
- // rect.bottom = bottom pixel of previous row
- if (rect.top > clipRect.bottom) break; // Gone past cliprect
- if (rect.bottom < clipRect.top) continue; // Reached cliprect yet?
- rect.right = -1;
- for (col = 0; col < m_nFixedCols; col++)
- {
- rect.left = rect.right+1;
- rect.right = rect.left + GetColumnWidth(col)-1;
- if (rect.left > clipRect.right) break; // gone past cliprect
- if (rect.right < clipRect.left) continue; // Reached cliprect yet?
- DrawFixedCell(pDC, row, col, rect);
- }
- }
-
- // draw fixed row cells 0..m_nFixedRows, m_nFixedCols..n
- rect.bottom = -1;
- for (row = 0; row < m_nFixedRows; row++)
- {
- rect.top = rect.bottom+1;
- rect.bottom = rect.top + GetRowHeight(row)-1;
- // rect.bottom = bottom pixel of previous row
- if (rect.top > clipRect.bottom) break; // Gone past cliprect
- if (rect.bottom < clipRect.top) continue; // Reached cliprect yet?
- rect.right = nFixedColWidth-1;
- for (col = minVisibleCol; col <= maxVisibleCol; col++)
- {
- rect.left = rect.right+1;
- rect.right = rect.left + GetColumnWidth(col)-1;
- if (rect.left > clipRect.right) break; // gone past cliprect
- if (rect.right < clipRect.left) continue; // Reached cliprect yet?
- DrawFixedCell(pDC, row, col, rect);
- }
- }
-
- // draw rest of non-fixed cells
- rect.bottom = nFixedRowHeight-1;
- for (row = minVisibleRow; row <= maxVisibleRow; row++)
- {
- rect.top = rect.bottom+1;
- rect.bottom = rect.top + GetRowHeight(row)-1;
- // rect.bottom = bottom pixel of previous row
- if (rect.top > clipRect.bottom) break; // Gone past cliprect
- if (rect.bottom < clipRect.top) continue; // Reached cliprect yet?
- rect.right = nFixedColWidth-1;
- for (col = minVisibleCol; col <= maxVisibleCol; col++)
- {
- rect.left = rect.right+1;
- rect.right = rect.left + GetColumnWidth(col)-1;
- if (rect.left > clipRect.right) break; // gone past cliprect
- if (rect.right < clipRect.left) continue; // Reached cliprect yet?
- DrawCell(pDC, row, col, rect);
- }
- }
- CPen pen;
- try {
- pen.CreatePen(PS_SOLID, 0, m_crGridColour);
- }
- catch (CResourceException *e)
- {
- e->Delete();
- return;
- }
- pDC->SelectObject(&pen);
- // draw vertical lines (drawn at ends of cells)
- if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
- {
- int x = nFixedColWidth;
- for (col = minVisibleCol; col <= maxVisibleCol; col++) {
- x += GetColumnWidth(col);
- pDC->MoveTo(x-1, nFixedRowHeight);
- pDC->LineTo(x-1, VisRect.bottom);
- }
- }
-
- // draw horizontal lines (drawn at bottom of each cell)
- if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
- {
- int y = nFixedRowHeight;
- for (row = minVisibleRow; row <= maxVisibleRow; row++) {
- y += GetRowHeight(row);
- pDC->MoveTo(nFixedColWidth, y-1);
- pDC->LineTo(VisRect.right, y-1);
- }
- }
- pDC->SelectStockObject(NULL_PEN);
- #ifdef USE_MEMDC // Use a memDC for flicker free update
- }
- #else // Use normal DC - this helps in debugging
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////////////
- // CGridCtrl Cell selection stuff
- BOOL CGridCtrl::IsValid(int nRow, int nCol) const
- {
- return (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols);
- }
- BOOL CGridCtrl::IsValid(const CCellID& cell) const
- {
- return IsValid(cell.row, cell.col);
- }
- BOOL CGridCtrl::IsValid(const CCellRange& range) const
- {
- return (range.GetMinRow() >= 0 && range.GetMinCol() >= 0 &&
- range.GetMaxRow() >= 0 && range.GetMaxCol() >= 0 &&
- range.GetMaxRow() < m_nRows && range.GetMaxCol() < m_nCols &&
- range.GetMinRow() <= range.GetMaxRow() && range.GetMinCol() <= range.GetMaxCol());
- }
- // Enables/Disables redraw for certain operations like columns auto-sizing etc,
- // but not for user caused things such as selection changes.
- void CGridCtrl::SetRedraw(BOOL bAllowDraw, BOOL bResetScrollBars /* = FALSE */)
- {
- TRACE(_T("%s: Setting redraw to %sn"),
- GetRuntimeClass()->m_lpszClassName, bAllowDraw? _T("TRUE") : _T("FALSE"));
- if (bAllowDraw && !m_bAllowDraw) Invalidate();
- m_bAllowDraw = bAllowDraw;
- if (bResetScrollBars) ResetScrollBars();
- }
- // Forces a redraw of a cell immediately (using a direct DC construction,
- // or the supplied dc)
- BOOL CGridCtrl::RedrawCell(const CCellID& cell, CDC* pDC /* = NULL */)
- {
- return RedrawCell(cell.row, cell.col, pDC);
- }
- BOOL CGridCtrl::RedrawCell(int nRow, int nCol, CDC* pDC /* = NULL */)
- {
- BOOL bResult = TRUE;
- BOOL bMustReleaseDC = FALSE;
- if (!m_bAllowDraw || !IsCellVisible(nRow, nCol)) return FALSE;
- CRect rect;
- if (!GetCellRect(nRow, nCol, rect)) return FALSE;
- if (!pDC) {
- pDC = GetDC();
- if (pDC) bMustReleaseDC = TRUE;
- }
- if (pDC)
- {
- // Redraw cells directly
- if (nRow < m_nFixedRows || nCol < m_nFixedCols)
- bResult = DrawFixedCell(pDC, nRow, nCol, rect, TRUE);
- else
- bResult = DrawCell(pDC, nRow, nCol, rect, TRUE);
- // Since we have erased the background, we will need to redraw the gridlines
- CPen pen;
- try {
- pen.CreatePen(PS_SOLID, 0, m_crGridColour);
- } catch (...) {}
-
- CPen* pOldPen = (CPen*) pDC->SelectObject(&pen);
- if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
- {
- pDC->MoveTo(rect.left, rect.bottom);
- pDC->LineTo(rect.right+1, rect.bottom);
- }
- if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
- {
- pDC->MoveTo(rect.right, rect.top);
- pDC->LineTo(rect.right, rect.bottom+1);
- }
- pDC->SelectObject(pOldPen);
- } else
- InvalidateRect(rect, TRUE); // Could not get a DC - invalidate it anyway
- // and hope that OnPaint manages to get one
- if (bMustReleaseDC)
- ReleaseDC(pDC);
- return bResult;
- }
- // redraw a complete row
- BOOL CGridCtrl::RedrawRow(int row)
- {
- BOOL bResult = TRUE;
- CDC* pDC = GetDC();
- for (int col = 0; col < GetColumnCount(); col++)
- bResult = RedrawCell(row, col, pDC) && bResult;
- if (pDC) ReleaseDC(pDC);
- return bResult;
- }
- // redraw a complete column
- BOOL CGridCtrl::RedrawColumn(int col)
- {
- BOOL bResult = TRUE;
- CDC* pDC = GetDC();
- for (int row = 0; row < GetRowCount(); row++)
- bResult = RedrawCell(row, col, pDC) && bResult;
- if (pDC) ReleaseDC(pDC);
- return bResult;
- }
- // Sets the currently selected cell, returning the previous current cell
- CCellID CGridCtrl::SetFocusCell(int nRow, int nCol)
- {
- return SetFocusCell(CCellID(nRow, nCol));
- }
- CCellID CGridCtrl::SetFocusCell(CCellID cell)
- {
- if (cell == m_idCurrentCell)
- return m_idCurrentCell;
- CCellID idPrev = m_idCurrentCell;
- m_idCurrentCell = cell;
- if (IsValid(idPrev))
- {
- SendMessageToParent(idPrev.row, idPrev.col, GVN_SELCHANGING);
- SetItemState(idPrev.row, idPrev.col,
- GetItemState(idPrev.row, idPrev.col) & ~GVIS_FOCUSED);
- RedrawCell(idPrev);
- if (idPrev.col != m_idCurrentCell.col)
- for (int row = 0; row < m_nFixedRows; row++)
- RedrawCell(row, idPrev.col);
- if (idPrev.row != m_idCurrentCell.row)
- for (int col = 0; col < m_nFixedCols; col++)
- RedrawCell(idPrev.row, col);
- }
- if (IsValid(m_idCurrentCell)) {
- SetItemState(m_idCurrentCell.row, m_idCurrentCell.col,
- GetItemState(m_idCurrentCell.row, m_idCurrentCell.col) | GVIS_FOCUSED);
- RedrawCell(m_idCurrentCell);
- if (idPrev.col != m_idCurrentCell.col)
- for (int row = 0; row < m_nFixedRows; row++)
- RedrawCell(row, m_idCurrentCell.col);
- if (idPrev.row != m_idCurrentCell.row)
- for (int col = 0; col < m_nFixedCols; col++)
- RedrawCell(m_idCurrentCell.row, col);
- SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_SELCHANGED);
- }
- return idPrev;
- }
- // Sets the range of currently selected cells
- void CGridCtrl::SetSelectedRange(const CCellRange& Range,
- BOOL bForceRepaint /* = FALSE */)
- {
- SetSelectedRange(Range.GetMinRow(), Range.GetMinCol(), Range.GetMaxRow(), Range.GetMaxCol(),
- bForceRepaint);
- }
- void CGridCtrl::SetSelectedRange(int nMinRow, int nMinCol, int nMaxRow, int nMaxCol,
- BOOL bForceRepaint /* = FALSE */)
- {
- if (!m_bEnableSelection) return;
- CDC* pDC = NULL;
- if (bForceRepaint) pDC = GetDC();
- // Unselect all previously selected cells
- for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
- {
- DWORD key;
- CCellID cell;
- m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
- // Reset the selection flag on the cell
- if (IsValid(cell)) {
- SetItemState(cell.row, cell.col,
- GetItemState(cell.row, cell.col) & ~GVIS_SELECTED);
- // If this is to be reselected, continue on past the redraw
- if (nMinRow <= cell.row && cell.row <= nMaxRow &&
- nMinCol <= cell.col && cell.col <= nMaxCol)
- continue;
- if (bForceRepaint && pDC) // Redraw NOW
- RedrawCell(cell.row, cell.col, pDC);
- else
- InvalidateCellRect(cell); // Redraw at leisure
- }
- }
-
- // if any previous selected cells are to be retained (eg Ctrl is being held down)
- // then copy them to the newly created list, and mark all these cells as
- // selected
- for (pos = m_PrevSelectedCellMap.GetStartPosition(); pos != NULL; )
- {
- DWORD key;
- CCellID cell;
- m_PrevSelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
- if (!IsValid(cell)) continue;
- int nState = GetItemState(cell.row, cell.col);
- // Set state as Selected. This will add the cell to m_SelectedCells[]
- SetItemState(cell.row, cell.col, nState | GVIS_SELECTED);
- // Redraw (immediately or at leisure)
- if (bForceRepaint && pDC)
- RedrawCell(cell.row, cell.col, pDC);
- else
- InvalidateCellRect(cell);
- }
- // Now select all cells in the cell range specified. If the cell has already
- // been marked as selected (above) then ignore it.
- if (nMinRow >= 0 && nMinCol >= 0 && nMaxRow >= 0 && nMaxCol >= 0 &&
- nMaxRow < m_nRows && nMaxCol < m_nCols &&
- nMinRow <= nMaxRow && nMinCol <= nMaxCol)
- {
- for (int row = nMinRow; row <= nMaxRow; row++)
- for (int col = nMinCol; col <= nMaxCol; col++)
- {
- int nState = GetItemState(row, col);
- if (nState & GVIS_SELECTED) {
- continue; // Already selected - ignore
- }
- // Add to list of selected cells
- CCellID cell(row, col);
- // Set state as Selected. This will add the cell to m_SelectedCells[]
- SetItemState(row, col, nState | GVIS_SELECTED);
- // Redraw (immediately or at leisure)
- if (bForceRepaint && pDC)
- RedrawCell(row, col, pDC);
- else
- InvalidateCellRect(cell);
- }
- }
- // TRACE(_T("%d cells selected.n"), m_SelectedCellMap.GetCount());
- if (pDC != NULL) ReleaseDC(pDC);
- }
- // selects all cells
- void CGridCtrl::SelectAllCells()
- {
- if (!m_bEnableSelection) return;
- SetSelectedRange(m_nFixedRows, m_nFixedCols, GetRowCount()-1, GetColumnCount()-1);
- }
- // selects columns
- void CGridCtrl::SelectColumns(CCellID currentCell)
- {
- if (!m_bEnableSelection) return;
- //if (currentCell.col == m_idCurrentCell.col) return;
- if (currentCell.col < m_nFixedCols) return;
- if (!IsValid(currentCell)) return;
- SetSelectedRange(GetFixedRowCount(), min(m_SelectionStartCell.col, currentCell.col),
- GetRowCount()-1, max(m_SelectionStartCell.col, currentCell.col));
- }
- // selects rows
- void CGridCtrl::SelectRows(CCellID currentCell)
- {
- if (!m_bEnableSelection) return;
- //if (currentCell.row; == m_idCurrentCell.row) return;
- if (currentCell.row < m_nFixedRows) return;
- if (!IsValid(currentCell)) return;
- SetSelectedRange(min(m_SelectionStartCell.row, currentCell.row), GetFixedColumnCount(),
- max(m_SelectionStartCell.row, currentCell.row), GetColumnCount()-1);
- }
- // selects cells
- void CGridCtrl::SelectCells(CCellID currentCell)
- {
- if (!m_bEnableSelection) return;
- int row = currentCell.row;
- int col = currentCell.col;
- if (row < m_nFixedRows || col < m_nFixedCols) return;
- if (!IsValid(currentCell)) return;
- // Prevent unnecessary redraws
- //if (currentCell == m_LeftClickDownCell) return;
- //else if (currentCell == m_idCurrentCell) return;
- SetSelectedRange(min(m_SelectionStartCell.row, row), min(m_SelectionStartCell.col, col),
- max(m_SelectionStartCell.row, row), max(m_SelectionStartCell.col, col));
- }
- void CGridCtrl::OnSelecting(const CCellID& currentCell)
- {
- if (!m_bEnableSelection) return;
- switch(m_MouseMode)
- {
- case MOUSE_SELECT_ALL: SelectAllCells(); break;
- case MOUSE_SELECT_COL: SelectColumns(currentCell); break;
- case MOUSE_SELECT_ROW: SelectRows(currentCell); break;
- case MOUSE_SELECT_CELLS: SelectCells(currentCell); break;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Clipboard functions
- void CGridCtrl::CutSelectedText()
- {
- if (!IsEditable())
- return;
- // Clear contents of selected cells.
- for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
- {
- DWORD key;
- CCellID cell;
- m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
- if (!IsCellEditable(cell))
- continue;
- CGridCell* pCell = GetCell(cell.row, cell.col);
- if (pCell)
- {
- EmptyCell(pCell, cell.row, cell.col);
- SetModified(TRUE);
- }
- }
- Invalidate();
- }
- COleDataSource* CGridCtrl::CopyTextFromGrid()
- {
- USES_CONVERSION;
- CCellRange Selection = GetSelectedCellRange();
- if (!IsValid(Selection)) return NULL;
- // Write to shared file (REMEBER: CF_TEXT is ANSI, not UNICODE, so we need to convert)
- CSharedFile sf(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
- // Get a tab delimited string to copy to cache
- CString str;
- CGridCell *pCell;
- for (int row = Selection.GetMinRow(); row <= Selection.GetMaxRow(); row++)
- {
- str.Empty();
- for (int col = Selection.GetMinCol(); col <= Selection.GetMaxCol(); col++)
- {
- pCell = GetCell(row,col);
- if (pCell && (pCell->state & GVIS_SELECTED))
- {
- if (pCell->szText.IsEmpty())
- str += _T(" ");
- else
- str += pCell->szText;
- }
- if (col != Selection.GetMaxCol())
- str += _T("t");
- }
- if (row != Selection.GetMaxRow())
- str += _T("n");
- sf.Write(T2A(str.GetBuffer(1)), str.GetLength());
- str.ReleaseBuffer();
- }
- char c = ' ';
- sf.Write(&c, 1);
- DWORD dwLen = sf.GetLength();
- HGLOBAL hMem = sf.Detach();
- if (!hMem)
- return NULL;
- hMem = ::GlobalReAlloc(hMem, dwLen, GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT);
- if (!hMem)
- return NULL;
- // Cache data
- COleDataSource* pSource = new COleDataSource();
- pSource->CacheGlobalData(CF_TEXT, hMem);
- return pSource;
- }
- BOOL CGridCtrl::PasteTextToGrid(CCellID cell, COleDataObject* pDataObject)
- {
- if (!IsValid(cell) || !IsCellEditable(cell) || !pDataObject->IsDataAvailable(CF_TEXT))
- return FALSE;
- // Get the text from the COleDataObject
- HGLOBAL hmem = pDataObject->GetGlobalData(CF_TEXT);
- CMemFile sf((BYTE*) ::GlobalLock(hmem), ::GlobalSize(hmem));
- // CF_TEXT is ANSI text, so we need to allocate a char* buffer
- // to hold this.
- LPSTR szBuffer = new char[::GlobalSize(hmem)];
- if (!szBuffer)
- return FALSE;
- sf.Read(szBuffer, ::GlobalSize(hmem));
- ::GlobalUnlock(hmem);
- // Now store in generic TCHAR form so we no longer have to deal with
- // ANSI/UNICODE problems
- CString strText = szBuffer;
- delete szBuffer;
- // Parse text data and set in cells...
- strText.LockBuffer();
- CString strLine = strText;
- int nLine = 0;
- // Find the end of the first line
- int nIndex;
- do {
- int nColumn = 0;
- nIndex = strLine.Find(_T("n"));
- // Store the remaining chars after the newline
- CString strNext = (nIndex < 0)? _T("") : strLine.Mid(nIndex+1);
- // Remove all chars after the newline
- if (nIndex >= 0)
- strLine = strLine.Left(nIndex);
- // Make blank entries a "space"
- if (strLine.IsEmpty() && nIndex >= 0)
- strLine = _T(" ");
- LPTSTR szLine = strLine.GetBuffer(1);
- // Break the current line into tokens (tab or comma delimited)
- LPTSTR pszCellText = _tcstok(szLine, _T("t,n"));
- while (pszCellText != NULL)
- {
- CCellID TargetCell(cell.row + nLine, cell.col + nColumn);
- if (IsValid(TargetCell))
- {
- CString strCellText = pszCellText;
- strCellText.TrimLeft(); strCellText.TrimRight();
- SetItemText(TargetCell.row, TargetCell.col, strCellText);
- SetModified(TRUE);
- // Make sure cell is not selected to avoid data loss
- SetItemState(TargetCell.row, TargetCell.col,
- GetItemState(TargetCell.row, TargetCell.col) & ~GVIS_SELECTED);
- }
- pszCellText = _tcstok(NULL, _T("t,n"));
- nColumn++;
- }
- strLine.ReleaseBuffer();
- strLine = strNext;
- nLine++;
- } while (nIndex >= 0);
- strText.UnlockBuffer();
- Invalidate();
- return TRUE;
- }
- void CGridCtrl::OnBeginDrag()
- {
- if (!m_bAllowDragAndDrop) return;
- COleDataSource* pSource = CopyTextFromGrid();
- if (pSource)
- {
- SendMessageToParent(GetSelectedCellRange().GetTopLeft().row,
- GetSelectedCellRange().GetTopLeft().col,
- GVN_BEGINDRAG);
- m_MouseMode = MOUSE_DRAGGING;
- DROPEFFECT dropEffect = pSource->DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE);
- if (dropEffect & DROPEFFECT_MOVE)
- CutSelectedText();
- if (pSource)
- delete pSource; // Did not pass source to clipboard, so must delete
- }
- }
- DROPEFFECT CGridCtrl::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState,
- CPoint point)
- {
- // Any text data available for us?
- if (!m_bAllowDragAndDrop || !IsEditable() || !pDataObject->IsDataAvailable(CF_TEXT))
- return DROPEFFECT_NONE;
- // Find which cell we are over and drop-highlight it
- CCellID cell = GetCellFromPt(point, FALSE);
- // If not valid, set the previously drop-highlighted cell as no longer drop-highlighted
- if (!IsValid(cell))
- {
- OnDragLeave();
- m_LastDragOverCell = CCellID(-1,-1);
- return DROPEFFECT_NONE;
- }
- if (!IsCellEditable(cell))
- return DROPEFFECT_NONE;
- // Have we moved over a different cell than last time?
- if (cell != m_LastDragOverCell)
- {
- // Set the previously drop-highlighted cell as no longer drop-highlighted
- if (IsValid(m_LastDragOverCell)) {
- UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
- SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
- nState & ~GVIS_DROPHILITED);
- RedrawCell(m_LastDragOverCell);
- }
- m_LastDragOverCell = cell;
- // Set the new cell as drop-highlighted
- if (IsValid(m_LastDragOverCell)) {
- UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
- SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
- nState | GVIS_DROPHILITED);
- RedrawCell(m_LastDragOverCell);
- }
- }
- // Return an appropraite value of DROPEFFECT so mouse cursor is set properly
- if (dwKeyState & MK_CONTROL)
- return DROPEFFECT_COPY;
- else
- return DROPEFFECT_MOVE;
- }
- DROPEFFECT CGridCtrl::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState,
- CPoint point)
- {
- // Any text data available for us?
- if (!m_bAllowDragAndDrop || !pDataObject->IsDataAvailable(CF_TEXT))
- return DROPEFFECT_NONE;
- // Find which cell we are over and drop-highlight it
- m_LastDragOverCell = GetCellFromPt(point, FALSE);
- if (!IsValid(m_LastDragOverCell))
- return DROPEFFECT_NONE;
- if (!IsCellEditable(m_LastDragOverCell))
- return DROPEFFECT_NONE;
- if (IsValid(m_LastDragOverCell))
- {
- UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
- SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
- nState | GVIS_DROPHILITED);
- RedrawCell(m_LastDragOverCell);
- }
- // Return an appropraite value of DROPEFFECT so mouse cursor is set properly
- if (dwKeyState & MK_CONTROL)
- return DROPEFFECT_COPY;
- else
- return DROPEFFECT_MOVE;
- }
- void CGridCtrl::OnDragLeave()
- {
- // Set the previously drop-highlighted cell as no longer drop-highlighted
- if (IsValid(m_LastDragOverCell)) {
- UINT nState = GetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col);
- SetItemState(m_LastDragOverCell.row, m_LastDragOverCell.col,
- nState & ~GVIS_DROPHILITED);
- RedrawCell(m_LastDragOverCell);
- }
- }
- BOOL CGridCtrl::OnDrop(COleDataObject* pDataObject, DROPEFFECT /*dropEffect*/,
- CPoint /* point */)
- {
- BOOL bResult = FALSE;
- if (!m_bAllowDragAndDrop || !IsCellEditable(m_LastDragOverCell))
- return bResult;
- m_MouseMode = MOUSE_NOTHING;
- OnDragLeave();
- return PasteTextToGrid(m_LastDragOverCell, pDataObject);
- }
- void CGridCtrl::OnEditCut()
- {
- if (!IsEditable())
- return;
- COleDataSource* pSource = CopyTextFromGrid();
- if (!pSource) return;
- pSource->SetClipboard();
- CutSelectedText();
- }
- void CGridCtrl::OnEditCopy()
- {
- COleDataSource* pSource = CopyTextFromGrid();
- if (!pSource) return;
- pSource->SetClipboard();
- }
- void CGridCtrl::OnEditPaste()
- {
- if (!IsEditable())
- return;
- // Get the Focus cell, or if none, get the topleft (non-fixed) cell
- CCellID cell = GetFocusCell();
- if (!IsValid(cell)) cell = GetTopleftNonFixedCell();
- if (!IsValid(cell)) return;
- // Attach a COleDataObject to the clipboard and paste the data to the grid
- COleDataObject obj;
- if (obj.AttachClipboard())
- PasteTextToGrid(cell, &obj);
- }
- void CGridCtrl::OnEditSelectAll()
- {
- SelectAllCells();
- }
- void CGridCtrl::OnUpdateEditCopy(CCmdUI* pCmdUI)
- {
- CCellRange Selection = GetSelectedCellRange();
- pCmdUI->Enable(Selection.Count() && IsValid(Selection));
- }
- void CGridCtrl::OnUpdateEditCut(CCmdUI* pCmdUI)
- {
- CCellRange Selection = GetSelectedCellRange();
- pCmdUI->Enable(IsEditable() && Selection.Count() && IsValid(Selection));
- }
- void CGridCtrl::OnUpdateEditPaste(CCmdUI* pCmdUI)
- {
- CCellID cell = GetFocusCell();
- BOOL bCanPaste = IsValid(cell) && IsCellEditable(cell) &&
- ::IsClipboardFormatAvailable(CF_TEXT);
- pCmdUI->Enable(bCanPaste);
- }
- void CGridCtrl::OnUpdateEditSelectAll(CCmdUI* pCmdUI)
- {
- pCmdUI->Enable(m_bEnableSelection);
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // hittest-like functions
- // Get cell from point
- CCellID CGridCtrl::GetCellFromPt(CPoint point, BOOL bAllowFixedCellCheck /*=TRUE*/) const
- {
- CCellID idTopLeft = GetTopleftNonFixedCell();
- CCellID cellID; // return value
- // calculate column index
- int fixedColWidth = GetFixedColumnWidth();
- if (point.x < 0 || (!bAllowFixedCellCheck && point.x < fixedColWidth)) // not in window
- cellID.col = -1;
- else if (point.x < fixedColWidth) // in fixed col
- {
- int xpos = 0;
- for (int col = 0; col < m_nFixedCols; col++)
- {
- xpos += GetColumnWidth(col);
- if (xpos > point.x) break;
- }
- cellID.col = col;
- }
- else // in non-fixed col
- {
- int xpos = fixedColWidth;
- for (int col = idTopLeft.col; col < GetColumnCount(); col++)
- {
- xpos += GetColumnWidth(col);
- if (xpos > point.x) break;
- }
- if (col >= GetColumnCount())
- cellID.col = -1;
- else
- cellID.col = col;
- }
-
- // calculate row index
- int fixedRowHeight = GetFixedRowHeight();
- if (point.y < 0 || (!bAllowFixedCellCheck && point.y < fixedRowHeight)) // not in window
- cellID.row = -1;
- else if (point.y < fixedRowHeight) // in fixed col
- {
- int ypos = 0;
- for (int row = 0; row < m_nFixedRows; row++)
- {
- ypos += GetRowHeight(row);
- if (ypos > point.y) break;
- }
- cellID.row = row;
- }
- else
- {
- int ypos = fixedRowHeight;
- for (int row = idTopLeft.row; row < GetRowCount(); row++)
- {
- ypos += GetRowHeight(row);
- if (ypos > point.y) break;
- }
- if (row >= GetRowCount())
- cellID.row = -1;
- else
- cellID.row = row;
- }
- return cellID;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // CGridCtrl cellrange functions
- CCellID CGridCtrl::GetTopleftNonFixedCell() const
- {
- int nVertScroll = GetScrollPos(SB_VERT),
- nHorzScroll = GetScrollPos(SB_HORZ);
- int nColumn = m_nFixedCols, nRight = 0;
- while (nRight < nHorzScroll && nColumn < (GetColumnCount()-1))
- nRight += GetColumnWidth(nColumn++);
- int nRow = m_nFixedRows, nTop = 0;
- while (nTop < nVertScroll && nRow < (GetRowCount()-1))
- nTop += GetRowHeight(nRow++);
- //TRACE("TopLeft cell is row %d, col %dn",nRow, nColumn);
- return CCellID(nRow, nColumn);
- }
- // This gets even partially visible cells
- CCellRange CGridCtrl::GetVisibleNonFixedCellRange(LPRECT pRect /*=NULL*/) const
- {
- CRect rect;
- GetClientRect(rect);
- CCellID idTopLeft = GetTopleftNonFixedCell();
- // calc bottom
- int bottom = GetFixedRowHeight();
- for (int i = idTopLeft.row; i < GetRowCount(); i++)
- {
- bottom += GetRowHeight(i);
- if (bottom >= rect.bottom) {
- bottom = rect.bottom;
- break;
- }
- }
- int maxVisibleRow = min(i, GetRowCount() - 1);
-
- // calc right
- int right = GetFixedColumnWidth();
- for (i = idTopLeft.col; i < GetColumnCount(); i++)
- {
- right += GetColumnWidth(i);
- if (right >= rect.right) {
- right = rect.right;
- break;
- }
- }
- int maxVisibleCol = min(i, GetColumnCount() - 1);
- if (pRect) {
- pRect->left = pRect->top = 0;
- pRect->right = right;
- pRect->bottom = bottom;
- }
- return CCellRange(idTopLeft.row, idTopLeft.col, maxVisibleRow, maxVisibleCol);
- }
- // used by ResetScrollBars() - This gets only fully visible cells
- CCellRange CGridCtrl::GetUnobstructedNonFixedCellRange() const
- {
- CRect rect;
- GetClientRect(rect);
- CCellID idTopLeft = GetTopleftNonFixedCell();
- // calc bottom
- int bottom = GetFixedRowHeight();
- for (int i = idTopLeft.row; i < GetRowCount(); i++)
- {
- bottom += GetRowHeight(i);
- if (bottom >= rect.bottom) break;
- }
- int maxVisibleRow = min(i, GetRowCount() - 1);
- if (maxVisibleRow > 0 && bottom > rect.bottom) maxVisibleRow--;
-
- // calc right
- int right = GetFixedColumnWidth();
- for (i = idTopLeft.col; i < GetColumnCount(); i++)
- {
- right += GetColumnWidth(i);
- if (right >= rect.right) break;
- }
- int maxVisibleCol = min(i, GetColumnCount() - 1);
- if (maxVisibleCol > 0 && right > rect.right) maxVisibleCol--;
- return CCellRange(idTopLeft.row, idTopLeft.col, maxVisibleRow, maxVisibleCol);
- }
- // Returns the minimum bounding range of the current selection
- // If no selection, then the returned CCellRange will be invalid
- CCellRange CGridCtrl::GetSelectedCellRange() const
- {
- CCellRange Selection(GetRowCount(), GetColumnCount(), -1,-1);
- for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
- {
- DWORD key;
- CCellID cell;
- m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
- Selection.SetMinRow( min(Selection.GetMinRow(), cell.row) );
- Selection.SetMinCol( min(Selection.GetMinCol(), cell.col) );
- Selection.SetMaxRow( max(Selection.GetMaxRow(), cell.row) );
- Selection.SetMaxCol( max(Selection.GetMaxCol(), cell.col) );
- }
- return Selection;
- }
- // Returns ALL the cells in the grid
- CCellRange CGridCtrl::GetCellRange() const
- {
- return CCellRange(0, 0, GetRowCount() - 1, GetColumnCount() - 1);
- }
- void CGridCtrl::ResetSelectedRange()
- {
- SetSelectedRange(-1,-1,-1,-1);
- SetFocusCell(-1,-1);
- }
- // Get/Set scroll position using 32 bit functions
- int CGridCtrl::GetScrollPos32(int nBar, BOOL bGetTrackPos /* = FALSE */)
- {
- SCROLLINFO si;
- si.cbSize = sizeof(SCROLLINFO);
- if (bGetTrackPos)
- {
- if (GetScrollInfo(nBar, &si, SIF_TRACKPOS))
- return si.nTrackPos;
- }
- else
- {
- if (GetScrollInfo(nBar, &si, SIF_POS))
- return si.nPos;
- }
- return 0;
- }
- BOOL CGridCtrl::SetScrollPos32(int nBar, int nPos, BOOL bRedraw /* = TRUE */)
- {
- SCROLLINFO si;
- si.cbSize = sizeof(SCROLLINFO);
- si.fMask = SIF_POS;
- si.nPos = nPos;
- return SetScrollInfo(nBar, &si, bRedraw);
- }
- void CGridCtrl::ResetScrollBars()
- {
- if (!m_bAllowDraw || !::IsWindow(GetSafeHwnd()))
- return;
- CRect rect;
- GetClientRect(rect);
- rect.left += GetFixedColumnWidth();
- rect.top += GetFixedRowHeight();
- if (rect.left >= rect.right || rect.top >= rect.bottom) return;
- CRect VisibleRect(GetFixedColumnWidth(), GetFixedRowHeight(), rect.right, rect.bottom);
- CRect VirtualRect(GetFixedColumnWidth(), GetFixedRowHeight(), GetVirtualWidth(), GetVirtualHeight());
- CCellRange visibleCells = GetUnobstructedNonFixedCellRange();
- if (!IsValid(visibleCells)) return;
- SCROLLINFO si;
- si.cbSize = sizeof(SCROLLINFO);
- si.fMask = SIF_PAGE;
- si.nPage = VisibleRect.Width(); SetScrollInfo(SB_HORZ, &si, FALSE);
- si.nPage = VisibleRect.Height(); SetScrollInfo(SB_VERT, &si, FALSE);
- if (VisibleRect.Height() < VirtualRect.Height())
- m_nVScrollMax = VirtualRect.Height()-1; //+ GetRowHeight(visibleCells.GetTopLeft().row);
- else
- m_nVScrollMax = 0;
- if (VisibleRect.Width() < VirtualRect.Width())
- m_nHScrollMax = VirtualRect.Width()-1; //+ GetColumnWidth(visibleCells.GetTopLeft().col);
- else
- m_nHScrollMax = 0;
- ASSERT(m_nVScrollMax < INT_MAX && m_nHScrollMax < INT_MAX); // This should be fine :)
- SetScrollRange(SB_VERT, 0, m_nVScrollMax, TRUE);
- SetScrollRange(SB_HORZ, 0, m_nHScrollMax, TRUE);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Row/Column position functions
- // returns the top left point of the cell. Returns FALSE if cell not visible.
- BOOL CGridCtrl::GetCellOrigin(int nRow, int nCol, LPPOINT p) const
- {
- int i;
- if (!IsValid(nRow, nCol)) return FALSE;
- CCellID idTopLeft;
- if (nCol >= m_nFixedCols || nRow >= m_nFixedRows)
- idTopLeft = GetTopleftNonFixedCell();
- if ((nRow >= m_nFixedRows && nRow < idTopLeft.row) ||
- (nCol>= m_nFixedCols && nCol < idTopLeft.col))
- return FALSE;
- p->x = 0;
- if (nCol < m_nFixedCols) // is a fixed column
- for (i = 0; i < nCol; i++)
- p->x += GetColumnWidth(i);
- else { // is a scrollable data column
- for (i = 0; i < m_nFixedCols; i++)
- p->x += GetColumnWidth(i);
- for (i = idTopLeft.col; i < nCol; i++)
- p->x += GetColumnWidth(i);
- }
- p->y = 0;
- if (nRow < m_nFixedRows) // is a fixed row
- for (i = 0; i < nRow; i++)
- p->y += GetRowHeight(i);
- else { // is a scrollable data row
- for (i = 0; i < m_nFixedRows; i++)
- p->y += GetRowHeight(i);
- for (i = idTopLeft.row; i < nRow; i++)
- p->y += GetRowHeight(i);
- }
- return TRUE;
- }
- BOOL CGridCtrl::GetCellOrigin(const CCellID& cell, LPPOINT p) const
- {
- return GetCellOrigin(cell.row, cell.col, p);
- }
- // Returns the bounding box of the cell
- BOOL CGridCtrl::GetCellRect(const CCellID& cell, LPRECT pRect) const
- {
- return GetCellRect(cell.row, cell.col, pRect);
- }
- BOOL CGridCtrl::GetCellRect(int nRow, int nCol, LPRECT pRect) const
- {
- CPoint CellOrigin;
- if (!GetCellOrigin(nRow, nCol, &CellOrigin)) return FALSE;
- pRect->left = CellOrigin.x;
- pRect->top = CellOrigin.y;
- pRect->right = CellOrigin.x + GetColumnWidth(nCol)-1;
- pRect->bottom = CellOrigin.y + GetRowHeight(nRow)-1;
- //TRACE("Row %d, col %d: L %d, T %d, W %d, H %d: %d,%d - %d,%dn",
- // nRow,nCol, CellOrigin.x, CellOrigin.y, GetColumnWidth(nCol), GetRowHeight(nRow),
- // pRect->left, pRect->top, pRect->right, pRect->bottom);
- return TRUE;
- }
- // Returns the bounding box of a range of cells
- BOOL CGridCtrl::GetCellRangeRect(const CCellRange& cellRange, LPRECT lpRect) const
- {
- CPoint MinOrigin,MaxOrigin;
- if (!GetCellOrigin(cellRange.GetMinRow(), cellRange.GetMinCol(), &MinOrigin))
- return FALSE;
- if (!GetCellOrigin(cellRange.GetMaxRow(), cellRange.GetMaxCol(), &MaxOrigin))
- return FALSE;
- lpRect->left = MinOrigin.x;
- lpRect->top = MinOrigin.y;
- lpRect->right = MaxOrigin.x + GetColumnWidth(cellRange.GetMaxCol()-1);
- lpRect->bottom = MaxOrigin.y + GetRowHeight(cellRange.GetMaxRow()-1);
- return TRUE;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Grid attribute functions
- LRESULT CGridCtrl::OnSetFont(WPARAM hFont, LPARAM /*lParam */)
- {
- LRESULT result = Default();
- // Get the logical font
- LOGFONT lf;
- if (!GetObject((HFONT) hFont, sizeof(LOGFONT), &lf))
- return result;
-
- // Store font as the global default
- memcpy(&m_Logfont, &lf, sizeof(LOGFONT));
-
- // reset all cells' fonts
- for (int row = 0; row < GetRowCount(); row++)
- for (int col = 0; col < GetColumnCount(); col++)
- SetItemFont(row, col, &lf);
- // Get the font size and hence the default cell size
- CDC* pDC = GetDC();
- if (pDC)
- {
- m_Font.DeleteObject();
- m_Font.CreateFontIndirect(&m_Logfont);
- CFont* pOldFont = pDC->SelectObject(&m_Font);
-
- TEXTMETRIC tm;
- pDC->GetTextMetrics(&tm);
-
- m_nMargin = pDC->GetTextExtent(_T(" "),1).cx;
- pDC->SelectObject(pOldFont);
- ReleaseDC(pDC);
- m_nDefCellHeight = tm.tmHeight+tm.tmExternalLeading + 2*m_nMargin;
- m_nDefCellWidth = tm.tmAveCharWidth*12 + 2*m_nMargin;
- }
- if (::IsWindow(GetSafeHwnd()))
- Invalidate();
- return result;
- }
- LRESULT CGridCtrl::OnGetFont(WPARAM /*wParam*/, LPARAM /*lParam*/)
- {
- return (LRESULT) (HFONT) m_Font;
- }
- BOOL CGridCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
- {
- if (nHitTest == HTCLIENT)
- {
- switch (m_MouseMode)
- {
- case MOUSE_OVER_COL_DIVIDE: SetCursor(::LoadCursor(NULL, IDC_SIZEWE)); break;
- case MOUSE_OVER_ROW_DIVIDE: SetCursor(::LoadCursor(NULL, IDC_SIZENS)); break;
- case MOUSE_DRAGGING: break;
- default: SetCursor(::LoadCursor(NULL, IDC_ARROW));
- }
- return TRUE;
- }
- return CWnd::OnSetCursor(pWnd, nHitTest, message);
- }
- ////////////////////////////////////////////////////////////////////////////////////
- // Row/Column count functions
- BOOL CGridCtrl::SetFixedRowCount(int nFixedRows)
- {
- ASSERT(nFixedRows >= 0);
- if (nFixedRows > GetRowCount())
- if (!SetRowCount(nFixedRows)) return FALSE;
- if (m_idCurrentCell.row < nFixedRows)
- SetFocusCell(-1,-1);
- m_nFixedRows = nFixedRows;
- if (GetSafeHwnd() && m_bAllowDraw)
- Invalidate();
- return TRUE;
- }
- BOOL CGridCtrl::SetFixedColumnCount(int nFixedCols)
- {
- ASSERT(nFixedCols >= 0);
- if (nFixedCols > GetColumnCount())
- if (!SetColumnCount(nFixedCols)) return FALSE;
- if (m_idCurrentCell.col < nFixedCols)
- SetFocusCell(-1,-1);
- m_nFixedCols = nFixedCols;
- if (GetSafeHwnd() && m_bAllowDraw)
- Invalidate();
- return TRUE;
- }
- BOOL CGridCtrl::SetRowCount(int nRows)
- {
- ASSERT(nRows >= 0);
- if (nRows == GetRowCount()) return TRUE;
- if (nRows < m_nFixedRows)
- m_nFixedRows = nRows;
- if (m_idCurrentCell.row >= nRows)
- SetFocusCell(-1,-1);
- int addedRows = nRows - GetRowCount();
- // If we are about to lose rows, then we need to delete the GridCell objects
- // in each column within each row
- if (addedRows < 0) {
- for (int row = nRows; row < m_nRows; row++)
- {
- // Delete cells
- for (int col = 0; col < m_nCols; col++)
- {
- CGridCell* pCell = GetCell(row, col);
- if (pCell) {
- EmptyCell(pCell, row, col);