代码拉取完成,页面将自动刷新
// GridCtrl.cpp : implementation file
//
// MFC Grid Control v2.26
//
// Written by Chris Maunder <[email protected]>
// Copyright (c) 1998-2005. All Rights Reserved.
//
// The code contained in this file was based on the original
// WorldCom Grid control written by Joe Willcoxson,
// mailto:[email protected]
// http://users.aol.com/chinajoe
// (These addresses may be out of date) The code has gone through
// so many modifications that I'm not sure if there is even a single
// original line of code. In any case Joe's code was a great
// framework on which to build.
//
// 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.
//
// An email letting me know how you are using it would be nice as well.
//
// 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 - 1.13 20 Feb 1998 - 6 May 1999
// First release version. Progressed from being a basic
// grid based on the original WorldCom Grid control
// written by Joe Willcoxson (mailto:[email protected],
// http://users.aol.com/chinajoe) to something a little
// more feature rich. Rewritten so many times I doubt
// there is a single line of Joe's code left. Many, many,
// MANY people sent in bug reports and fixes. Thank you
// all.
//
// 2.0 1 Feb 2000
// Rewritten to make the grid more object oriented, in
// that the CGridCell class now takes care of cell-specific
// tasks. This makes the code more robust, but more
// importantly it allows the simple insertion of other
// types of cells.
//
// 2.10 11 Mar 2000 - Ken Bertelson and Chris Maunder
// - Additions for virtual CGridCell support of embedded tree
// & cell buttons implementation
// - Optional WYSIWYG printing
// - Awareness of hidden (0 width/height) rows and columns for
// key movements, cut, copy, paste, and autosizing
// - CGridCell can make title tips display any text rather than
// cell text only
// - Minor vis bug fixes
// - CGridCtrl now works with CGridCellBase instead of CGridCell
// This is a taste of things to come.
//
// 2.20 30 Jul 2000 - Chris Maunder
// - Font storage optimised (suggested by Martin Richter)
// - AutoSizeColumn works on either column header, data or both
// - EnsureVisible. The saga continues... (Ken)
// - Rewrote exception handling
// - Added TrackFocusCell and FrameFocusCell properties, as well as
// ExpandLastColumn (suggested by Bruce E. Stemplewski).
// - InsertColumn now allows you to insert columns at the end of the
// column range (David Weibel)
// - Shift-cell-selection more intuitive
// - API change: Set/GetGridColor now Set/GetGridLineColor
// - API change: Set/GetBkColor now Set/GetGridBkColor
// - API change: Set/GetTextColor, Set/GetTextBkColor depricated
// - API change: Set/GetFixedTextColor, Set/GetFixedBkColor depricated
// - Stupid DDX_GridControl workaround removed.
// - Added "virtual mode" via Set/GetVirtualMode
// - Added SetCallbackFunc to allow callback functions in virtual mode
// - Added Set/GetAutoSizeStyle
// - AutoSize() bug fixed
// - added GVIS_FIXEDROW, GVIS_FIXEDCOL states
// - added Get/SetFixed[Row|Column]Selection
// - cell "Get" methods now const'd. Sorry folks...
// - GetMouseScrollLines now uses win98/W2K friendly code
// - WS_EX_CLIENTEDGE style now implicit
//
// [ Only the latest version and major version changes will be shown ]
////
// 2.25 13 Mar 2004 - Chris Maunder
// - Minor changes so it will compile in VS.NET (inc. Whidbey)
// - Fixed minor bug in EnsureVisible - Junlin Xu
// - Changed AfxGetInstanceHandle for AfxGetResourceHandle in RegisterWindowClass
// - Various changes thanks to Yogurt
//
// 2.26 13 Dec 2005 - Pierre Couderc
// - Added sort in Virtual mode
// - Change row/column order programatically or via drag and drop
// - Added save/restore layer (for undoing row/column order changes)
//
// TODO: 1) Implement sparse grids (super easy now)
// 2) Fix it so that as you drag select, the speed of selection increases
// with time.
// 3) Scrolling is still a little dodgy (too much grey area). I know there
// is a simple fix but it's been a low priority
//
// ISSUES: 1) WindowFromPoint seems to do weird things in W2K. Causing problems for
// the rigt-click-on-titletip code.
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "GridCtrl.h"
#include <algorithm>
namespace gridctrl {
#include "MemDC.h"
}
// OLE stuff for clipboard operations
#include <afxadv.h> // For CSharedFile
#include <afxconv.h> // For LPTSTR -> LPSTR macros
#pragma warning(disable:4996)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Spit out some messages as a sanity check for programmers
#ifdef GRIDCONTROL_NO_TITLETIPS
#pragma message(" -- CGridCtrl: No titletips for cells with large data")
#endif
#ifdef GRIDCONTROL_NO_DRAGDROP
#pragma message(" -- CGridCtrl: No OLE drag and drop")
#endif
#ifdef GRIDCONTROL_NO_CLIPBOARD
#pragma message(" -- CGridCtrl: No clipboard support")
#endif
#ifdef GRIDCONTROL_NO_PRINTING
#pragma message(" -- CGridCtrl: No printing support")
#endif
IMPLEMENT_DYNCREATE(CGridCtrl, CWnd)
// 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
#ifndef _WIN32_WCE
// Do things the hard way in win95
OSVERSIONINFO VersionInfo;
VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (!GetVersionEx(&VersionInfo) ||
(VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && VersionInfo.dwMinorVersion == 0))
{
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);
}
}
// win98 or greater
else
SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0, &nScrollLines, 0);
#endif
return nScrollLines;
}
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl
CGridCtrl::CGridCtrl(int nRows, int nCols, int nFixedRows, int nFixedCols)
{
RegisterWindowClass();
#if !defined(GRIDCONTROL_NO_DRAGDROP) || !defined(GRIDCONTROL_NO_CLIPBOARD)
_AFX_THREAD_STATE* pState = AfxGetThreadState();
if (!pState->m_bNeedTerm && !AfxOleInit())
AfxMessageBox(_T("OLE initialization failed. Make sure that the OLE libraries are the correct version"));
#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_crGridLineColour = RGB(192,192,192);
m_nRows = 0;
m_nCols = 0;
m_nFixedRows = 0;
m_nFixedCols = 0;
m_InDestructor = false;
m_bVirtualMode = FALSE;
m_pfnCallback = NULL;
m_nVScrollMax = 0; // Scroll position
m_nHScrollMax = 0;
m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
// per mouse wheel notch to scroll
m_nBarState = GVL_NONE;
m_MouseMode = MOUSE_NOTHING;
m_nGridLines = GVL_BOTH;
m_bEditable = TRUE;
m_bListMode = FALSE;
m_bSingleRowSelection = FALSE;
m_bSingleColSelection = FALSE;
m_bLMouseButtonDown = FALSE;
m_bRMouseButtonDown = FALSE;
m_bAllowDraw = TRUE; // allow draw updates
m_bEnableSelection = TRUE;
m_bFixedColumnSelection = TRUE;
m_bFixedRowSelection = TRUE;
m_bAllowRowResize = TRUE;
m_bAllowColumnResize = TRUE;
m_bSortOnClick = FALSE; // Sort on header row click
m_bHandleTabKey = TRUE;
#ifdef _WIN32_WCE
m_bDoubleBuffer = FALSE; // Use double buffering to avoid flicker?
#else
m_bDoubleBuffer = TRUE; // Use double buffering to avoid flicker?
#endif
m_bTitleTips = TRUE; // show cell title tips
m_bWysiwygPrinting = FALSE; // use size-to-width printing
m_bHiddenColUnhide = TRUE; // 0-width columns can be expanded via mouse
m_bHiddenRowUnhide = TRUE; // 0-Height rows can be expanded via mouse
m_bAllowColHide = TRUE; // Columns can be contracted to 0-width via mouse
m_bAllowRowHide = TRUE; // Rows can be contracted to 0-height via mouse
m_bAscending = TRUE; // sorting stuff
m_nSortColumn = -1;
m_pfnCompare = NULL;
m_pfnVirtualCompare = NULL;
m_nAutoSizeColumnStyle = GVS_BOTH; // Autosize grid using header and data info
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; // Images in the grid
m_bAllowDragAndDrop = FALSE; // for drag and drop - EFW - off by default
m_bTrackFocusCell = TRUE; // Track Focus cell?
m_bFrameFocus = TRUE; // Frame the selected cell?
m_AllowReorderColumn = false;
m_QuitFocusOnTab = false;
m_AllowSelectRowInFixedCol = false;
m_bDragRowMode = TRUE; // allow to drop a line over another one to change row order
m_pRtcDefault = RUNTIME_CLASS(CGridCell);
SetupDefaultCells();
SetGridBkColor(m_crShadow);
// Set up the initial grid size
SetRowCount(nRows);
SetColumnCount(nCols);
SetFixedRowCount(nFixedRows);
SetFixedColumnCount(nFixedCols);
SetTitleTipTextClr(CLR_DEFAULT); //FNA
SetTitleTipBackClr(CLR_DEFAULT);
// set initial selection range (ie. none)
m_SelectedCellMap.RemoveAll();
m_PrevSelectedCellMap.RemoveAll();
#if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
// EFW - Added to support shaded/unshaded printout and
// user-definable margins.
m_bShadedPrintOut = TRUE;
SetPrintMarginInfo(2, 2, 4, 4, 1, 1, 1);
#endif
}
CGridCtrl::~CGridCtrl()
{
m_InDestructor = true;
DeleteAllItems();
#ifndef GRIDCONTROL_NO_TITLETIPS
if (m_bTitleTips && ::IsWindow(m_TitleTip.GetSafeHwnd()))
m_TitleTip.DestroyWindow();
#endif
DestroyWindow();
#if !defined(GRIDCONTROL_NO_DRAGDROP) || !defined(GRIDCONTROL_NO_CLIPBOARD)
// BUG FIX - EFW
COleDataSource *pSource = COleDataSource::GetClipboardOwner();
if(pSource)
COleDataSource::FlushClipboard();
#endif
}
// Register the window class if it has not already been registered.
BOOL CGridCtrl::RegisterWindowClass()
{
WNDCLASS wndcls;
//HINSTANCE hInst = AfxGetInstanceHandle();
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;
#ifndef _WIN32_WCE_NO_CURSOR
wndcls.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
#else
wndcls.hCursor = 0;
#endif
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = GRIDCTRL_CLASSNAME;
if (!AfxRegisterClass(&wndcls))
{
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}
BOOL CGridCtrl::Initialise()
{
// Stop re-entry problems
static BOOL bInProcedure = FALSE;
if (bInProcedure)
return FALSE;
bInProcedure = TRUE;
#ifndef GRIDCONTROL_NO_TITLETIPS
m_TitleTip.SetParentWnd(this);
#endif
// This would be a good place to register the droptarget but
// unfortunately this causes problems if you are using the
// grid in a view.
// Moved from OnSize.
//#ifndef GRIDCONTROL_NO_DRAGDROP
// m_DropTarget.Register(this);
//#endif
if (::IsWindow(m_hWnd))
ModifyStyleEx(0, WS_EX_CLIENTEDGE);
// Kludge: Make sure the client edge shows
// This is so horrible it makes my eyes water.
CRect rect;
GetWindowRect(rect);
CWnd* pParent = GetParent();
if (pParent != NULL)
pParent->ScreenToClient(rect);
rect.InflateRect(1,1); MoveWindow(rect);
rect.DeflateRect(1,1); MoveWindow(rect);
bInProcedure = FALSE;
return TRUE;
}
// creates the control - use like any other window create control
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;
//Initialise(); - called in PreSubclassWnd
// 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();
return FALSE;
}
END_CATCH
int i;
for (i = 0; i < m_nRows; i++)
m_arRowHeights[i] = m_cellDefault.GetHeight();
for (i = 0; i < m_nCols; i++)
m_arColWidths[i] = m_cellDefault.GetWidth();
return TRUE;
}
void CGridCtrl::SetupDefaultCells()
{
m_cellDefault.SetGrid(this); // Normal editable cell
m_cellFixedColDef.SetGrid(this); // Cell for fixed columns
m_cellFixedRowDef.SetGrid(this); // Cell for fixed rows
m_cellFixedRowColDef.SetGrid(this); // Cell for area overlapped by fixed columns/rows
m_cellDefault.SetTextClr(m_crWindowText);
m_cellDefault.SetBackClr(m_crWindowColour);
m_cellFixedColDef.SetTextClr(m_crWindowText);
m_cellFixedColDef.SetBackClr(m_cr3DFace);
m_cellFixedRowDef.SetTextClr(m_crWindowText);
m_cellFixedRowDef.SetBackClr(m_cr3DFace);
m_cellFixedRowColDef.SetTextClr(m_crWindowText);
m_cellFixedRowColDef.SetBackClr(m_cr3DFace);
}
void CGridCtrl::PreSubclassWindow()
{
CWnd::PreSubclassWindow();
//HFONT hFont = ::CreateFontIndirect(m_cellDefault.GetFont());
//OnSetFont((LPARAM)hFont, 0);
//DeleteObject(hFont);
Initialise();
}
// Sends a message to the parent in the form of a WM_NOTIFY message with
// a NM_GRIDVIEW structure attached
LRESULT CGridCtrl::SendMessageToParent(int nRow, int nCol, int nMessage) const
{
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;
}
// Send a request to the parent to return information on a given cell
LRESULT CGridCtrl::SendDisplayRequestToParent(GV_DISPINFO* pDisplayInfo) const
{
if (!IsWindow(m_hWnd))
return 0;
// Fix up the message headers
pDisplayInfo->hdr.hwndFrom = m_hWnd;
pDisplayInfo->hdr.idFrom = GetDlgCtrlID();
pDisplayInfo->hdr.code = GVN_GETDISPINFO;
// Send the message
CWnd *pOwner = GetOwner();
if (pOwner && IsWindow(pOwner->m_hWnd))
return pOwner->SendMessage(WM_NOTIFY, pDisplayInfo->hdr.idFrom, (LPARAM)pDisplayInfo);
else
return 0;
}
// Send a hint to the parent about caching information
LRESULT CGridCtrl::SendCacheHintToParent(const CCellRange& range) const
{
if (!IsWindow(m_hWnd))
return 0;
GV_CACHEHINT CacheHint;
// Fix up the message headers
CacheHint.hdr.hwndFrom = m_hWnd;
CacheHint.hdr.idFrom = GetDlgCtrlID();
CacheHint.hdr.code = GVN_ODCACHEHINT;
CacheHint.range = range;
// Send the message
CWnd *pOwner = GetOwner();
if (pOwner && IsWindow(pOwner->m_hWnd))
return pOwner->SendMessage(WM_NOTIFY, CacheHint.hdr.idFrom, (LPARAM)&CacheHint);
else
return 0;
}
#define LAYER_SIGNATURE (0x5FD4E64)
int CGridCtrl::GetLayer(int** pLayer) // used to save and restore order of columns
{ // gives back the size of the area (do not forget to delete pLayer)
int Length = 2+GetColumnCount()*2;
int *Layer = new int[Length]; // the caller is supposed to delete it
Layer[0]= LAYER_SIGNATURE;
Layer[1]= GetColumnCount();
memcpy(&Layer[2], &m_arColOrder[0], GetColumnCount()*sizeof(int));
memcpy(&Layer[2+GetColumnCount()], &m_arColWidths[0], GetColumnCount()*sizeof(int));
*pLayer = Layer;
return Length;
}
void CGridCtrl::SetLayer(int* pLayer)
{ // coming from a previous GetLayer (ignored if not same number of column, or the same revision number)
if(pLayer[0] != LAYER_SIGNATURE) return;
if(pLayer[1] != GetColumnCount()) return;
/* TRACE(" %d == %d \n",m_arColOrder[0],pLayer[2]);
TRACE(" %d == %d \n",m_arColOrder[1],pLayer[3]);
TRACE(" %d == %d \n",m_arColOrder[2],pLayer[4]);
TRACE(" %d == %d \n",m_arColWidths[0],pLayer[2+3]);
TRACE(" %d == %d \n",m_arColWidths[1],pLayer[3+3]);
TRACE(" %d == %d \n",m_arColWidths[2],pLayer[4+3]);
TRACE(" %d == %d \n",GetColumnCount(),3);
ASSERT(m_arColOrder[0]==pLayer[2]);
ASSERT(m_arColOrder[1]==pLayer[3]);
ASSERT(m_arColOrder[2]==pLayer[4]);
ASSERT(m_arColWidths[0]==pLayer[2+3]);
ASSERT(m_arColWidths[1]==pLayer[3+3]);
ASSERT(m_arColWidths[2]==pLayer[4+3]);
ASSERT(GetColumnCount()==3);
*/ memcpy(&m_arColOrder[0],&pLayer[2], GetColumnCount()*sizeof(int));
memcpy(&m_arColWidths[0],&pLayer[2+GetColumnCount()], GetColumnCount()*sizeof(int));
}
BEGIN_MESSAGE_MAP(CGridCtrl, CWnd)
//EFW - Added ON_WM_RBUTTONUP
//{{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_RBUTTONDBLCLK()
ON_WM_ERASEBKGND()
ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateEditSelectAll)
ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
ON_WM_SYSKEYDOWN()
//}}AFX_MSG_MAP
#ifndef _WIN32_WCE_NO_CURSOR
ON_WM_SETCURSOR()
#endif
#ifndef _WIN32_WCE
ON_WM_RBUTTONUP()
ON_WM_SYSCOLORCHANGE()
ON_WM_CAPTURECHANGED()
#endif
#ifndef GRIDCONTROL_NO_CLIPBOARD
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)
#endif
#if (_WIN32_WCE >= 210)
ON_WM_SETTINGCHANGE()
#endif
#if !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
ON_WM_MOUSEWHEEL()
#endif
ON_MESSAGE(WM_SETFONT, OnSetFont)
ON_MESSAGE(WM_GETFONT, OnGetFont)
ON_MESSAGE(WM_IME_CHAR, OnImeChar)
ON_NOTIFY(GVN_ENDLABELEDIT, IDC_INPLACE_CONTROL, 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
{
gridctrl::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 FixedRowColBack(GetDefaultCell(TRUE, TRUE)->GetBackClr()),
FixedRowBack(GetDefaultCell(TRUE, FALSE)->GetBackClr()),
FixedColBack(GetDefaultCell(FALSE, TRUE)->GetBackClr()),
TextBack(GetDefaultCell(FALSE, FALSE)->GetBackClr());
CBrush Back(GetGridBkColor());
//CBrush Back(GetTextBkColor());
if (pDC->GetClipBox(ClipRect) == ERROR)
return;
GetVisibleNonFixedCellRange(VisRect);
int nFixedColumnWidth = GetFixedColumnWidth();
int nFixedRowHeight = GetFixedRowHeight();
// Draw Fixed row/column background
if (ClipRect.left < nFixedColumnWidth && ClipRect.top < nFixedRowHeight)
pDC->FillRect(CRect(ClipRect.left, ClipRect.top,
nFixedColumnWidth, nFixedRowHeight),
&FixedRowColBack);
// Draw Fixed columns background
if (ClipRect.left < nFixedColumnWidth && ClipRect.top < VisRect.bottom)
pDC->FillRect(CRect(ClipRect.left, ClipRect.top,
nFixedColumnWidth, VisRect.bottom),
&FixedColBack);
// Draw Fixed rows background
if (ClipRect.top < nFixedRowHeight &&
ClipRect.right > nFixedColumnWidth && ClipRect.left < VisRect.right)
pDC->FillRect(CRect(nFixedColumnWidth-1, ClipRect.top,
VisRect.right, nFixedRowHeight),
&FixedRowBack);
// 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)
{
static BOOL bAlreadyInsideThisProcedure = FALSE;
if (bAlreadyInsideThisProcedure)
return;
if (!::IsWindow(m_hWnd))
return;
// This is not the ideal place to register the droptarget
#ifndef GRIDCONTROL_NO_DRAGDROP
m_DropTarget.Register(this);
#endif
// Start re-entry blocking
bAlreadyInsideThisProcedure = TRUE;
EndEditing(); // destroy any InPlaceEdit's
CWnd::OnSize(nType, cx, cy);
ResetScrollBars();
// End re-entry blocking
bAlreadyInsideThisProcedure = FALSE;
}
UINT CGridCtrl::OnGetDlgCode()
{
UINT nCode = DLGC_WANTARROWS | DLGC_WANTCHARS; // DLGC_WANTALLKEYS; //
if (m_bHandleTabKey && !IsCTRLpressed())
nCode |= DLGC_WANTTAB;
return nCode;
}
#ifndef _WIN32_WCE
// If system colours change, then redo colours
void CGridCtrl::OnSysColorChange()
{
CWnd::OnSysColorChange();
if (GetDefaultCell(FALSE, FALSE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(FALSE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(FALSE, FALSE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(FALSE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetDefaultCell(TRUE, FALSE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(TRUE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(TRUE, FALSE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(TRUE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetDefaultCell(FALSE, TRUE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(FALSE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(FALSE, TRUE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(FALSE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetDefaultCell(TRUE, TRUE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(TRUE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(TRUE, TRUE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(TRUE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetGridBkColor() == m_crShadow)
SetGridBkColor(::GetSysColor(COLOR_3DSHADOW));
m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
m_crShadow = ::GetSysColor(COLOR_3DSHADOW);
}
#endif
#ifndef _WIN32_WCE_NO_CURSOR
// 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;
}
#ifndef GRIDCONTROL_NO_DRAGDROP
// Kill drag and drop if active
if (m_MouseMode == MOUSE_DRAGGING)
m_MouseMode = MOUSE_NOTHING;
#endif
}
#endif
#if (_MFC_VER >= 0x0421) || (_WIN32_WCE >= 210)
// If system settings change, then redo colours
void CGridCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
CWnd::OnSettingChange(uFlags, lpszSection);
if (GetDefaultCell(FALSE, FALSE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(FALSE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(FALSE, FALSE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(FALSE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetDefaultCell(TRUE, FALSE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(TRUE, FALSE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(TRUE, FALSE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(TRUE, FALSE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetDefaultCell(FALSE, TRUE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(FALSE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(FALSE, TRUE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(FALSE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetDefaultCell(TRUE, TRUE)->GetTextClr() == m_crWindowText) // Still using system colours
GetDefaultCell(TRUE, TRUE)->SetTextClr(::GetSysColor(COLOR_WINDOWTEXT)); // set to new system colour
if (GetDefaultCell(TRUE, TRUE)->GetBackClr() == m_crWindowColour)
GetDefaultCell(TRUE, TRUE)->SetBackClr(::GetSysColor(COLOR_WINDOW));
if (GetGridBkColor() == m_crShadow)
SetGridBkColor(::GetSysColor(COLOR_3DSHADOW));
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_PTR nIDEvent)
{
ASSERT(nIDEvent == WM_LBUTTONDOWN);
if (nIDEvent != WM_LBUTTONDOWN)
return;
CPoint pt, origPt;
#ifdef _WIN32_WCE
if (m_MouseMode == MOUSE_NOTHING)
return;
origPt = GetMessagePos();
#else
if (!GetCursorPos(&origPt))
return;
#endif
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;
BOOL bHorzScrollAction = FALSE;
BOOL bVertScrollAction = FALSE;
if (IsCTRLpressed())
{
switch (nChar)
{
case 'A':
OnEditSelectAll();
break;
case 'k': // This is ctrl+ on french keyboard, may need to be better processed for other locales
AutoSizeColumns();
Invalidate();
break;
#ifndef GRIDCONTROL_NO_CLIPBOARD
case 'X':
OnEditCut();
break;
case VK_INSERT:
case 'C':
OnEditCopy();
break;
case 'V':
OnEditPaste();
break;
#endif
}
}
#ifndef GRIDCONTROL_NO_CLIPBOARD
if (IsSHIFTpressed() &&(nChar == VK_INSERT))
OnEditPaste();
#endif
BOOL bFoundVisible;
int iOrig;
if (nChar == VK_DELETE)
{
CutSelectedText();
}
else if (nChar == VK_DOWN)
{
// don't let user go to a hidden row
bFoundVisible = FALSE;
iOrig = next.row;
next.row++;
while( next.row < GetRowCount())
{
if( GetRowHeight( next.row) > 0)
{
bFoundVisible = TRUE;
break;
}
next.row++;
}
if( !bFoundVisible)
next.row = iOrig;
}
else if (nChar == VK_UP)
{
// don't let user go to a hidden row
bFoundVisible = FALSE;
iOrig = next.row;
next.row--;
while( next.row >= m_nFixedRows)
{
if( GetRowHeight( next.row) > 0)
{
bFoundVisible = TRUE;
break;
}
next.row--;
}
if( !bFoundVisible)
next.row = iOrig;
}
else if (nChar == VK_RIGHT || (nChar == VK_TAB && !IsSHIFTpressed()) )
{
if( (nChar == VK_TAB) && m_QuitFocusOnTab )
{
CDialog* p= (CDialog*) GetParent();
if(p) p->NextDlgCtrl();
return;
}
// don't let user go to a hidden column
bFoundVisible = FALSE;
iOrig = next.col;
next.col++;
if (nChar == VK_TAB)
{
// If we're at the end of a row, go down a row till we find a non-hidden row
if (next.col == (GetColumnCount()) && next.row < (GetRowCount() - 1))
{
next.row++;
while( next.row < GetRowCount())
{
if( GetRowHeight(next.row) > 0)
{
bFoundVisible = TRUE;
break;
}
next.row++;
}
next.col = m_nFixedCols; // Place focus on first non-fixed column
bChangeLine = TRUE;
}
else
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
// We're on a non-hidden row, so look across for the next non-hidden column
while( next.col < GetColumnCount())
{
if( GetColumnWidth( next.col) > 0)
{
bFoundVisible = TRUE;
break;
}
next.col++;
}
// If nothing worked then don't bother
if( !bFoundVisible)
next.col = iOrig;
}
else if (nChar == VK_LEFT || (nChar == VK_TAB && IsSHIFTpressed()) )
{
// don't let user go to a hidden column
bFoundVisible = FALSE;
iOrig = next.col;
next.col--;
if (nChar == VK_TAB)
{
if (next.col == (GetFixedColumnCount()-1) && next.row > GetFixedRowCount())
{
next.row--;
while( next.row > GetFixedRowCount())
{
if( GetRowHeight(next.row) > 0)
{
bFoundVisible = TRUE;
break;
}
next.row--;
}
next.col = GetColumnCount() - 1;
bChangeLine = TRUE;
}
else
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
while( next.col >= m_nFixedCols)
{
if( GetColumnWidth( next.col) > 0)
{
bFoundVisible = TRUE;
break;
}
next.col--;
}
if( !bFoundVisible)
next.col = iOrig;
}
else if (nChar == VK_NEXT)
{
CCellID idOldTopLeft = GetTopleftNonFixedCell();
SendMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
bVertScrollAction = TRUE;
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;
}
else if (nChar == VK_PRIOR)
{
CCellID idOldTopLeft = GetTopleftNonFixedCell();
SendMessage(WM_VSCROLL, SB_PAGEUP, 0);
bVertScrollAction = TRUE;
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;
}
else if (nChar == VK_HOME)
{
// Home and Ctrl-Home work more like Excel
// and don't let user go to a hidden cell
if (IsCTRLpressed())
{
SendMessage(WM_VSCROLL, SB_TOP, 0);
SendMessage(WM_HSCROLL, SB_LEFT, 0);
bVertScrollAction = TRUE;
bHorzScrollAction = TRUE;
next.row = m_nFixedRows;
next.col = m_nFixedCols;
}
else
{
SendMessage(WM_HSCROLL, SB_LEFT, 0);
bHorzScrollAction = TRUE;
next.col = m_nFixedCols;
}
// adjust column to avoid hidden columns and rows
while( next.col < GetColumnCount() - 1)
{
if( GetColumnWidth( next.col) > 0)
break;
next.col++;
}
while( next.row < GetRowCount() - 1)
{
if( GetRowHeight( next.row) > 0)
break;
next.row++;
}
}
else if (nChar == VK_END)
{
// End and Ctrl-End work more like Excel
// and don't let user go to a hidden cell
if (IsCTRLpressed())
{
SendMessage(WM_VSCROLL, SB_BOTTOM, 0);
SendMessage(WM_HSCROLL, SB_RIGHT, 0);
bHorzScrollAction = TRUE;
bVertScrollAction = TRUE;
next.row = GetRowCount() - 1;
next.col = GetColumnCount() - 1;
}
else
{
SendMessage(WM_HSCROLL, SB_RIGHT, 0);
bHorzScrollAction = TRUE;
next.col = GetColumnCount() - 1;
}
// adjust column to avoid hidden columns and rows
while( next.col > m_nFixedCols + 1)
{
if( GetColumnWidth( next.col) > 0)
break;
next.col--;
}
while( next.row > m_nFixedRows + 1)
{
if( GetRowHeight( next.row) > 0)
break;
next.row--;
}
}
else if (nChar == VK_F2)
{
OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, CPoint( -1, -1), VK_LBUTTON);
}
else
{
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
return;
}
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;
// Notify parent that selection is changing - Arthur Westerman/Scot Brennecke
SendMessageToParent(next.row, next.col, GVN_SELCHANGING);
OnSelecting(next);
SendMessageToParent(next.row, next.col, GVN_SELCHANGED);
m_MouseMode = MOUSE_NOTHING;
}
SetFocusCell(next);
if (!IsCellVisible(next))
{
switch (nChar)
{
case VK_RIGHT:
SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
bHorzScrollAction = TRUE;
break;
case VK_LEFT:
SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
bHorzScrollAction = TRUE;
break;
case VK_DOWN:
SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
bVertScrollAction = TRUE;
break;
case VK_UP:
SendMessage(WM_VSCROLL, SB_LINEUP, 0);
bVertScrollAction = TRUE;
break;
case VK_TAB:
if (IsSHIFTpressed())
{
if (bChangeLine)
{
SendMessage(WM_VSCROLL, SB_LINEUP, 0);
bVertScrollAction = TRUE;
SetScrollPos32(SB_HORZ, m_nHScrollMax);
break;
}
else
{
SendMessage(WM_HSCROLL, SB_LINELEFT, 0);
bHorzScrollAction = TRUE;
}
}
else
{
if (bChangeLine)
{
SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
bVertScrollAction = TRUE;
SetScrollPos32(SB_HORZ, 0);
break;
}
else
{
SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
bHorzScrollAction = TRUE;
}
}
break;
}
EnsureVisible(next); // Make sure cell is visible
Invalidate();
}
EnsureVisible(next); // Make sure cell is visible
if (bHorzScrollAction)
SendMessage(WM_HSCROLL, SB_ENDSCROLL, 0);
if (bVertScrollAction)
SendMessage(WM_VSCROLL, SB_ENDSCROLL, 0);
}
}
void CGridCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CWnd::OnKeyUp(nChar, nRepCnt, nFlags);
}
void CGridCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
#ifdef GRIDCONTROL_USE_TITLETIPS
m_TitleTip.Hide(); // hide any titletips
#endif
CWnd::OnSysKeyDown(nChar, nRepCnt, nFlags);
}
// Instant editing of cells when keys are pressed
void CGridCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// EFW - BUG FIX
if (!IsCTRLpressed() && m_MouseMode == MOUSE_NOTHING && nChar != VK_ESCAPE)
{
if (!m_bHandleTabKey || (m_bHandleTabKey && nChar != VK_TAB))
OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, CPoint( -1, -1), nChar);
}
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
// Added by KiteFly
LRESULT CGridCtrl::OnImeChar(WPARAM wCharCode, LPARAM)
{
// EFW - BUG FIX
if (!IsCTRLpressed() && m_MouseMode == MOUSE_NOTHING && wCharCode != VK_ESCAPE)
OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, CPoint( -1, -1), (UINT)wCharCode);
return 0;
}
// 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;
OnEndEditCell(pgvItem->row, pgvItem->col, pgvItem->strText);
//InvalidateCellRect(CCellID(pgvItem->row, pgvItem->col));
switch (pgvItem->lParam)
{
case VK_TAB:
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((UINT)pgvItem->lParam, 0, 0);
OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, CPoint( -1, -1), (UINT)pgvItem->lParam);
}
*pResult = 0;
}
// Handle horz scrollbar notifications
void CGridCtrl::OnHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/)
{
EndEditing();
#ifndef GRIDCONTROL_NO_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)
{
// may have contiguous hidden columns. Blow by them
while (idTopLeft.col < (GetColumnCount()-1)
&& GetColumnWidth( idTopLeft.col) < 1 )
{
idTopLeft.col++;
}
int xScroll = GetColumnWidth(idTopLeft.col);
SetScrollPos32(SB_HORZ, scrollPos + xScroll);
if (GetScrollPos32(SB_HORZ) == scrollPos)
break; // didn't work
rect.left = GetFixedColumnWidth();
//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 iColToUse = idTopLeft.col-1;
// may have contiguous hidden columns. Blow by them
while( iColToUse > GetFixedColumnCount()
&& GetColumnWidth( iColToUse) < 1 )
{
iColToUse--;
}
int xScroll = GetColumnWidth(iColToUse);
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));
m_idTopLeftCell.row = -1;
CCellID idNewTopLeft = GetTopleftNonFixedCell();
if (idNewTopLeft != idTopLeft)
{
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*/)
{
EndEditing();
#ifndef GRIDCONTROL_NO_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)
{
// may have contiguous hidden rows. Blow by them
while( idTopLeft.row < (GetRowCount()-1)
&& GetRowHeight( idTopLeft.row) < 1 )
{
idTopLeft.row++;
}
int yScroll = GetRowHeight(idTopLeft.row);
SetScrollPos32(SB_VERT, scrollPos + yScroll);
if (GetScrollPos32(SB_VERT) == scrollPos)
break; // didn't work
rect.top = GetFixedRowHeight();
//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 iRowToUse = idTopLeft.row-1;
// may have contiguous hidden rows. Blow by them
while( iRowToUse > GetFixedRowCount()
&& GetRowHeight( iRowToUse) < 1 )
{
iRowToUse--;
}
int yScroll = GetRowHeight( iRowToUse);
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));
m_idTopLeftCell.row = -1;
CCellID idNewTopLeft = GetTopleftNonFixedCell();
if (idNewTopLeft != idTopLeft)
{
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)
{
if (!m_bAllowDraw)
return;
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.
#ifdef _DEBUG
LARGE_INTEGER iStartCount;
QueryPerformanceCounter(&iStartCount);
#endif
CRect rect;
int row, col;
CGridCellBase* pCell;
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();
if (GetVirtualMode())
SendCacheHintToParent(VisCellRange);
// draw top-left cells 0..m_nFixedRows-1, 0..m_nFixedCols-1
rect.bottom = -1;
for (row = 0; row < m_nFixedRows; row++)
{
if (GetRowHeight(row) <= 0) continue;
rect.top = rect.bottom+1;
rect.bottom = rect.top + GetRowHeight(row)-1;
rect.right = -1;
for (col = 0; col < m_nFixedCols; col++)
{
if (GetColumnWidth(col) <= 0) continue;
rect.left = rect.right+1;
rect.right = rect.left + GetColumnWidth(col)-1;
pCell = GetCell(row, col);
if (pCell)
{
pCell->SetCoords(row,col);
pCell->Draw(pDC, row, col, rect, FALSE);
}
}
}
// draw fixed column cells: m_nFixedRows..n, 0..m_nFixedCols-1
rect.bottom = nFixedRowHeight-1;
for (row = minVisibleRow; row <= maxVisibleRow; row++)
{
if (GetRowHeight(row) <= 0) continue;
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++)
{
if (GetColumnWidth(col) <= 0) continue;
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?
pCell = GetCell(row, col);
if (pCell)
{
pCell->SetCoords(row,col);
pCell->Draw(pDC, row, col, rect, FALSE);
}
}
}
// draw fixed row cells 0..m_nFixedRows, m_nFixedCols..n
rect.bottom = -1;
for (row = 0; row < m_nFixedRows; row++)
{
if (GetRowHeight(row) <= 0) continue;
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++)
{
if (GetColumnWidth(col) <= 0) continue;
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?
pCell = GetCell(row, col);
if (pCell)
{
pCell->SetCoords(row,col);
pCell->Draw(pDC, row, col, rect, FALSE);
}
}
}
// draw rest of non-fixed cells
rect.bottom = nFixedRowHeight-1;
for (row = minVisibleRow; row <= maxVisibleRow; row++)
{
if (GetRowHeight(row) <= 0) continue;
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++)
{
if (GetColumnWidth(col) <= 0) continue;
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?
pCell = GetCell(row, col);
// TRACE(_T("Cell %d,%d type: %s\n"), row, col, pCell->GetRuntimeClass()->m_lpszClassName);
if (pCell)
{
pCell->SetCoords(row,col);
pCell->Draw(pDC, row, col, rect, FALSE);
}
}
}
CPen pen;
pen.CreatePen(PS_SOLID, 0, m_crGridLineColour);
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++)
{
if (GetColumnWidth(col) <= 0) continue;
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++)
{
if (GetRowHeight(row) <= 0) continue;
y += GetRowHeight(row);
pDC->MoveTo(nFixedColWidth, y-1);
pDC->LineTo(VisRect.right, y-1);
}
}
pDC->SelectStockObject(NULL_PEN);
// Let parent know it can discard it's data if it needs to.
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(-1,-1,-1,-1));
#ifdef _DEBUG
LARGE_INTEGER iEndCount;
QueryPerformanceCounter(&iEndCount);
// TRACE1("Draw counter ticks: %d\n", iEndCount.LowPart-iStartCount.LowPart);
#endif
}
////////////////////////////////////////////////////////////////////////////////////////
// CGridCtrl Cell selection stuff
// Is a given cell designation valid (ie within the bounds of our number
// of columns/rows)?
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);
}
// Is a given cell range valid (ie within the bounds of our number
// of columns/rows)?
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 %s\n"),
// GetRuntimeClass()->m_lpszClassName, bAllowDraw? _T("TRUE") : _T("FALSE"));
if (bAllowDraw && !m_bAllowDraw)
{
m_bAllowDraw = TRUE;
Refresh();
}
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)
{
CGridCellBase* pCell = GetCell(nRow, nCol);
if (pCell)
bResult = pCell->Draw(pDC, nRow, nCol, rect, TRUE);
}
else
{
CGridCellBase* pCell = GetCell(nRow, nCol);
if (pCell)
bResult = pCell->Draw(pDC, nRow, nCol, rect, TRUE);
// Since we have erased the background, we will need to redraw the gridlines
CPen pen;
pen.CreatePen(PS_SOLID, 0, m_crGridLineColour);
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;
// EFW - Bug Fix - Force focus to be in a non-fixed cell
if (cell.row != -1 && cell.row < GetFixedRowCount())
cell.row = GetFixedRowCount();
if (cell.col != -1 && cell.col < GetFixedColumnCount())
cell.col = GetFixedColumnCount();
m_idCurrentCell = cell;
if (IsValid(idPrev))
{
SetItemState(idPrev.row, idPrev.col,
GetItemState(idPrev.row, idPrev.col) & ~GVIS_FOCUSED);
RedrawCell(idPrev); // comment to reduce flicker
if (GetTrackFocusCell() && idPrev.col != m_idCurrentCell.col)
for (int row = 0; row < m_nFixedRows; row++)
RedrawCell(row, idPrev.col);
if (GetTrackFocusCell() && 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); // comment to reduce flicker
if (GetTrackFocusCell() && idPrev.col != m_idCurrentCell.col)
for (int row = 0; row < m_nFixedRows; row++)
RedrawCell(row, m_idCurrentCell.col);
if (GetTrackFocusCell() && idPrev.row != m_idCurrentCell.row)
for (int col = 0; col < m_nFixedCols; col++)
RedrawCell(m_idCurrentCell.row, col);
// EFW - New addition. If in list mode, make sure the selected
// row highlight follows the cursor.
// Removed by C Maunder 27 May
//if (m_bListMode)
//{
// m_PrevSelectedCellMap.RemoveAll();
// m_MouseMode = MOUSE_SELECT_ROW;
// OnSelecting(m_idCurrentCell);
// Leave this off so that you can still drag the highlight around
// without selecting rows.
// m_MouseMode = MOUSE_NOTHING;
//}
}
return idPrev;
}
// Sets the range of currently selected cells
void CGridCtrl::SetSelectedRange(const CCellRange& Range,
BOOL bForceRepaint /* = FALSE */, BOOL bSelectCells/*=TRUE*/)
{
SetSelectedRange(Range.GetMinRow(), Range.GetMinCol(),
Range.GetMaxRow(), Range.GetMaxCol(),
bForceRepaint, bSelectCells);
}
void CGridCtrl::SetSelectedRange(int nMinRow, int nMinCol, int nMaxRow, int nMaxCol,
BOOL bForceRepaint /* = FALSE */, BOOL bSelectCells/*=TRUE*/)
{
if (!m_bEnableSelection)
return;
CWaitCursor wait; // Thomas Haase
CDC* pDC = NULL;
if (bForceRepaint)
pDC = GetDC();
// Only redraw visible cells
CCellRange VisCellRange, FixedVisCellRange;
if (IsWindow(GetSafeHwnd()))
{
VisCellRange = GetVisibleNonFixedCellRange();
FixedVisCellRange = GetVisibleFixedCellRange();
}
// EFW - Bug fix - Don't allow selection of fixed rows
int Left= (m_AllowSelectRowInFixedCol ? 0 : GetFixedColumnCount());
if(nMinRow >= 0 && nMinRow < GetFixedRowCount())
nMinRow = GetFixedRowCount();
if(nMaxRow >= 0 && nMaxRow < GetFixedRowCount())
nMaxRow = GetFixedRowCount();
if(nMinCol >= 0 && nMinCol < Left)
nMinCol = GetFixedColumnCount();
if(nMaxCol >= 0 && nMaxCol < Left)
nMaxCol = GetFixedColumnCount();
// If we are selecting cells, then first clear out the list of currently selected cells, then
if (bSelectCells)
{
POSITION pos;
// Unselect all previously selected cells
for (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))
{
// This will remove the cell from the m_SelectedCellMap map
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 ( (VisCellRange.IsValid() && VisCellRange.InRange( cell )) || FixedVisCellRange.InRange( cell ) )
{
if (bForceRepaint && pDC) // Redraw NOW
RedrawCell(cell.row, cell.col, pDC);
else
InvalidateCellRect(cell); // Redraw at leisure
}
}
else
{
m_SelectedCellMap.RemoveKey( key); // if it's not valid, get rid of it!
}
}
// if we are selecting cells, and there are previous selected cells to be retained
// (eg Ctrl is being held down) then copy them to the newly created list, and mark
// all these cells as selected
// Note that if we are list mode, single row selection, then we won't be adding
// the previous cells. Only the current row of cells will be added (see below)
if (!GetSingleRowSelection() &&
nMinRow >= 0 && nMinCol >= 0 && nMaxRow >= 0 && nMaxCol >= 0)
{
for (pos = m_PrevSelectedCellMap.GetStartPosition(); pos != NULL; /* nothing */)
{
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_SelectedCellMap
SetItemState(cell.row, cell.col, nState | GVIS_SELECTED);
if (( VisCellRange.IsValid() && VisCellRange.InRange( cell )) || FixedVisCellRange.InRange( cell ) )
{
// Redraw (immediately or at leisure)
if (bForceRepaint && pDC)
RedrawCell(cell.row, cell.col, pDC);
else
InvalidateCellRect(cell);
}
}
}
}
// Now select/deselect all cells in the cell range specified. If selecting, and the cell
// has already been marked as selected (above) then ignore it. If we are deselecting and
// the cell isn't selected, then ignore
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++)
{
BOOL bCellSelected = IsCellSelected(row, col);
if (bSelectCells == bCellSelected)
continue; // Already selected or deselected - ignore
// Set the selected state. This will add/remove the cell to m_SelectedCellMap
if (bSelectCells)
SetItemState(row, col, GetItemState(row, col) | GVIS_SELECTED);
else
SetItemState(row, col, GetItemState(row, col) & ~GVIS_SELECTED);
if ( (VisCellRange.IsValid() && VisCellRange.InRange(row, col)) || FixedVisCellRange.InRange(row, col) )
{
// Redraw (immediately or at leisure)
if (bForceRepaint && pDC)
RedrawCell(row, col, pDC);
else
InvalidateCellRect(row, col);
}
}
}
// 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,
BOOL bForceRedraw /*=FALSE*/, BOOL bSelectCells /*=TRUE*/)
{
if (!m_bEnableSelection)
return;
//if (currentCell.col == m_idCurrentCell.col) return;
if (currentCell.col < m_nFixedCols)
return;
if (!IsValid(currentCell))
return;
if (GetSingleColSelection())
SetSelectedRange(GetFixedRowCount(), currentCell.col,
GetRowCount()-1, currentCell.col,
bForceRedraw, bSelectCells);
else
SetSelectedRange(GetFixedRowCount(),
min(m_SelectionStartCell.col, currentCell.col),
GetRowCount()-1,
max(m_SelectionStartCell.col, currentCell.col),
bForceRedraw, bSelectCells);
}
// selects rows
void CGridCtrl::SelectRows(CCellID currentCell,
BOOL bForceRedraw /*=FALSE*/, BOOL bSelectCells /*=TRUE*/)
{
if (!m_bEnableSelection)
return;
//if (currentCell.row; == m_idCurrentCell.row) return;
if (currentCell.row < m_nFixedRows)
return;
if (!IsValid(currentCell))
return;
int Left = ( m_AllowSelectRowInFixedCol ? 0 : GetFixedColumnCount());
if (GetSingleRowSelection())
SetSelectedRange(currentCell.row, Left ,
currentCell.row, GetColumnCount()-1,
bForceRedraw, bSelectCells);
else
SetSelectedRange(min(m_SelectionStartCell.row, currentCell.row),
Left,
__max(m_SelectionStartCell.row, currentCell.row),
GetColumnCount()-1,
bForceRedraw, bSelectCells);
}
// selects cells
void CGridCtrl::SelectCells(CCellID currentCell,
BOOL bForceRedraw /*=FALSE*/, BOOL bSelectCells /*=TRUE*/)
{
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),
bForceRedraw, bSelectCells);
}
// Called when mouse/keyboard selection is a-happening.
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, FALSE);
break;
case MOUSE_SELECT_ROW:
SelectRows(currentCell, FALSE);
break;
case MOUSE_SELECT_CELLS:
SelectCells(currentCell, FALSE);
break;
}
// EFW - Bug fix [REMOVED CJM: this will cause infinite loop in list mode]
// SetFocusCell(max(currentCell.row, m_nFixedRows), max(currentCell.col, m_nFixedCols));
}
void CGridCtrl::ValidateAndModifyCellContents(int nRow, int nCol, LPCTSTR strText)
{
if (!IsCellEditable(nRow, nCol))
return;
if (SendMessageToParent(nRow, nCol, GVN_BEGINLABELEDIT) >= 0)
{
CString strCurrentText = GetItemText(nRow, nCol);
if (strCurrentText != strText)
{
SetItemText(nRow, nCol, strText);
if (ValidateEdit(nRow, nCol, strText) &&
SendMessageToParent(nRow, nCol, GVN_ENDLABELEDIT) >= 0)
{
SetModified(TRUE, nRow, nCol);
RedrawCell(nRow, nCol);
}
else
{
SetItemText(nRow, nCol, strCurrentText);
}
}
}
}
void CGridCtrl::ClearCells(CCellRange Selection)
{
for (int row = Selection.GetMinRow(); row <= Selection.GetMaxRow(); row++)
{
for (int col = Selection.GetMinCol(); col <= Selection.GetMaxCol(); col++)
{
// don't clear hidden cells
if ( m_arRowHeights[row] > 0 && m_arColWidths[col] > 0 )
{
ValidateAndModifyCellContents(row, col, _T(""));
}
}
}
Refresh();
}
#ifndef GRIDCONTROL_NO_CLIPBOARD
////////////////////////////////////////////////////////////////////////////////////////
// Clipboard functions
// Deletes the contents from the selected cells
void CGridCtrl::CutSelectedText()
{
if (!IsEditable())
return;
for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
{
DWORD key;
CCellID cell;
m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
ValidateAndModifyCellContents(cell.row, cell.col, _T(""));
}
}
// Copies text from the selected cells to the clipboard
COleDataSource* CGridCtrl::CopyTextFromGrid()
{
USES_CONVERSION;
CCellRange Selection = GetSelectedCellRange();
if (!IsValid(Selection))
return NULL;
if (GetVirtualMode())
SendCacheHintToParent(Selection);
// 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;
CGridCellBase *pCell;
for (int row = Selection.GetMinRow(); row <= Selection.GetMaxRow(); row++)
{
// don't copy hidden cells
if( m_arRowHeights[row] <= 0 )
continue;
str.Empty();
for (int col = Selection.GetMinCol(); col <= Selection.GetMaxCol(); col++)
{
// don't copy hidden cells
if( m_arColWidths[col] <= 0 )
continue;
pCell = GetCell(row, col);
if (pCell &&(pCell->GetState() & GVIS_SELECTED))
{
// if (!pCell->GetText())
// str += _T(" ");
// else
str += pCell->GetText();
}
if (col != Selection.GetMaxCol())
str += _T("\t");
}
if (row != Selection.GetMaxRow())
str += _T("\r\n");
sf.Write(T2A(str.GetBuffer(1)), str.GetLength());
str.ReleaseBuffer();
}
char c = '\0';
sf.Write(&c, 1);
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(-1,-1,-1,-1));
DWORD dwLen = (DWORD) 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;
}
// Pastes text from the clipboard to the selected cells
BOOL CGridCtrl::PasteTextToGrid(CCellID cell, COleDataObject* pDataObject,
BOOL bSelectPastedCells /*=TRUE*/)
{
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), (UINT)::GlobalSize(hmem));
// CF_TEXT is ANSI text, so we need to allocate a char* buffer
// to hold this.
LPSTR szBuffer = new char[::GlobalSize(hmem)]; // FIX: Use LPSTR char here
if (!szBuffer)
return FALSE;
sf.Read(szBuffer, (UINT)::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
CCellRange PasteRange(cell.row, cell.col,-1,-1);
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);
int nLineIndex = strLine.FindOneOf(_T("\t,"));
CString strCellText = (nLineIndex >= 0)? strLine.Left(nLineIndex) : strLine;
// skip hidden rows
int iRowVis = cell.row + nLine;
while( iRowVis < GetRowCount())
{
if( GetRowHeight( iRowVis) > 0)
break;
nLine++;
iRowVis++;
}
while (!strLine.IsEmpty())
{
// skip hidden columns
int iColVis = cell.col + nColumn;
while( iColVis < GetColumnCount())
{
if( GetColumnWidth( iColVis) > 0)
break;
nColumn++;
iColVis++;
}
CCellID TargetCell(iRowVis, iColVis);
if (IsValid(TargetCell))
{
strCellText.TrimLeft();
strCellText.TrimRight();
ValidateAndModifyCellContents(TargetCell.row, TargetCell.col, strCellText);
// Make sure cell is not selected to avoid data loss
SetItemState(TargetCell.row, TargetCell.col,
GetItemState(TargetCell.row, TargetCell.col) & ~GVIS_SELECTED);
if (iRowVis > PasteRange.GetMaxRow()) PasteRange.SetMaxRow(iRowVis);
if (iColVis > PasteRange.GetMaxCol()) PasteRange.SetMaxCol(iColVis);
}
strLine = (nLineIndex >= 0)? strLine.Mid(nLineIndex + 1) : _T("");
nLineIndex = strLine.FindOneOf(_T("\t,"));
strCellText = (nLineIndex >= 0)? strLine.Left(nLineIndex) : strLine;
nColumn++;
}
strLine = strNext;
nLine++;
} while (nIndex >= 0);
strText.UnlockBuffer();
if (bSelectPastedCells)
SetSelectedRange(PasteRange, TRUE);
else
{
ResetSelectedRange();
Refresh();
}
return TRUE;
}
#endif
#ifndef GRIDCONTROL_NO_DRAGDROP
// Start drag n drop
void CGridCtrl::OnBeginDrag()
{
COleDataSource* pSource = NULL;
if (!m_bAllowDragAndDrop && m_CurCol==-1)
return;
if (m_CurCol>=0) pSource = new COleDataSource();
if (!pSource && m_bAllowDragAndDrop) pSource = CopyTextFromGrid();
if (pSource)
{
SendMessageToParent(GetSelectedCellRange().GetTopLeft().row,
GetSelectedCellRange().GetTopLeft().col,
GVN_BEGINDRAG);
m_MouseMode = MOUSE_DRAGGING;
m_bLMouseButtonDown = FALSE;
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
}
}
// Handle drag over grid
DROPEFFECT CGridCtrl::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState,
CPoint point)
{
// Find which cell we are over and drop-highlight it
CCellID cell = GetCellFromPt(point, FALSE);
bool Valid;
// Any text data available for us?
if(m_CurCol==-1)
{
if(m_bDragRowMode)
{
Valid = cell.col>=GetFixedColumnCount() && cell.row>=GetFixedRowCount();
}
else
{
if (!m_bAllowDragAndDrop || !IsEditable() || !pDataObject->IsDataAvailable(CF_TEXT))
return DROPEFFECT_NONE;
Valid = IsValid(cell)!=0;
}
}
else
{
Valid = cell.col>=GetFixedColumnCount() && cell.row<GetFixedRowCount() ;
}
// If not valid, set the previously drop-highlighted cell as no longer drop-highlighted
if (!Valid)
{
OnDragLeave();
m_LastDragOverCell = CCellID(-1,-1);
return DROPEFFECT_NONE;
}
if(m_CurCol==-1)
{
if (!m_bDragRowMode && !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;
}
// Something has just been dragged onto the grid
DROPEFFECT CGridCtrl::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState,
CPoint point)
{
// Any text data available for us?
m_LastDragOverCell = GetCellFromPt(point, m_CurCol>=0);
bool Valid;
if(m_CurCol==-1)
{
if (!m_bAllowDragAndDrop || !pDataObject->IsDataAvailable(CF_TEXT))
return DROPEFFECT_NONE;
// Find which cell we are over and drop-highlight it
if (!IsValid(m_LastDragOverCell))
return DROPEFFECT_NONE;
if (!IsCellEditable(m_LastDragOverCell))
return DROPEFFECT_NONE;
Valid = IsValid(m_LastDragOverCell)!=0;
}
else
{
Valid = m_LastDragOverCell.row>=0 && m_LastDragOverCell.row<GetFixedRowCount() && m_LastDragOverCell.col>=GetFixedColumnCount();
}
if (Valid)
{
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;
}
// Something has just been dragged away from the grid
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);
}
}
// Something has just been dropped onto the grid
BOOL CGridCtrl::OnDrop(COleDataObject* pDataObject, DROPEFFECT /*dropEffect*/,
CPoint /* point */)
{
m_MouseMode = MOUSE_NOTHING;
if(m_CurCol ==-1)
{
if (!m_bAllowDragAndDrop || (!IsCellEditable(m_LastDragOverCell) && !m_bDragRowMode))
return FALSE;
}
OnDragLeave();
if (m_CurCol>=0)
{
if(m_LastDragOverCell.col == m_CurCol || m_LastDragOverCell.row >= GetFixedRowCount())
return FALSE;
else
{
int New = m_arColOrder[m_CurCol];
m_arColOrder.erase(m_arColOrder.begin()+m_CurCol);
m_arColOrder.insert(m_arColOrder.begin()+m_LastDragOverCell.col, New);
m_CurCol=-1;
Invalidate();
return TRUE;
}
}
else
{
if(m_bDragRowMode)
{
Reorder(m_CurRow,m_LastDragOverCell.row);
Invalidate();
return TRUE;
}
else
return PasteTextToGrid(m_LastDragOverCell, pDataObject, FALSE);
}
}
#endif
#ifndef GRIDCONTROL_NO_CLIPBOARD
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;
CCellRange cellRange = GetSelectedCellRange();
// Get the top-left selected cell, or the Focus cell, or the topleft (non-fixed) cell
CCellID cell;
if (cellRange.IsValid())
{
cell.row = cellRange.GetMinRow();
cell.col = cellRange.GetMinCol();
}
else
{
cell = GetFocusCell();
if (!IsValid(cell))
cell = GetTopleftNonFixedCell();
if (!IsValid(cell))
return;
}
// If a cell is being edited, then call it's edit window paste function.
if ( IsItemEditing(cell.row, cell.col) )
{
CGridCellBase* pCell = GetCell(cell.row, cell.col);
ASSERT(pCell);
if (!pCell) return;
CWnd* pEditWnd = pCell->GetEditWnd();
if ( pEditWnd && pEditWnd->IsKindOf(RUNTIME_CLASS(CEdit)) )
{
((CEdit*)pEditWnd)->Paste();
return;
}
}
// Attach a COleDataObject to the clipboard and paste the data to the grid
COleDataObject obj;
if (obj.AttachClipboard())
PasteTextToGrid(cell, &obj);
}
#endif
void CGridCtrl::OnEditSelectAll()
{
SendMessageToParent(m_LeftClickDownCell.row, m_LeftClickDownCell.col, GVN_SELCHANGING);
SelectAllCells();
SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_SELCHANGED);
}
#ifndef GRIDCONTROL_NO_CLIPBOARD
void CGridCtrl::OnUpdateEditCopy(CCmdUI* pCmdUI)
{
pCmdUI->Enable(GetSelectedCount() > 0); // - Thomas Haase
//CCellRange Selection = GetSelectedCellRange();
//pCmdUI->Enable(Selection.Count() && IsValid(Selection));
}
void CGridCtrl::OnUpdateEditCut(CCmdUI* pCmdUI)
{
pCmdUI->Enable(IsEditable() && GetSelectedCount() > 0); // - Thomas Haase
//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);
}
#endif
void CGridCtrl::OnUpdateEditSelectAll(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bEnableSelection);
}
////////////////////////////////////////////////////////////////////////////////////////
// hittest-like functions
// TRUE if the mouse is over a row resize area
BOOL CGridCtrl::MouseOverRowResizeArea(CPoint& point)
{
if (point.x >= GetFixedColumnWidth())
return FALSE;
CCellID idCurrentCell = GetCellFromPt(point);
CPoint start;
if (!GetCellOrigin(idCurrentCell, &start))
return FALSE;
int endy = start.y + GetRowHeight(idCurrentCell.row);
if ((point.y - start.y < m_nResizeCaptureRange && idCurrentCell.row != 0) ||
endy - point.y < m_nResizeCaptureRange)
{
return TRUE;
}
else
return FALSE;
}
// TRUE if the mouse is over a column resize area. point is in Client coords
BOOL CGridCtrl::MouseOverColumnResizeArea(CPoint& point)
{
if (point.y >= GetFixedRowHeight())
return FALSE;
CCellID idCurrentCell = GetCellFromPt(point);
CPoint start;
if (!GetCellOrigin(idCurrentCell, &start))
return FALSE;
int endx = start.x + GetColumnWidth(idCurrentCell.col);
if ((point.x - start.x < m_nResizeCaptureRange && idCurrentCell.col != 0) ||
endx - point.x < m_nResizeCaptureRange)
{
return TRUE;
}
else
return FALSE;
}
// Get cell from point.
// point - client coordinates
// bAllowFixedCellCheck - if TRUE then fixed cells are checked
CCellID CGridCtrl::GetCellFromPt(CPoint point, BOOL bAllowFixedCellCheck /*=TRUE*/)
{
CCellID cellID; // return value
CCellID idTopLeft = GetTopleftNonFixedCell();
if (!bAllowFixedCellCheck && !IsValid(idTopLeft))
return cellID;
// 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;
int col = 0;
while (col < m_nFixedCols)
{
xpos += GetColumnWidth(col);
if (xpos > point.x)
break;
col++;
}
cellID.col = col;
}
else // in non-fixed col
{
int xpos = fixedColWidth;
int col = idTopLeft.col; //m_nFixedCols;
while ( col < GetColumnCount())
{
xpos += GetColumnWidth(col);
if (xpos > point.x)
break;
col++;
}
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;
int row = 0;
while (row < m_nFixedRows)
{
ypos += GetRowHeight(row);
if (ypos > point.y)
break;
row++;
}
cellID.row = row;
}
else
{
int ypos = fixedRowHeight;
int row = idTopLeft.row; //m_nFixedRows;
while ( row < GetRowCount() )
{
ypos += GetRowHeight(row);
if (ypos > point.y)
break;
row++;
}
if (row >= GetRowCount())
cellID.row = -1;
else
cellID.row = row;
}
return cellID;
}
////////////////////////////////////////////////////////////////////////////////
// CGridCtrl cellrange functions
// Gets the first non-fixed cell ID
CCellID CGridCtrl::GetTopleftNonFixedCell(BOOL bForceRecalculation /*=FALSE*/)
{
// Used cached value if possible
if (m_idTopLeftCell.IsValid() && !bForceRecalculation)
return m_idTopLeftCell;
int nVertScroll = GetScrollPos(SB_VERT),
nHorzScroll = GetScrollPos(SB_HORZ);
m_idTopLeftCell.col = m_nFixedCols;
int nRight = 0;
while (nRight < nHorzScroll && m_idTopLeftCell.col < (GetColumnCount()-1))
nRight += GetColumnWidth(m_idTopLeftCell.col++);
m_idTopLeftCell.row = m_nFixedRows;
int nTop = 0;
while (nTop < nVertScroll && m_idTopLeftCell.row < (GetRowCount()-1))
nTop += GetRowHeight(m_idTopLeftCell.row++);
//TRACE2("TopLeft cell is row %d, col %d\n",m_idTopLeftCell.row, m_idTopLeftCell.col);
return m_idTopLeftCell;
}
// This gets even partially visible cells
CCellRange CGridCtrl::GetVisibleNonFixedCellRange(LPRECT pRect /*=NULL*/,
BOOL bForceRecalculation /*=FALSE*/)
{
int i;
CRect rect;
GetClientRect(rect);
CCellID idTopLeft = GetTopleftNonFixedCell(bForceRecalculation);
// calc bottom
int bottom = GetFixedRowHeight();
for (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);
}
CCellRange CGridCtrl::GetVisibleFixedCellRange(LPRECT pRect /*=NULL*/,
BOOL bForceRecalculation /*=FALSE*/)
{
int i;
CRect rect;
GetClientRect(rect);
CCellID idTopLeft = GetTopleftNonFixedCell(bForceRecalculation);
// calc bottom
int bottom = GetFixedRowHeight();
for (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 = 0;
for (i = 0; i < GetFixedColumnCount(); 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, 0, maxVisibleRow, maxVisibleCol);
}
// used by ResetScrollBars() - This gets only fully visible cells
CCellRange CGridCtrl::GetUnobstructedNonFixedCellRange(BOOL bForceRecalculation /*=FALSE*/)
{
CRect rect;
GetClientRect(rect);
CCellID idTopLeft = GetTopleftNonFixedCell(bForceRecalculation);
// calc bottom
int i;
int bottom = GetFixedRowHeight();
for (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);
}
// Resets the selected cell range to the empty set.
void CGridCtrl::ResetSelectedRange()
{
m_PrevSelectedCellMap.RemoveAll();
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 */)
{
m_idTopLeftCell.row = -1;
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
si.nPos = nPos;
return SetScrollInfo(nBar, &si, bRedraw);
}
void CGridCtrl::EnableScrollBars(int nBar, BOOL bEnable /*=TRUE*/)
{
if (bEnable)
{
if (!IsVisibleHScroll() && (nBar == SB_HORZ || nBar == SB_BOTH))
{
m_nBarState |= GVL_HORZ;
CWnd::EnableScrollBarCtrl(SB_HORZ, bEnable);
}
if (!IsVisibleVScroll() && (nBar == SB_VERT || nBar == SB_BOTH))
{
m_nBarState |= GVL_VERT;
CWnd::EnableScrollBarCtrl(SB_VERT, bEnable);
}
}
else
{
if ( IsVisibleHScroll() && (nBar == SB_HORZ || nBar == SB_BOTH))
{
m_nBarState &= ~GVL_HORZ;
CWnd::EnableScrollBarCtrl(SB_HORZ, bEnable);
}
if ( IsVisibleVScroll() && (nBar == SB_VERT || nBar == SB_BOTH))
{
m_nBarState &= ~GVL_VERT;
CWnd::EnableScrollBarCtrl(SB_VERT, bEnable);
}
}
}
// If resizing or cell counts/sizes change, call this - it'll fix up the scroll bars
void CGridCtrl::ResetScrollBars()
{
// Force a refresh.
m_idTopLeftCell.row = -1;
if (!m_bAllowDraw || !::IsWindow(GetSafeHwnd()))
return;
CRect rect;
// This would have caused OnSize event - Brian
//EnableScrollBars(SB_BOTH, FALSE);
GetClientRect(rect);
if (rect.left == rect.right || rect.top == rect.bottom)
return;
if (IsVisibleVScroll())
rect.right += GetSystemMetrics(SM_CXVSCROLL) + GetSystemMetrics(SM_CXBORDER);
if (IsVisibleHScroll())
rect.bottom += GetSystemMetrics(SM_CYHSCROLL) + GetSystemMetrics(SM_CYBORDER);
rect.left += GetFixedColumnWidth();
rect.top += GetFixedRowHeight();
if (rect.left >= rect.right || rect.top >= rect.bottom)
{
EnableScrollBarCtrl(SB_BOTH, FALSE);
return;
}
CRect VisibleRect(GetFixedColumnWidth(), GetFixedRowHeight(),
rect.right, rect.bottom);
CRect VirtualRect(GetFixedColumnWidth(), GetFixedRowHeight(),
GetVirtualWidth(), GetVirtualHeight());
// Removed to fix single row scrollbar problem (Pontus Goffe)
// CCellRange visibleCells = GetUnobstructedNonFixedCellRange();
// if (!IsValid(visibleCells)) return;
//TRACE(_T("Visible: %d x %d, Virtual %d x %d. H %d, V %d\n"),
// VisibleRect.Width(), VisibleRect.Height(),
// VirtualRect.Width(), VirtualRect.Height(),
// IsVisibleHScroll(), IsVisibleVScroll());
// If vertical scroll bar, horizontal space is reduced
if (VisibleRect.Height() < VirtualRect.Height())
VisibleRect.right -= ::GetSystemMetrics(SM_CXVSCROLL);
// If horz scroll bar, vert space is reduced
if (VisibleRect.Width() < VirtualRect.Width())
VisibleRect.bottom -= ::GetSystemMetrics(SM_CYHSCROLL);
// Recheck vertical scroll bar
//if (VisibleRect.Height() < VirtualRect.Height())
// VisibleRect.right -= ::GetSystemMetrics(SM_CXVSCROLL);
if (VisibleRect.Height() < VirtualRect.Height())
{
EnableScrollBars(SB_VERT, TRUE);
m_nVScrollMax = VirtualRect.Height() - 1;
}
else
{
EnableScrollBars(SB_VERT, FALSE);
m_nVScrollMax = 0;
}
if (VisibleRect.Width() < VirtualRect.Width())
{
EnableScrollBars(SB_HORZ, TRUE);
m_nHScrollMax = VirtualRect.Width() - 1;
}
else
{
EnableScrollBars(SB_HORZ, FALSE);
m_nHScrollMax = 0;
}
ASSERT(m_nVScrollMax < INT_MAX && m_nHScrollMax < INT_MAX); // This should be fine
/* Old code - CJM
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE;
si.nPage = (m_nHScrollMax>0)? VisibleRect.Width() : 0;
SetScrollInfo(SB_HORZ, &si, FALSE);
si.nPage = (m_nVScrollMax>0)? VisibleRect.Height() : 0;
SetScrollInfo(SB_VERT, &si, FALSE);
SetScrollRange(SB_VERT, 0, m_nVScrollMax, TRUE);
SetScrollRange(SB_HORZ, 0, m_nHScrollMax, TRUE);
*/
// New code - Paul Runstedler
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_RANGE;
si.nPage = (m_nHScrollMax>0)? VisibleRect.Width() : 0;
si.nMin = 0;
si.nMax = m_nHScrollMax;
SetScrollInfo(SB_HORZ, &si, TRUE);
si.fMask |= SIF_DISABLENOSCROLL;
si.nPage = (m_nVScrollMax>0)? VisibleRect.Height() : 0;
si.nMin = 0;
si.nMax = m_nVScrollMax;
SetScrollInfo(SB_VERT, &si, 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)
{
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)
{
return GetCellOrigin(cell.row, cell.col, p);
}
// Returns the bounding box of the cell
BOOL CGridCtrl::GetCellRect(const CCellID& cell, LPRECT pRect)
{
return GetCellRect(cell.row, cell.col, pRect);
}
BOOL CGridCtrl::GetCellRect(int nRow, int nCol, LPRECT pRect)
{
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,%d\n",
// nRow,nCol, CellOrigin.x, CellOrigin.y, GetColumnWidth(nCol), GetRowHeight(nRow),
// pRect->left, pRect->top, pRect->right, pRect->bottom);
return TRUE;
}
BOOL CGridCtrl::GetTextRect(const CCellID& cell, LPRECT pRect)
{
return GetTextRect(cell.row, cell.col, pRect);
}
BOOL CGridCtrl::GetTextRect(int nRow, int nCol, LPRECT pRect)
{
CGridCellBase* pCell = GetCell( nRow, nCol);
if( pCell == NULL)
return FALSE;
if( !GetCellRect( nRow, nCol, pRect) )
return FALSE;
return pCell->GetTextRect( pRect);
}
// Returns the bounding box of a range of cells
BOOL CGridCtrl::GetCellRangeRect(const CCellRange& cellRange, LPRECT lpRect)
{
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;
m_cellDefault.SetFont(&lf);
m_cellFixedColDef.SetFont(&lf);
m_cellFixedRowDef.SetFont(&lf);
m_cellFixedRowColDef.SetFont(&lf);
Refresh();
return result;
}
LRESULT CGridCtrl::OnGetFont(WPARAM /*wParam*/, LPARAM /*lParam*/)
{
//LOGFONT lf;
//m_cellDefault.GetFontObject()->GetLogFont(&lf);
return (LRESULT) m_cellDefault.GetFontObject()->GetSafeHandle();
}
#ifndef _WIN32_WCE_NO_CURSOR
BOOL CGridCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (nHitTest == HTCLIENT)
{
switch (m_MouseMode)
{
case MOUSE_OVER_COL_DIVIDE:
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
break;
case MOUSE_OVER_ROW_DIVIDE:
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
break;
#ifndef GRIDCONTROL_NO_DRAGDROP
case MOUSE_DRAGGING:
break;
#endif
default:
if (!GetVirtualMode())
{
CPoint pt(GetMessagePos());
ScreenToClient(&pt);
CCellID cell = GetCellFromPt(pt);
if (IsValid(cell))
{
CGridCellBase* pCell = GetCell(cell.row, cell.col);
if (pCell)
return pCell->OnSetCursor();
}
}
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
}
return TRUE;
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
#endif
////////////////////////////////////////////////////////////////////////////////////
// Row/Column count functions
BOOL CGridCtrl::SetFixedRowCount(int nFixedRows)
{
if (m_nFixedRows == nFixedRows)
return TRUE;
ASSERT(nFixedRows >= 0);
ResetSelectedRange();
// Force recalculation
m_idTopLeftCell.col = -1;
if (nFixedRows > GetRowCount())
if (!SetRowCount(nFixedRows))
return FALSE;
if (m_idCurrentCell.row < nFixedRows)
SetFocusCell(-1, - 1);
if (!GetVirtualMode())
{
if (nFixedRows > m_nFixedRows)
{
for (int i = m_nFixedRows; i < nFixedRows; i++)
for (int j = 0; j < GetColumnCount(); j++)
{
SetItemState(i, j, GetItemState(i, j) | GVIS_FIXED | GVIS_FIXEDROW);
SetItemBkColour(i, j, CLR_DEFAULT );
SetItemFgColour(i, j, CLR_DEFAULT );
}
}
else
{
for (int i = nFixedRows; i < m_nFixedRows; i++)
{
int j;
for (j = 0; j < GetFixedColumnCount(); j++)
SetItemState(i, j, GetItemState(i, j) & ~GVIS_FIXEDROW );
for (j = GetFixedColumnCount(); j < GetColumnCount(); j++)
{
SetItemState(i, j, GetItemState(i, j) & ~(GVIS_FIXED | GVIS_FIXEDROW) );
SetItemBkColour(i, j, CLR_DEFAULT );
SetItemFgColour(i, j, CLR_DEFAULT );
}
}
}
}
m_nFixedRows = nFixedRows;
Refresh();
return TRUE;
}
BOOL CGridCtrl::SetFixedColumnCount(int nFixedCols)
{
if (m_nFixedCols == nFixedCols)
return TRUE;
ASSERT(nFixedCols >= 0);
if (nFixedCols > GetColumnCount())
if (!SetColumnCount(nFixedCols))
return FALSE;
if (m_idCurrentCell.col < nFixedCols)
SetFocusCell(-1, - 1);
ResetSelectedRange();
// Force recalculation
m_idTopLeftCell.col = -1;
if (!GetVirtualMode())
{
if (nFixedCols > m_nFixedCols)
{
for (int i = 0; i < GetRowCount(); i++)
for (int j = m_nFixedCols; j < nFixedCols; j++)
{
SetItemState(i, j, GetItemState(i, j) | GVIS_FIXED | GVIS_FIXEDCOL);
SetItemBkColour(i, j, CLR_DEFAULT );
SetItemFgColour(i, j, CLR_DEFAULT );
}
}
else
{
{ // Scope limit i,j
for (int i = 0; i < GetFixedRowCount(); i++)
for (int j = nFixedCols; j < m_nFixedCols; j++)
SetItemState(i, j, GetItemState(i, j) & ~GVIS_FIXEDCOL );
}
{// Scope limit i,j
for (int i = GetFixedRowCount(); i < GetRowCount(); i++)
for (int j = nFixedCols; j < m_nFixedCols; j++)
{
SetItemState(i, j, GetItemState(i, j) & ~(GVIS_FIXED | GVIS_FIXEDCOL) );
SetItemBkColour(i, j, CLR_DEFAULT );
SetItemFgColour(i, j, CLR_DEFAULT );
}
}
}
}
m_nFixedCols = nFixedCols;
Refresh();
return TRUE;
}
BOOL CGridCtrl::SetRowCount(int nRows)
{
BOOL bResult = TRUE;
ASSERT(nRows >= 0);
if (nRows == GetRowCount())
return bResult;
// Force recalculation
m_idTopLeftCell.col = -1;
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)
{
if (!GetVirtualMode())
{
for (int row = nRows; row < m_nRows; row++)
{
// Delete cells
for (int col = 0; col < m_nCols; col++)
DestroyCell(row, col);
// Delete rows
GRID_ROW* pRow = m_RowData[row];
if (pRow)
delete pRow;
}
}
m_nRows = nRows;
}
TRY
{
m_arRowHeights.SetSize(nRows);
if (GetVirtualMode())
{
m_nRows = nRows;
if (addedRows > 0)
{
int startRow = nRows - addedRows;
for (int row = startRow; row < nRows; row++)
m_arRowHeights[row] = m_cellDefault.GetHeight();
}
ResetVirtualOrder();
}
else
{
// Change the number of rows.
m_RowData.SetSize(nRows);
// If we have just added rows, we need to construct new elements for each cell
// and set the default row height
if (addedRows > 0)
{
// initialize row heights and data
int startRow = nRows - addedRows;
for (int row = startRow; row < nRows; row++)
{
m_arRowHeights[row] = m_cellDefault.GetHeight();
m_RowData[row] = new GRID_ROW;
m_RowData[row]->SetSize(m_nCols);
for (int col = 0; col < m_nCols; col++)
{
GRID_ROW* pRow = m_RowData[row];
if (pRow && !GetVirtualMode())
pRow->SetAt(col, CreateCell(row, col));
}
m_nRows++;
}
}
}
}
CATCH (CMemoryException, e)
{
e->ReportError();
bResult = FALSE;
}
END_CATCH
SetModified();
ResetScrollBars();
Refresh();
return bResult;
}
BOOL CGridCtrl::SetColumnCount(int nCols)
{
BOOL bResult = TRUE;
ASSERT(nCols >= 0);
if (nCols == GetColumnCount())
return bResult;
// Force recalculation
m_idTopLeftCell.col = -1;
if (nCols < m_nFixedCols)
m_nFixedCols = nCols;
if (m_idCurrentCell.col >= nCols)
SetFocusCell(-1, - 1);
int addedCols = nCols - GetColumnCount();
// If we are about to lose columns, then we need to delete the GridCell objects
// within each column
if (addedCols < 0 && !GetVirtualMode())
{
for (int row = 0; row < m_nRows; row++)
for (int col = nCols; col < GetColumnCount(); col++)
DestroyCell(row, col);
}
TRY
{
// Change the number of columns.
m_arColWidths.SetSize(nCols);
// Change the number of columns in each row.
if (!GetVirtualMode())
for (int i = 0; i < m_nRows; i++)
if (m_RowData[i])
m_RowData[i]->SetSize(nCols);
// If we have just added columns, we need to construct new elements for each cell
// and set the default column width
if (addedCols > 0)
{
int row, col;
// initialized column widths
int startCol = nCols - addedCols;
for (col = startCol; col < nCols; col++)
m_arColWidths[col] = m_cellFixedColDef.GetWidth();
// initialise column data
if (!GetVirtualMode())
{
for (row = 0; row < m_nRows; row++)
for (col = startCol; col < nCols; col++)
{
GRID_ROW* pRow = m_RowData[row];
if (pRow)
pRow->SetAt(col, CreateCell(row, col));
}
}
}
// else // check for selected cell ranges
// ResetSelectedRange();
}
CATCH (CMemoryException, e)
{
e->ReportError();
bResult = FALSE;
}
END_CATCH
m_arColOrder.resize(nCols); // Reset Column Order
for (int i = 0; i < nCols; i++)
{
m_arColOrder[i] = i;
}
m_nCols = nCols;
SetModified();
ResetScrollBars();
Refresh();
return bResult;
}
// Insert a column at a given position, or add to end of columns (if nColumn = -1)
int CGridCtrl::InsertColumn(LPCTSTR strHeading,
UINT nFormat /* = DT_CENTER|DT_VCENTER|DT_SINGLELINE */,
int nColumn /* = -1 */)
{
ASSERT(!m_AllowReorderColumn); // function not implemented in case of m_AllowReorderColumn option
if (nColumn >= 0 && nColumn < m_nFixedCols)
{
// TODO: Fix it so column insertion works for in the fixed column area
ASSERT(FALSE);
return -1;
}
// If the insertion is for a specific column, check it's within range.
if (nColumn >= 0 && nColumn > GetColumnCount())
return -1;
// Force recalculation
m_idTopLeftCell.col = -1;
ResetSelectedRange();
// Gotta be able to at least _see_ some of the column.
if (m_nRows < 1)
SetRowCount(1);
// Allow the user to insert after the last of the columns, but process it as a
// "-1" column, meaning it gets flaged as being the last column, and not a regular
// "insert" routine.
if (nColumn == GetColumnCount())
nColumn = -1;
TRY
{
if (nColumn < 0)
{
nColumn = m_nCols;
m_arColWidths.Add(0);
if (!GetVirtualMode())
{
for (int row = 0; row < m_nRows; row++)
{
GRID_ROW* pRow = m_RowData[row];
if (!pRow)
return -1;
pRow->Add(CreateCell(row, nColumn));
}
}
}
else
{
m_arColWidths.InsertAt(nColumn, (UINT)0);
if (!GetVirtualMode())
{
for (int row = 0; row < m_nRows; row++)
{
GRID_ROW* pRow = m_RowData[row];
if (!pRow)
return -1;
pRow->InsertAt(nColumn, CreateCell(row, nColumn));
}
}
}
}
CATCH (CMemoryException, e)
{
e->ReportError();
return -1;
}
END_CATCH
m_nCols++;
// Initialise column data
SetItemText(0, nColumn, strHeading);
for (int row = 0; row < m_nRows; row++)
SetItemFormat(row, nColumn, nFormat);
// initialized column width
m_arColWidths[nColumn] = GetTextExtent(0, nColumn, strHeading).cx;
if (m_idCurrentCell.col != -1 && nColumn < m_idCurrentCell.col)
m_idCurrentCell.col++;
ResetScrollBars();
SetModified();
return nColumn;
}
// Insert a row at a given position, or add to end of rows (if nRow = -1)
int CGridCtrl::InsertRow(LPCTSTR strHeading, int nRow /* = -1 */)
{
if (nRow >= 0 && nRow < m_nFixedRows)
{
// TODO: Fix it so column insertion works for in the fixed row area
ASSERT(FALSE);
return -1;
}
// If the insertion is for a specific row, check it's within range.
if (nRow >= 0 && nRow >= GetRowCount())
return -1;
// Force recalculation
m_idTopLeftCell.col = -1;
ResetSelectedRange();
// Gotta be able to at least _see_ some of the row.
if (m_nCols < 1)
SetColumnCount(1);
TRY
{
// Adding a row to the bottom
if (nRow < 0)
{
nRow = m_nRows;
m_arRowHeights.Add(0);
if (!GetVirtualMode())
m_RowData.Add(new GRID_ROW);
else
m_arRowOrder.push_back(m_nRows);
}
else
{
m_arRowHeights.InsertAt(nRow, (UINT)0);
if (!GetVirtualMode())
m_RowData.InsertAt(nRow, new GRID_ROW);
else
{
ResetVirtualOrder();
}
}
if (!GetVirtualMode())
m_RowData[nRow]->SetSize(m_nCols);
}
CATCH (CMemoryException, e)
{
e->ReportError();
return -1;
}
END_CATCH
m_nRows++;
// Initialise cell data
if (!GetVirtualMode())
{
for (int col = 0; col < m_nCols; col++)
{
GRID_ROW* pRow = m_RowData[nRow];
if (!pRow)
return -1;
pRow->SetAt(col, CreateCell(nRow, col));
}
}
// Set row title
SetItemText(nRow, 0, strHeading);
// initialized row height
if (strHeading && strHeading[0])
m_arRowHeights[nRow] = GetTextExtent(nRow, 0, strHeading).cy;
else
m_arRowHeights[nRow] = m_cellFixedRowDef.GetHeight();
if (m_idCurrentCell.row != -1 && nRow < m_idCurrentCell.row)
m_idCurrentCell.row++;
ResetScrollBars();
SetModified();
return nRow;
}
///////////////////////////////////////////////////////////////////////////////
// Cell creation stuff
BOOL CGridCtrl::SetCellType(int nRow, int nCol, CRuntimeClass* pRuntimeClass)
{
if (GetVirtualMode())
return FALSE;
ASSERT(IsValid(nRow, nCol));
if (!IsValid(nRow, nCol))
return FALSE;
if (!pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CGridCellBase)))
{
ASSERT( FALSE);
return FALSE;
}
CGridCellBase* pNewCell = (CGridCellBase*) pRuntimeClass->CreateObject();
CGridCellBase* pCurrCell = GetCell(nRow, nCol);
if (pCurrCell)
*pNewCell = *pCurrCell;
SetCell(nRow, nCol, pNewCell);
delete pCurrCell;
return TRUE;
}
BOOL CGridCtrl::SetDefaultCellType( CRuntimeClass* pRuntimeClass)
{
ASSERT( pRuntimeClass != NULL );
if (!pRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CGridCellBase)))
{
ASSERT( FALSE);
return FALSE;
}
m_pRtcDefault = pRuntimeClass;
return TRUE;
}
// Creates a new grid cell and performs any necessary initialisation
/*virtual*/ CGridCellBase* CGridCtrl::CreateCell(int nRow, int nCol)
{
ASSERT(!GetVirtualMode());
if (!m_pRtcDefault || !m_pRtcDefault->IsDerivedFrom(RUNTIME_CLASS(CGridCellBase)))
{
ASSERT( FALSE);
return NULL;
}
CGridCellBase* pCell = (CGridCellBase*) m_pRtcDefault->CreateObject();
if (!pCell)
return NULL;
pCell->SetGrid(this);
pCell->SetCoords(nRow, nCol);
if (nCol < m_nFixedCols)
pCell->SetState(pCell->GetState() | GVIS_FIXED | GVIS_FIXEDCOL);
if (nRow < m_nFixedRows)
pCell->SetState(pCell->GetState() | GVIS_FIXED | GVIS_FIXEDROW);
pCell->SetFormat(pCell->GetDefaultCell()->GetFormat());
return pCell;
}
// Performs any cell cleanup necessary to maintain grid integrity
/*virtual*/ void CGridCtrl::DestroyCell(int nRow, int nCol)
{
// Should NEVER get here in virtual mode.
ASSERT(!GetVirtualMode());
// Set the cells state to 0. If the cell is selected, this
// will remove the cell from the selected list.
SetItemState(nRow, nCol, 0);
delete GetCell(nRow, nCol);
}
BOOL CGridCtrl::DeleteColumn(int nColumn)
{
if (nColumn < 0 || nColumn >= GetColumnCount())
return FALSE;
ResetSelectedRange();
if (!GetVirtualMode())
{
for (int row = 0; row < GetRowCount(); row++)
{
GRID_ROW* pRow = m_RowData[row];
if (!pRow)
return FALSE;
DestroyCell(row, nColumn);
pRow->RemoveAt(nColumn);
}
}
m_arColWidths.RemoveAt(nColumn);
m_nCols--;
if (nColumn < m_nFixedCols)
m_nFixedCols--;
if (nColumn == m_idCurrentCell.col)
m_idCurrentCell.row = m_idCurrentCell.col = -1;
else if (nColumn < m_idCurrentCell.col)
m_idCurrentCell.col--;
ResetScrollBars();
SetModified();
return TRUE;
}
void CGridCtrl::AddSubVirtualRow(int Num, int Nb)
{
ASSERT(Nb == -1 || Nb ==1); // only these vlaues are implemented now
if(!GetVirtualMode()) return;
for(int ind = 0; ind<m_nRows ; ind++)
if(m_arRowOrder[ind]>Num) m_arRowOrder[ind]+=Nb;
if(Nb>0)
m_arRowOrder.insert(m_arRowOrder.begin()+Num,Num);
else
m_arRowOrder.erase(m_arRowOrder.begin()+Num);
}
BOOL CGridCtrl::DeleteRow(int nRow)
{
if (nRow < 0 || nRow >= GetRowCount())
return FALSE;
ResetSelectedRange();
if (!GetVirtualMode())
{
GRID_ROW* pRow = m_RowData[nRow];
if (!pRow)
return FALSE;
for (int col = 0; col < GetColumnCount(); col++)
DestroyCell(nRow, col);
delete pRow;
m_RowData.RemoveAt(nRow);
}
else
AddSubVirtualRow(nRow,-1);
m_arRowHeights.RemoveAt(nRow);
m_nRows--;
if (nRow < m_nFixedRows)
m_nFixedRows--;
if (nRow == m_idCurrentCell.row)
m_idCurrentCell.row = m_idCurrentCell.col = -1;
else if (nRow < m_idCurrentCell.row)
m_idCurrentCell.row--;
ResetScrollBars();
SetModified();
return TRUE;
}
// Handy function that removes all non-fixed rows
BOOL CGridCtrl::DeleteNonFixedRows()
{
ResetSelectedRange();
int nFixed = GetFixedRowCount();
int nCount = GetRowCount();
if (GetVirtualMode())
{
if(nCount != nFixed)
{
SetRowCount(nFixed);
m_arRowOrder.resize(nFixed);
m_arRowHeights.SetSize(nFixed);
m_idCurrentCell.row = m_idCurrentCell.col = -1;
ResetScrollBars();
SetModified();
}
}
else
{
// Delete all data rows
for (int nRow = nCount; nRow >= nFixed; nRow--)
DeleteRow(nRow);
}
return TRUE;
}
// Removes all rows, columns and data from the grid.
BOOL CGridCtrl::DeleteAllItems()
{
ResetSelectedRange();
m_arColWidths.RemoveAll();
m_arRowHeights.RemoveAll();
// Delete all cells in the grid
if (!GetVirtualMode())
{
for (int row = 0; row < m_nRows; row++)
{
for (int col = 0; col < m_nCols; col++)
DestroyCell(row, col);
GRID_ROW* pRow = m_RowData[row];
delete pRow;
}
// Remove all rows
m_RowData.RemoveAll();
}
else
m_arRowOrder.clear();
m_idCurrentCell.row = m_idCurrentCell.col = -1;
m_nRows = m_nFixedRows = m_nCols = m_nFixedCols = 0;
ResetScrollBars();
SetModified();
return TRUE;
}
void CGridCtrl::AutoFill()
{
if (!::IsWindow(m_hWnd))
return;
CRect rect;
GetClientRect(rect);
SetColumnCount(rect.Width() / m_cellDefault.GetWidth() + 1);
SetRowCount(rect.Height() / m_cellDefault.GetHeight() + 1);
SetFixedRowCount(1);
SetFixedColumnCount(1);
ExpandToFit();
}
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl data functions
// Set CListCtrl::GetNextItem for details
CCellID CGridCtrl::GetNextItem(CCellID& cell, int nFlags) const
{
if ((nFlags & GVNI_ALL) == GVNI_ALL)
{ // GVNI_ALL Search whole Grid beginning from cell
// First row (cell.row) -- ONLY Columns to the right of cell
// following rows -- ALL Columns
int row = cell.row , col = cell.col + 1;
if (row <= 0)
row = GetFixedRowCount();
for (; row < GetRowCount(); row++)
{
if (col <= 0)
col = GetFixedColumnCount();
for (; col < GetColumnCount(); col++)
{
int nState = GetItemState(row, col);
if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) ||
(nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED) ||
(nFlags & GVNI_SELECTED && nState & GVIS_SELECTED) ||
(nFlags & GVNI_READONLY && nState & GVIS_READONLY) ||
(nFlags & GVNI_FIXED && nState & GVIS_FIXED) ||
(nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED))
return CCellID(row, col);
}
// go to First Column
col = GetFixedColumnCount();
}
}
else if ((nFlags & GVNI_BELOW) == GVNI_BELOW &&
(nFlags & GVNI_TORIGHT) == GVNI_TORIGHT)
{ // GVNI_AREA Search Grid beginning from cell to Lower-Right of Grid
// Only rows starting with cell.row and below
// All rows -- ONLY Columns to the right of cell
int row = cell.row;
if (row <= 0)
row = GetFixedRowCount();
for (; row < GetRowCount(); row++)
{
int col = cell.col + 1;
if (col <= 0)
col = GetFixedColumnCount();
for (; col < GetColumnCount(); col++)
{
int nState = GetItemState(row, col);
if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) ||
(nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED) ||
(nFlags & GVNI_SELECTED && nState & GVIS_SELECTED) ||
(nFlags & GVNI_READONLY && nState & GVIS_READONLY) ||
(nFlags & GVNI_FIXED && nState & GVIS_FIXED) ||
(nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED))
return CCellID(row, col);
}
}
}
else if ((nFlags & GVNI_ABOVE) == GVNI_ABOVE)
{
for (int row = cell.row - 1; row >= GetFixedRowCount(); row--)
{
int nState = GetItemState(row, cell.col);
if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) ||
(nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED) ||
(nFlags & GVNI_SELECTED && nState & GVIS_SELECTED) ||
(nFlags & GVNI_READONLY && nState & GVIS_READONLY) ||
(nFlags & GVNI_FIXED && nState & GVIS_FIXED) ||
(nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED))
return CCellID(row, cell.col);
}
}
else if ((nFlags & GVNI_BELOW) == GVNI_BELOW)
{
for (int row = cell.row + 1; row < GetRowCount(); row++)
{
int nState = GetItemState(row, cell.col);
if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) ||
(nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED) ||
(nFlags & GVNI_SELECTED && nState & GVIS_SELECTED) ||
(nFlags & GVNI_READONLY && nState & GVIS_READONLY) ||
(nFlags & GVNI_FIXED && nState & GVIS_FIXED) ||
(nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED))
return CCellID(row, cell.col);
}
}
else if ((nFlags & GVNI_TOLEFT) == GVNI_TOLEFT)
{
for (int col = cell.col - 1; col >= GetFixedColumnCount(); col--)
{
int nState = GetItemState(cell.row, col);
if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) ||
(nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED) ||
(nFlags & GVNI_SELECTED && nState & GVIS_SELECTED) ||
(nFlags & GVNI_READONLY && nState & GVIS_READONLY) ||
(nFlags & GVNI_FIXED && nState & GVIS_FIXED) ||
(nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED))
return CCellID(cell.row, col);
}
}
else if ((nFlags & GVNI_TORIGHT) == GVNI_TORIGHT)
{
for (int col = cell.col + 1; col < GetColumnCount(); col++)
{
int nState = GetItemState(cell.row, col);
if ((nFlags & GVNI_DROPHILITED && nState & GVIS_DROPHILITED) ||
(nFlags & GVNI_FOCUSED && nState & GVIS_FOCUSED) ||
(nFlags & GVNI_SELECTED && nState & GVIS_SELECTED) ||
(nFlags & GVNI_READONLY && nState & GVIS_READONLY) ||
(nFlags & GVNI_FIXED && nState & GVIS_FIXED) ||
(nFlags & GVNI_MODIFIED && nState & GVIS_MODIFIED))
return CCellID(cell.row, col);
}
}
return CCellID(-1, -1);
}
// Sorts on a given column using the cell text
BOOL CGridCtrl::SortTextItems(int nCol, BOOL bAscending, LPARAM data /* = 0 */)
{
return CGridCtrl::SortItems(pfnCellTextCompare, nCol, bAscending, data);
}
void CGridCtrl::SetCompareFunction(PFNLVCOMPARE pfnCompare)
{
m_pfnCompare = pfnCompare;
}
// Sorts on a given column using the cell text and using the specified comparison
// function
BOOL CGridCtrl::SortItems(int nCol, BOOL bAscending, LPARAM data /* = 0 */)
{
SetSortColumn(nCol);
SetSortAscending(bAscending);
ResetSelectedRange();
SetFocusCell(-1, - 1);
if (m_pfnCompare == NULL)
return CGridCtrl::SortItems(pfnCellTextCompare, nCol, bAscending, data);
else
return CGridCtrl::SortItems(m_pfnCompare, nCol, bAscending, data);
}
// Sorts on a given column using the supplied compare function (see CListCtrl::SortItems)
BOOL CGridCtrl::SortItems(PFNLVCOMPARE pfnCompare, int nCol, BOOL bAscending,
LPARAM data /* = 0 */)
{
SetSortColumn(nCol);
SetSortAscending(bAscending);
ResetSelectedRange();
SetFocusCell(-1, -1);
return SortItems(pfnCompare, nCol, bAscending, data, GetFixedRowCount(), -1);
}
int CALLBACK CGridCtrl::pfnCellTextCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
UNUSED_ALWAYS(lParamSort);
CGridCellBase* pCell1 = (CGridCellBase*) lParam1;
CGridCellBase* pCell2 = (CGridCellBase*) lParam2;
if (!pCell1 || !pCell2) return 0;
return _tcscmp(pCell1->GetText(), pCell2->GetText());
}
int CALLBACK CGridCtrl::pfnCellNumericCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
UNUSED_ALWAYS(lParamSort);
CGridCellBase* pCell1 = (CGridCellBase*) lParam1;
CGridCellBase* pCell2 = (CGridCellBase*) lParam2;
if (!pCell1 || !pCell2) return 0;
int nValue1 = _ttol(pCell1->GetText());
int nValue2 = _ttol(pCell2->GetText());
if (nValue1 < nValue2)
return -1;
else if (nValue1 == nValue2)
return 0;
else
return 1;
}
CGridCtrl * CGridCtrl::m_This;
// private recursive sort implementation
bool CGridCtrl::NotVirtualCompare(int c1, int c2)
{
return ! CGridCtrl::m_This->m_pfnVirtualCompare(c1, c2);
}
BOOL CGridCtrl::SortItems(PFNLVCOMPARE pfnCompare, int nCol, BOOL bAscending, LPARAM data,
int low, int high)
{
if (nCol >= GetColumnCount())
return FALSE;
if (high == -1)
high = GetRowCount() - 1;
int lo = low;
int hi = high;
if (hi <= lo)
return FALSE;
if (GetVirtualMode())
{
ASSERT(m_pfnVirtualCompare);
m_CurCol = m_arColOrder[nCol];
m_This = this;
std::stable_sort(m_arRowOrder.begin() + m_nFixedRows, m_arRowOrder.end(),
bAscending ? m_pfnVirtualCompare : NotVirtualCompare);
return TRUE;
}
//LPARAM midItem = GetItemData((lo + hi)/2, nCol);
LPARAM pMidCell = (LPARAM) GetCell((lo + hi)/2, nCol);
// loop through the list until indices cross
while (lo <= hi)
{
// Find the first element that is greater than or equal to the partition
// element starting from the left Index.
if (bAscending)
while (lo < high && pfnCompare( (LPARAM)GetCell(lo, nCol), (LPARAM) pMidCell, data) < 0)
++lo;
else
while (lo < high && pfnCompare((LPARAM)GetCell(lo, nCol), pMidCell, data) > 0)
++lo;
// Find an element that is smaller than or equal to the partition
// element starting from the right Index.
if (bAscending)
while (hi > low && pfnCompare((LPARAM)GetCell(hi, nCol), pMidCell, data) > 0)
--hi;
else
while (hi > low && pfnCompare((LPARAM)GetCell(hi, nCol), pMidCell, data) < 0)
--hi;
// If the indexes have not crossed, swap if the items are not equal
if (lo <= hi)
{
// swap only if the items are not equal
if (pfnCompare((LPARAM)GetCell(lo, nCol), (LPARAM)GetCell(hi, nCol), data) != 0)
{
for (int col = 0; col < GetColumnCount(); col++)
{
CGridCellBase *pCell = GetCell(lo, col);
SetCell(lo, col, GetCell(hi, col));
SetCell(hi, col, pCell);
}
UINT nRowHeight = m_arRowHeights[lo];
m_arRowHeights[lo] = m_arRowHeights[hi];
m_arRowHeights[hi] = nRowHeight;
}
++lo;
--hi;
}
}
// If the right index has not reached the left side of array
// must now sort the left partition.
if (low < hi)
SortItems(pfnCompare, nCol, bAscending, data, low, hi);
// If the left index has not reached the right side of array
// must now sort the right partition.
if (lo < high)
SortItems(pfnCompare, nCol, bAscending, data, lo, high);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl data functions
BOOL CGridCtrl::SetItem(const GV_ITEM* pItem)
{
if (!pItem || GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(pItem->row, pItem->col);
if (!pCell)
return FALSE;
SetModified(TRUE, pItem->row, pItem->col);
if (pItem->mask & GVIF_TEXT)
pCell->SetText(pItem->strText);
if (pItem->mask & GVIF_PARAM)
pCell->SetData(pItem->lParam);
if (pItem->mask & GVIF_IMAGE)
pCell->SetImage(pItem->iImage);
if (pItem->mask & GVIF_STATE)
SetItemState(pItem->row, pItem->col, pItem->nState);
if (pItem->mask & GVIF_FORMAT)
pCell->SetFormat(pItem->nFormat);
if (pItem->mask & GVIF_BKCLR)
pCell->SetBackClr(pItem->crBkClr);
if (pItem->mask & GVIF_FGCLR)
pCell->SetTextClr(pItem->crFgClr);
if (pItem->mask & GVIF_FONT)
pCell->SetFont(&(pItem->lfFont));
if( pItem->mask & GVIF_MARGIN)
pCell->SetMargin( pItem->nMargin);
return TRUE;
}
BOOL CGridCtrl::GetItem(GV_ITEM* pItem)
{
if (!pItem)
return FALSE;
CGridCellBase* pCell = GetCell(pItem->row, pItem->col);
if (!pCell)
return FALSE;
if (pItem->mask & GVIF_TEXT)
pItem->strText = GetItemText(pItem->row, pItem->col);
if (pItem->mask & GVIF_PARAM)
pItem->lParam = pCell->GetData();;
if (pItem->mask & GVIF_IMAGE)
pItem->iImage = pCell->GetImage();
if (pItem->mask & GVIF_STATE)
pItem->nState = pCell->GetState();
if (pItem->mask & GVIF_FORMAT)
pItem->nFormat = pCell->GetFormat();
if (pItem->mask & GVIF_BKCLR)
pItem->crBkClr = pCell->GetBackClr();
if (pItem->mask & GVIF_FGCLR)
pItem->crFgClr = pCell->GetTextClr();
if (pItem->mask & GVIF_FONT)
memcpy(&(pItem->lfFont), pCell->GetFont(), sizeof(LOGFONT));
if( pItem->mask & GVIF_MARGIN)
pItem->nMargin = pCell->GetMargin();
return TRUE;
}
BOOL CGridCtrl::SetItemText(int nRow, int nCol, LPCTSTR str)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
if (!pCell)
return FALSE;
pCell->SetText(str);
SetModified(TRUE, nRow, nCol);
return TRUE;
}
#if !defined(_WIN32_WCE) || (_WIN32_WCE >= 210)
// EFW - 06/13/99 - Added to support printf-style formatting codes
BOOL CGridCtrl::SetItemTextFmt(int nRow, int nCol, LPCTSTR szFmt, ...)
{
if (GetVirtualMode())
return FALSE;
CString strText;
va_list argptr;
CGridCellBase* pCell = GetCell(nRow, nCol);
if (!pCell)
return FALSE;
// Format the message text
va_start(argptr, szFmt);
strText.FormatV(szFmt, argptr);
va_end(argptr);
pCell->SetText(strText);
SetModified(TRUE, nRow, nCol);
return TRUE;
}
// EFW - 06/13/99 - Added to support string resource ID. Supports
// a variable argument list too.
BOOL CGridCtrl::SetItemTextFmtID(int nRow, int nCol, UINT nID, ...)
{
if (GetVirtualMode())
return FALSE;
CString strFmt, strText;
va_list argptr;
CGridCellBase* pCell = GetCell(nRow, nCol);
if (!pCell)
return FALSE;
// Format the message text
va_start(argptr, nID);
VERIFY(strFmt.LoadString(nID));
strText.FormatV(strFmt, argptr);
va_end(argptr);
pCell->SetText(strText);
SetModified(TRUE, nRow, nCol);
return TRUE;
}
#endif
BOOL CGridCtrl::SetItemData(int nRow, int nCol, LPARAM lParam)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
if (!pCell)
return FALSE;
pCell->SetData(lParam);
SetModified(TRUE, nRow, nCol);
return TRUE;
}
LPARAM CGridCtrl::GetItemData(int nRow, int nCol) const
{
CGridCellBase* pCell = GetCell(nRow, nCol);
if (!pCell)
return (LPARAM) 0;
return pCell->GetData();
}
BOOL CGridCtrl::SetItemImage(int nRow, int nCol, int iImage)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
if (!pCell)
return FALSE;
pCell->SetImage(iImage);
SetModified(TRUE, nRow, nCol);
return TRUE;
}
int CGridCtrl::GetItemImage(int nRow, int nCol) const
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return -1;
return pCell->GetImage();
}
BOOL CGridCtrl::SetItemState(int nRow, int nCol, UINT state)
{
BOOL bSelected = IsCellSelected(nRow, nCol);
// If the cell is being unselected, remove it from the selected list
if (bSelected && !(state & GVIS_SELECTED))
{
CCellID cell;
DWORD key = MAKELONG(nRow, nCol);
if (m_SelectedCellMap.Lookup(key, (CCellID&)cell))
m_SelectedCellMap.RemoveKey(key);
}
// If cell is being selected, add it to the list of selected cells
else if (!bSelected && (state & GVIS_SELECTED))
{
CCellID cell(nRow, nCol);
m_SelectedCellMap.SetAt(MAKELONG(nRow, nCol), cell);
}
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return FALSE;
// Set the cell's state
pCell->SetState(state);
return TRUE;
}
UINT CGridCtrl::GetItemState(int nRow, int nCol) const
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return 0;
return pCell->GetState();
}
BOOL CGridCtrl::SetItemFormat(int nRow, int nCol, UINT nFormat)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return FALSE;
pCell->SetFormat(nFormat);
return TRUE;
}
UINT CGridCtrl::GetItemFormat(int nRow, int nCol) const
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return 0;
return pCell->GetFormat();
}
BOOL CGridCtrl::SetItemBkColour(int nRow, int nCol, COLORREF cr /* = CLR_DEFAULT */)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return FALSE;
pCell->SetBackClr(cr);
return TRUE;
}
COLORREF CGridCtrl::GetItemBkColour(int nRow, int nCol) const
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return 0;
return pCell->GetBackClr();
}
BOOL CGridCtrl::SetItemFgColour(int nRow, int nCol, COLORREF cr /* = CLR_DEFAULT */)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return FALSE;
pCell->SetTextClr(cr);
return TRUE;
}
COLORREF CGridCtrl::GetItemFgColour(int nRow, int nCol) const
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return 0;
return pCell->GetTextClr();
}
BOOL CGridCtrl::SetItemFrColour(int nRow, int nCol, COLORREF cr /* = CLR_DEFAULT */)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return FALSE;
pCell->SetFrameClr(cr);
return TRUE;
}
COLORREF CGridCtrl::GetItemFrColour(int nRow, int nCol) const
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return 0;
return pCell->GetFrameClr();
}
BOOL CGridCtrl::SetItemFont(int nRow, int nCol, const LOGFONT* plf)
{
if (GetVirtualMode())
return FALSE;
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return FALSE;
pCell->SetFont(plf);
return TRUE;
}
const LOGFONT* CGridCtrl::GetItemFont(int nRow, int nCol)
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return GetDefaultCell(nRow < GetFixedRowCount(), nCol < GetFixedColumnCount())->GetFont();
return pCell->GetFont();
}
BOOL CGridCtrl::IsItemEditing(int nRow, int nCol)
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return FALSE;
return pCell->IsEditing();
}
////////////////////////////////////////////////////////////////////////////////////
// Row/Column size functions
long CGridCtrl::GetVirtualWidth() const
{
long lVirtualWidth = 0;
int iColCount = GetColumnCount();
for (int i = 0; i < iColCount; i++)
lVirtualWidth += m_arColWidths[i];
return lVirtualWidth;
}
long CGridCtrl::GetVirtualHeight() const
{
long lVirtualHeight = 0;
int iRowCount = GetRowCount();
for (int i = 0; i < iRowCount; i++)
lVirtualHeight += m_arRowHeights[i];
return lVirtualHeight;
}
int CGridCtrl::GetRowHeight(int nRow) const
{
ASSERT(nRow >= 0 && nRow < m_nRows);
if (nRow < 0 || nRow >= m_nRows)
return -1;
return m_arRowHeights[nRow];
}
int CGridCtrl::GetColumnWidth(int nCol) const
{
ASSERT(nCol >= 0 && nCol < m_nCols);
if (nCol < 0 || nCol >= m_nCols)
return -1;
return m_arColWidths[m_arColOrder[nCol]];
}
BOOL CGridCtrl::SetRowHeight(int nRow, int height)
{
ASSERT(nRow >= 0 && nRow < m_nRows && height >= 0);
if (nRow < 0 || nRow >= m_nRows || height < 0)
return FALSE;
m_arRowHeights[nRow] = height;
ResetScrollBars();
return TRUE;
}
BOOL CGridCtrl::SetColumnWidth(int nCol, int width)
{
ASSERT(nCol >= 0 && nCol < m_nCols && width >= 0);
if (nCol < 0 || nCol >= m_nCols || width < 0)
return FALSE;
m_arColWidths[m_arColOrder[nCol]] = width;
ResetScrollBars();
return TRUE;
}
int CGridCtrl::GetFixedRowHeight() const
{
int nHeight = 0;
for (int i = 0; i < m_nFixedRows; i++)
nHeight += GetRowHeight(i);
return nHeight;
}
int CGridCtrl::GetFixedColumnWidth() const
{
int nWidth = 0;
for (int i = 0; i < m_nFixedCols; i++)
nWidth += GetColumnWidth(i);
return nWidth;
}
BOOL CGridCtrl::AutoSizeColumn(int nCol, UINT nAutoSizeStyle /*=GVS_DEFAULT*/,
BOOL bResetScroll /*=TRUE*/)
{
ASSERT(nCol >= 0 && nCol < m_nCols);
if (nCol < 0 || nCol >= m_nCols)
return FALSE;
// Skip hidden columns when autosizing
if( GetColumnWidth( nCol) <=0 )
return FALSE;
CSize size;
CDC* pDC = GetDC();
if (!pDC)
return FALSE;
int nWidth = 0;
ASSERT(GVS_DEFAULT <= nAutoSizeStyle && nAutoSizeStyle <= GVS_BOTH);
if (nAutoSizeStyle == GVS_DEFAULT)
nAutoSizeStyle = GetAutoSizeStyle();
int nStartRow = (nAutoSizeStyle & GVS_HEADER)? 0 : GetFixedRowCount();
int nEndRow = (nAutoSizeStyle & GVS_DATA)? GetRowCount()-1 : GetFixedRowCount()-1;
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(nStartRow, nCol, nEndRow, nCol));
for (int nRow = nStartRow; nRow <= nEndRow; nRow++)
{
CGridCellBase* pCell = GetCell(nRow, nCol);
if (pCell)
size = pCell->GetCellExtent(pDC);
if (size.cx > nWidth)
nWidth = size.cx;
}
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(-1,-1,-1,-1));
m_arColWidths[nCol] = nWidth;
ReleaseDC(pDC);
if (bResetScroll)
ResetScrollBars();
return TRUE;
}
BOOL CGridCtrl::AutoSizeRow(int nRow, BOOL bResetScroll /*=TRUE*/)
{
ASSERT(nRow >= 0 && nRow < m_nRows);
if (nRow < 0 || nRow >= m_nRows)
return FALSE;
// Skip hidden rows when autosizing
if( GetRowHeight( nRow) <=0 )
return FALSE;
CSize size;
CDC* pDC = GetDC();
if (!pDC)
return FALSE;
int nHeight = 0;
int nNumColumns = GetColumnCount();
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(nRow, 0, nRow, nNumColumns));
for (int nCol = 0; nCol < nNumColumns; nCol++)
{
CGridCellBase* pCell = GetCell(nRow, nCol);
if (pCell)
size = pCell->GetCellExtent(pDC);
if (size.cy > nHeight)
nHeight = size.cy;
}
m_arRowHeights[nRow] = nHeight;
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(-1,-1,-1,-1));
ReleaseDC(pDC);
if (bResetScroll)
ResetScrollBars();
return TRUE;
}
void CGridCtrl::AutoSizeColumns(UINT nAutoSizeStyle /*=GVS_DEFAULT*/)
{
int nNumColumns = GetColumnCount();
for (int nCol = 0; nCol < nNumColumns; nCol++)
{
// Skip hidden columns when autosizing
if( GetColumnWidth( nCol) > 0 )
AutoSizeColumn(nCol, nAutoSizeStyle, FALSE);
}
ResetScrollBars();
}
void CGridCtrl::AutoSizeRows()
{
int nNumRows = GetRowCount();
for (int nRow = 0; nRow < nNumRows; nRow++)
{
// Skip hidden rows when autosizing
if( GetRowHeight( nRow) > 0 )
AutoSizeRow(nRow, FALSE);
}
ResetScrollBars();
}
// sizes all rows and columns
// faster than calling both AutoSizeColumns() and AutoSizeRows()
void CGridCtrl::AutoSize(UINT nAutoSizeStyle /*=GVS_DEFAULT*/)
{
CDC* pDC = GetDC();
if (!pDC)
return;
int nNumColumns = GetColumnCount();
int nCol, nRow;
ASSERT(GVS_DEFAULT <= nAutoSizeStyle && nAutoSizeStyle <= GVS_BOTH);
if (nAutoSizeStyle == GVS_DEFAULT)
nAutoSizeStyle = GetAutoSizeStyle();
int nStartRow = (nAutoSizeStyle & GVS_HEADER)? 0 : GetFixedRowCount();
int nEndRow = (nAutoSizeStyle & GVS_DATA)? GetRowCount()-1 : GetFixedRowCount()-1;
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(nStartRow, 0, nEndRow, nNumColumns));
// Row initialisation - only work on rows whose height is > 0
for (nRow = nStartRow; nRow <= nEndRow; nRow++)
{
if( GetRowHeight( nRow) > 0 )
m_arRowHeights[nRow] = 1;
}
CSize size;
for (nCol = 0; nCol < nNumColumns; nCol++)
{
// Don't size hidden columns or rows
if( GetColumnWidth( nCol) > 0 )
{
// Skip columns that are hidden, but now initialize
m_arColWidths[nCol] = 0;
for (nRow = nStartRow; nRow <= nEndRow; nRow++)
{
if( GetRowHeight( nRow) > 0 )
{
CGridCellBase* pCell = GetCell(nRow, nCol);
if (pCell)
size = pCell->GetCellExtent(pDC);
if (size.cx >(int) m_arColWidths[nCol])
m_arColWidths[nCol] = size.cx;
if (size.cy >(int) m_arRowHeights[nRow])
m_arRowHeights[nRow] = size.cy;
}
}
}
}
if (GetVirtualMode())
SendCacheHintToParent(CCellRange(-1,-1,-1,-1));
ReleaseDC(pDC);
ResetScrollBars();
Refresh();
}
// Expands the columns to fit the screen space. If bExpandFixed is FALSE then fixed
// columns will not be affected
void CGridCtrl::ExpandColumnsToFit(BOOL bExpandFixed /*=TRUE*/)
{
if (bExpandFixed)
{
if (GetColumnCount() <= 0) return;
}
else
{
if (GetColumnCount() <= GetFixedColumnCount()) return;
}
EnableScrollBars(SB_HORZ, FALSE);
int col;
CRect rect;
GetClientRect(rect);
int nFirstColumn = (bExpandFixed)? 0 : GetFixedColumnCount();
int nNumColumnsAffected = 0;
for (col = nFirstColumn; col < GetColumnCount(); col++)
{
if (m_arColWidths[col] > 0)
nNumColumnsAffected++;
}
if (nNumColumnsAffected <= 0)
return;
long virtualWidth = GetVirtualWidth();
int nDifference = rect.Width() -(int) virtualWidth;
int nColumnAdjustment = nDifference / nNumColumnsAffected;
for (col = nFirstColumn; col < GetColumnCount(); col++)
{
if (m_arColWidths[col] > 0)
m_arColWidths[col] += nColumnAdjustment;
}
if (nDifference > 0)
{
int leftOver = nDifference % nNumColumnsAffected;
for (int nCount = 0, col = nFirstColumn;
(col < GetColumnCount()) && (nCount < leftOver); col++, nCount++)
{
if (m_arColWidths[col] > 0)
m_arColWidths[col] += 1;
}
}
else
{
int leftOver = (-nDifference) % nNumColumnsAffected;
for (int nCount = 0, col = nFirstColumn;
(col < GetColumnCount()) && (nCount < leftOver); col++, nCount++)
{
if (m_arColWidths[col] > 0)
m_arColWidths[col] -= 1;
}
}
Refresh();
ResetScrollBars();
}
void CGridCtrl::ExpandLastColumn()
{
if (GetColumnCount() <= 0)
return;
// Search for last non-hidden column
int nLastColumn = GetColumnCount()-1;
while (m_arColWidths[nLastColumn] <= 0)
nLastColumn--;
if (nLastColumn <= 0)
return;
EnableScrollBars(SB_HORZ, FALSE);
CRect rect;
GetClientRect(rect);
long virtualWidth = GetVirtualWidth();
int nDifference = rect.Width() -(int) virtualWidth;
if (nDifference > 0)
{
//if (GetVirtualHeight() > rect.Height())
// nDifference -= GetSystemMetrics(SM_CXVSCROLL);
m_arColWidths[ nLastColumn ] += nDifference;
Refresh();
}
ResetScrollBars();
}
// Expands the rows to fit the screen space. If bExpandFixed is FALSE then fixed
// rows will not be affected
void CGridCtrl::ExpandRowsToFit(BOOL bExpandFixed /*=TRUE*/)
{
if (bExpandFixed)
{
if (GetRowCount() <= 0) return;
}
else
{
if (GetRowCount() <= GetFixedRowCount()) return;
}
EnableScrollBars(SB_VERT, FALSE);
int row;
CRect rect;
GetClientRect(rect);
int nFirstRow = (bExpandFixed)? 0 : GetFixedRowCount();
int nNumRowsAffected = 0;
for (row = nFirstRow; row < GetRowCount(); row++)
{
if (m_arRowHeights[row] > 0)
nNumRowsAffected++;
}
if (nNumRowsAffected <= 0)
return;
long virtualHeight = GetVirtualHeight();
int nDifference = rect.Height() -(int) virtualHeight;
int nRowAdjustment = nDifference / nNumRowsAffected;
for (row = nFirstRow; row < GetRowCount(); row++)
{
if (m_arRowHeights[row] > 0)
m_arRowHeights[row] += nRowAdjustment;
}
if (nDifference > 0)
{
int leftOver = nDifference % nNumRowsAffected;
for (int nCount = 0, row = nFirstRow;
(row < GetRowCount()) && (nCount < leftOver); row++, nCount++)
{
if (m_arRowHeights[row] > 0)
m_arRowHeights[row] += 1;
}
}
else
{
int leftOver = (-nDifference) % nNumRowsAffected;
for (int nCount = 0, row = nFirstRow;
(row < GetRowCount()) && (nCount < leftOver); row++, nCount++)
{
if (m_arRowHeights[row] > 0)
m_arRowHeights[row] -= 1;
}
}
Refresh();
ResetScrollBars();
}
// Expands the cells to fit the screen space. If bExpandFixed is FALSE then fixed
// cells will not be affected
void CGridCtrl::ExpandToFit(BOOL bExpandFixed /*=TRUE*/)
{
ExpandColumnsToFit(bExpandFixed); // This will remove any existing horz scrollbar
ExpandRowsToFit(bExpandFixed); // This will remove any existing vert scrollbar
ExpandColumnsToFit(bExpandFixed); // Just in case the first adjustment was with a vert
// scrollbar in place
Refresh();
}
/////////////////////////////////////////////////////////////////////////////////////
// Attributes
void CGridCtrl::SetVirtualMode(BOOL bVirtual)
{
DeleteAllItems();
m_bVirtualMode = bVirtual;
// Force some defaults here.
if (m_bVirtualMode)
{
SetEditable(FALSE);
SetAutoSizeStyle(GVS_HEADER);
SetFixedColumnSelection(FALSE);
SetFixedRowSelection(FALSE);
ResetVirtualOrder();
}
}
void CGridCtrl::SetGridLines(int nWhichLines /*=GVL_BOTH*/)
{
m_nGridLines = nWhichLines;
Refresh();
}
void CGridCtrl::SetListMode(BOOL bEnableListMode /*=TRUE*/)
{
ResetSelectedRange();
SetSortColumn(-1);
m_bListMode = bEnableListMode;
SetFixedRowSelection(FALSE);
Refresh();
}
void CGridCtrl::SetSortColumn(int nCol)
{
if (m_nSortColumn >= 0)
InvalidateCellRect(0, m_nSortColumn);
m_nSortColumn = nCol;
if (nCol >= 0)
InvalidateCellRect(0, nCol);
}
BOOL CGridCtrl::IsCellFixed(int nRow, int nCol)
{
return (nRow < GetFixedRowCount() || nCol < GetFixedColumnCount());
}
void CGridCtrl::SetModified(BOOL bModified /*=TRUE*/, int nRow /*=-1*/, int nCol /*=-1*/)
{
// Cannot guarantee sorting anymore...
if (nCol < 0 || nCol == GetSortColumn())
SetSortColumn(-1);
if (nRow >= 0 && nCol >= 0)
{
if (bModified)
{
SetItemState(nRow, nCol, GetItemState(nRow, nCol) | GVIS_MODIFIED);
m_bModified = TRUE;
}
else
SetItemState(nRow, nCol, GetItemState(nRow, nCol) & ~GVIS_MODIFIED);
}
else
m_bModified = bModified;
if (!m_bModified)
{
for (int row = 0; row < GetRowCount(); row++)
for (int col = 0; col < GetColumnCount(); col++)
SetItemState(row, col, GetItemState(row, col) & ~GVIS_MODIFIED);
}
}
BOOL CGridCtrl::GetModified(int nRow /*=-1*/, int nCol /*=-1*/)
{
if (nRow >= 0 && nCol >= 0)
return ( (GetItemState(nRow, nCol) & GVIS_MODIFIED) == GVIS_MODIFIED );
else
return m_bModified;
}
/////////////////////////////////////////////////////////////////////////////////////
// GridCtrl cell visibility tests and invalidation/redraw functions
void CGridCtrl::Refresh()
{
if (GetSafeHwnd() && m_bAllowDraw)
Invalidate();
}
// EnsureVisible supplied by Roelf Werkman
void CGridCtrl::EnsureVisible(int nRow, int nCol)
{
if (!m_bAllowDraw)
return;
CRect rectWindow;
/*
// set the scroll to the approximate position of row (Nigel Page-Jones)
int nPos = (int)((float)nRow / GetRowCount() * 1000);
float fPos = (float)nPos / 1000;
SCROLLINFO scrollInfo;
GetScrollInfo(SB_VERT, &scrollInfo);
scrollInfo.nPos = (int)(scrollInfo.nMax * fPos);
SetScrollInfo(SB_VERT, &scrollInfo, FALSE);
GetClientRect(rectWindow);
// redraw cells if necessary (Nigel Page-Jones)
CCellID idTopLeft = GetTopleftNonFixedCell(FALSE);
CCellID idNewTopLeft = GetTopleftNonFixedCell(TRUE);
if (idNewTopLeft != idTopLeft)
{
rectWindow.top = GetFixedRowHeight();
InvalidateRect(rectWindow);
}
*/
// We are going to send some scroll messages, which will steal the focus
// from it's rightful owner. Squirrel it away ourselves so we can give
// it back. (Damir)
CWnd* pFocusWnd = GetFocus();
CCellRange VisibleCells = GetVisibleNonFixedCellRange();
int right = nCol - VisibleCells.GetMaxCol();
int left = VisibleCells.GetMinCol() - nCol;
int down = nRow - VisibleCells.GetMaxRow();
int up = VisibleCells.GetMinRow() - nRow;
int iColumnStart;
int iRowStart;
iColumnStart = VisibleCells.GetMaxCol() + 1;
while( right > 0 )
{
if( GetColumnWidth( iColumnStart ) > 0 )
SendMessage( WM_HSCROLL, SB_LINERIGHT, 0 );
right--;
iColumnStart++;
}
iColumnStart = VisibleCells.GetMinCol() - 1;
while( left > 0 )
{
if( GetColumnWidth( iColumnStart ) > 0 )
SendMessage( WM_HSCROLL, SB_LINELEFT, 0 );
left--;
iColumnStart--;
}
iRowStart = VisibleCells.GetMaxRow() + 1;
while( down > 0 )
{
if( GetRowHeight( iRowStart ) > 0 )
SendMessage( WM_VSCROLL, SB_LINEDOWN, 0 );
down--;
iRowStart++;
}
iRowStart = VisibleCells.GetMinRow() - 1;
while( up > 0 )
{
if( GetRowHeight( iRowStart ) > 0 )
SendMessage( WM_VSCROLL, SB_LINEUP, 0 );
up--;
iRowStart--;
}
// Move one more if we only see a snall bit of the cell
CRect rectCell;
if (!GetCellRect(nRow, nCol, rectCell))
{
if (pFocusWnd && ::IsWindow(pFocusWnd->GetSafeHwnd()))
pFocusWnd->SetFocus();
return;
}
GetClientRect(rectWindow);
// The previous fix was fixed properly by Martin Richter
while (rectCell.right > rectWindow.right
&& rectCell.left > GetFixedColumnWidth()
&& IsVisibleHScroll() // Junlin Xu: added to prevent infinite loop
)
{
SendMessage(WM_HSCROLL, SB_LINERIGHT, 0);
if (!GetCellRect(nRow, nCol, rectCell))
{
pFocusWnd->SetFocus();
return;
}
}
while (rectCell.bottom > rectWindow.bottom
&& rectCell.top > GetFixedRowHeight()
&& IsVisibleVScroll() // Junlin Xu: added to prevent infinite loop
)
{
SendMessage(WM_VSCROLL, SB_LINEDOWN, 0);
if (!GetCellRect(nRow, nCol, rectCell))
{
pFocusWnd->SetFocus();
return;
}
}
// restore focus to whoever owned it
pFocusWnd->SetFocus();
}
BOOL CGridCtrl::IsCellEditable(CCellID &cell) const
{
return IsCellEditable(cell.row, cell.col);
}
BOOL CGridCtrl::IsCellEditable(int nRow, int nCol) const
{
return IsEditable() && ((GetItemState(nRow, nCol) & GVIS_READONLY) != GVIS_READONLY);
}
BOOL CGridCtrl::IsCellSelected(CCellID &cell) const
{
return IsCellSelected(cell.row, cell.col);
}
BOOL CGridCtrl::IsCellSelected(int nRow, int nCol) const
{
if (GetVirtualMode())
{
if (!IsSelectable())
return FALSE;
CCellID cell;
DWORD key = MAKELONG(nRow, nCol);
return (m_SelectedCellMap.Lookup(key, (CCellID&)cell));
}
else
return IsSelectable() && ((GetItemState(nRow, nCol) & GVIS_SELECTED) == GVIS_SELECTED);
}
BOOL CGridCtrl::IsCellVisible(CCellID cell)
{
return IsCellVisible(cell.row, cell.col);
}
BOOL CGridCtrl::IsCellVisible(int nRow, int nCol)
{
if (!IsWindow(m_hWnd))
return FALSE;
int x, y;
CCellID TopLeft;
if (nCol >= GetFixedColumnCount() || nRow >= GetFixedRowCount())
{
TopLeft = GetTopleftNonFixedCell();
if (nCol >= GetFixedColumnCount() && nCol < TopLeft.col)
return FALSE;
if (nRow >= GetFixedRowCount() && nRow < TopLeft.row)
return FALSE;
}
CRect rect;
GetClientRect(rect);
if (nCol < GetFixedColumnCount())
{
x = 0;
for (int i = 0; i <= nCol; i++)
{
if (x >= rect.right)
return FALSE;
x += GetColumnWidth(i);
}
}
else
{
x = GetFixedColumnWidth();
for (int i = TopLeft.col; i <= nCol; i++)
{
if (x >= rect.right)
return FALSE;
x += GetColumnWidth(i);
}
}
if (nRow < GetFixedRowCount())
{
y = 0;
for (int i = 0; i <= nRow; i++)
{
if (y >= rect.bottom)
return FALSE;
y += GetRowHeight(i);
}
}
else
{
if (nRow < TopLeft.row)
return FALSE;
y = GetFixedRowHeight();
for (int i = TopLeft.row; i <= nRow; i++)
{
if (y >= rect.bottom)
return FALSE;
y += GetRowHeight(i);
}
}
return TRUE;
}
BOOL CGridCtrl::InvalidateCellRect(const CCellID& cell)
{
return InvalidateCellRect(cell.row, cell.col);
}
BOOL CGridCtrl::InvalidateCellRect(const int row, const int col)
{
if (!::IsWindow(GetSafeHwnd()) || !m_bAllowDraw)
return FALSE;
if (!IsValid(row, col))
return FALSE;
if (!IsCellVisible(row, col))
return FALSE;
CRect rect;
if (!GetCellRect(row, col, rect))
return FALSE;
rect.right++;
rect.bottom++;
InvalidateRect(rect, TRUE);
return TRUE;
}
BOOL CGridCtrl::InvalidateCellRect(const CCellRange& cellRange)
{
ASSERT(IsValid(cellRange));
if (!::IsWindow(GetSafeHwnd()) || !m_bAllowDraw)
return FALSE;
CCellRange visibleCellRange = GetVisibleNonFixedCellRange().Intersect(cellRange);
CRect rect;
if (!GetCellRangeRect(visibleCellRange, rect))
return FALSE;
rect.right++;
rect.bottom++;
InvalidateRect(rect, TRUE);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl Mouse stuff
// Handles mouse wheel notifications
// Note - if this doesn't work for win95 then use OnRegisteredMouseWheel instead
#if !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
BOOL CGridCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// A m_nRowsPerWheelNotch value less than 0 indicates that the mouse
// wheel scrolls whole pages, not just lines.
if (m_nRowsPerWheelNotch == -1)
{
int nPagesScrolled = zDelta / 120;
if (nPagesScrolled > 0)
{
for (int i = 0; i < nPagesScrolled; i++)
{
PostMessage(WM_VSCROLL, SB_PAGEUP, 0);
}
}
else
{
for (int i = 0; i > nPagesScrolled; i--)
{
PostMessage(WM_VSCROLL, SB_PAGEDOWN, 0);
}
}
}
else
{
int nRowsScrolled = m_nRowsPerWheelNotch * zDelta / 120;
if (nRowsScrolled > 0)
{
for (int i = 0; i < nRowsScrolled; i++)
{
PostMessage(WM_VSCROLL, SB_LINEUP, 0);
}
}
else
{
for (int i = 0; i > nRowsScrolled; i--)
{
PostMessage(WM_VSCROLL, SB_LINEDOWN, 0);
}
}
}
return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}
#endif // !defined(_WIN32_WCE) && (_MFC_VER >= 0x0421)
void CGridCtrl::OnMouseMove(UINT /*nFlags*/, CPoint point)
{
CRect rect;
GetClientRect(rect);
#ifndef GRIDCONTROL_NO_DRAGDROP
// If outside client area, return (unless we are drag n dropping)
if (m_MouseMode != MOUSE_DRAGGING && !rect.PtInRect(point))
return;
#endif
// Sometimes a MOUSEMOVE message can come after the left buttons
// has been let go, but before the BUTTONUP message hs been processed.
// We'll keep track of mouse buttons manually to avoid this.
// All bMouseButtonDown's have been replaced with the member m_bLMouseButtonDown
// BOOL bMouseButtonDown = ((nFlags & MK_LBUTTON) == MK_LBUTTON);
// If the left mouse button is up, then test to see if row/column sizing is imminent
if (!m_bLMouseButtonDown ||
(m_bLMouseButtonDown && m_MouseMode == MOUSE_NOTHING))
{
if (m_bAllowColumnResize && MouseOverColumnResizeArea(point))
{
if (m_MouseMode != MOUSE_OVER_COL_DIVIDE)
{
#ifndef _WIN32_WCE_NO_CURSOR
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
#endif
m_MouseMode = MOUSE_OVER_COL_DIVIDE;
}
}
else if (m_bAllowRowResize && MouseOverRowResizeArea(point))
{
if (m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
{
#ifndef _WIN32_WCE_NO_CURSOR
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
#endif
m_MouseMode = MOUSE_OVER_ROW_DIVIDE;
}
}
else if (m_MouseMode != MOUSE_NOTHING)
{
#ifndef _WIN32_WCE_NO_CURSOR
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
#endif
m_MouseMode = MOUSE_NOTHING;
}
if (m_MouseMode == MOUSE_NOTHING)
{
CGridCellBase* pCell = NULL;
CCellID idCurrentCell;
if (!GetVirtualMode() || m_bTitleTips)
{
// Let the cell know that a big fat cursor is currently hovering
// over it.
idCurrentCell = GetCellFromPt(point);
pCell = GetCell(idCurrentCell.row, idCurrentCell.col);
if (pCell)
pCell->OnMouseOver();
}
#ifndef GRIDCONTROL_NO_TITLETIPS
// Titletips anyone? anyone?
if (m_bTitleTips)
{
CRect TextRect, CellRect;
if (pCell)
{
LPCTSTR szTipText = pCell->GetTipText();
if (!m_bRMouseButtonDown
&& szTipText && szTipText[0]
&& !pCell->IsEditing()
&& GetCellRect( idCurrentCell.row, idCurrentCell.col, &TextRect)
&& pCell->GetTipTextRect( &TextRect)
&& GetCellRect(idCurrentCell.row, idCurrentCell.col, CellRect) )
{
// TRACE0("Showing TitleTip\n");
m_TitleTip.Show(TextRect, pCell->GetTipText(), 0, CellRect,
pCell->GetFont(), GetTitleTipTextClr(), GetTitleTipBackClr());
}
}
}
#endif
}
m_LastMousePoint = point;
return;
}
if (!IsValid(m_LeftClickDownCell))
{
m_LastMousePoint = point;
return;
}
// If the left mouse button is down, then process appropriately
if (m_bLMouseButtonDown)
{
switch (m_MouseMode)
{
case MOUSE_SELECT_ALL:
break;
case MOUSE_SELECT_COL:
case MOUSE_SELECT_ROW:
case MOUSE_SELECT_CELLS:
{
CCellID idCurrentCell = GetCellFromPt(point);
if (!IsValid(idCurrentCell))
return;
if (idCurrentCell != GetFocusCell())
{
OnSelecting(idCurrentCell);
// EFW - BUG FIX - Keep the appropriate cell row and/or
// column focused. A fix in SetFocusCell() will place
// the cursor in a non-fixed cell as needed.
if((idCurrentCell.row >= m_nFixedRows &&
idCurrentCell.col >= m_nFixedCols) ||
m_MouseMode==MOUSE_SELECT_COL ||
m_MouseMode==MOUSE_SELECT_ROW)
{
SetFocusCell(idCurrentCell);
}
}
break;
}
case MOUSE_SIZING_COL:
{
CDC* pDC = GetDC();
if (!pDC)
break;
CRect oldInvertedRect(m_LastMousePoint.x, rect.top,
m_LastMousePoint.x + 2, rect.bottom);
pDC->InvertRect(&oldInvertedRect);
CRect newInvertedRect(point.x, rect.top,
point.x + 2, rect.bottom);
pDC->InvertRect(&newInvertedRect);
ReleaseDC(pDC);
}
break;
case MOUSE_SIZING_ROW:
{
CDC* pDC = GetDC();
if (!pDC)
break;
CRect oldInvertedRect(rect.left, m_LastMousePoint.y,
rect.right, m_LastMousePoint.y + 2);
pDC->InvertRect(&oldInvertedRect);
CRect newInvertedRect(rect.left, point.y,
rect.right, point.y + 2);
pDC->InvertRect(&newInvertedRect);
ReleaseDC(pDC);
}
break;
#ifndef GRIDCONTROL_NO_DRAGDROP
case MOUSE_PREPARE_EDIT:
case MOUSE_PREPARE_DRAG:
m_MouseMode = MOUSE_PREPARE_DRAG;
OnBeginDrag();
break;
#endif
}
}
m_LastMousePoint = point;
}
// Returns the point inside the cell that was clicked (coords relative to cell top left)
CPoint CGridCtrl::GetPointClicked(int nRow, int nCol, const CPoint& point)
{
CPoint PointCellOrigin;
if( !GetCellOrigin( nRow, nCol, &PointCellOrigin) )
return CPoint( 0, 0);
CPoint PointClickedCellRelative( point);
PointClickedCellRelative -= PointCellOrigin;
return PointClickedCellRelative;
}
void CGridCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TRACE0("CGridCtrl::OnLButtonDblClk\n");
CCellID cell = GetCellFromPt(point);
if( !IsValid( cell) )
{
//ASSERT(FALSE);
return;
}
#ifdef _WIN32_WCE
if (MouseOverColumnResizeArea(point))
#else
if (m_MouseMode == MOUSE_OVER_COL_DIVIDE)
#endif
{
CPoint start;
if (!GetCellOrigin(0, cell.col, &start))
return;
if (point.x - start.x < m_nResizeCaptureRange) // Clicked right of border
cell.col--;
// ignore columns that are hidden and look left towards first visible column
BOOL bFoundVisible = FALSE;
while( cell.col >= 0)
{
if( GetColumnWidth( cell.col) > 0)
{
bFoundVisible = TRUE;
break;
}
cell.col--;
}
if( !bFoundVisible)
return;
AutoSizeColumn(cell.col, GetAutoSizeStyle());
Invalidate();
}
#ifdef _WIN32_WCE
else if (MouseOverRowResizeArea(point))
#else
else if (m_MouseMode == MOUSE_OVER_ROW_DIVIDE)
#endif
{
CPoint start;
if (!GetCellOrigin(0, cell.col, &start))
return;
if (point.y - start.y < m_nResizeCaptureRange) // Clicked below border
cell.row--;
// ignore rows that are hidden and look up towards first visible row
BOOL bFoundVisible = FALSE;
while( cell.row >= 0)
{
if( GetRowHeight( cell.row) > 0)
{
bFoundVisible = TRUE;
break;
}
cell.row--;
}
if( !bFoundVisible)
return;
AutoSizeRow(cell.row);
Invalidate();
}
else if (m_MouseMode == MOUSE_NOTHING)
{
CPoint pointClickedRel;
pointClickedRel = GetPointClicked( cell.row, cell.col, point);
CGridCellBase* pCell = NULL;
if (IsValid(cell))
pCell = GetCell(cell.row, cell.col);
// Clicked in the text area? Only then will cell selection work
BOOL bInTextArea = FALSE;
if (pCell)
{
CRect rectCell;
if (GetCellRect(cell.row, cell.col, rectCell) && pCell->GetTextRect(rectCell))
bInTextArea = rectCell.PtInRect(point);
}
if (cell.row >= m_nFixedRows && IsValid(m_LeftClickDownCell) &&
cell.col >= m_nFixedCols && bInTextArea)
{
OnEditCell(cell.row, cell.col, pointClickedRel, VK_LBUTTON);
}
else if (m_bListMode)
{
if (!IsValid(cell))
return;
if (cell.row >= m_nFixedRows && cell.col >= m_nFixedCols && bInTextArea)
OnEditCell(cell.row, cell.col, pointClickedRel, VK_LBUTTON);
}
if (IsValid(cell))
{
CGridCellBase* pCell = GetCell(cell.row, cell.col);
if (pCell)
pCell->OnDblClick(pointClickedRel);
SendMessageToParent(cell.row, cell.col, NM_DBLCLK);
}
}
CWnd::OnLButtonDblClk(nFlags, point);
}
void CGridCtrl::OnRButtonDblClk(UINT nFlags, CPoint point)
{
CCellID cell = GetCellFromPt(point);
if (!IsValid(cell))
{
//ASSERT(FALSE);
return;
}
if (m_MouseMode == MOUSE_NOTHING)
{
SendMessageToParent(cell.row, cell.col, NM_RDBLCLK);
}
CWnd::OnRButtonDblClk(nFlags, point);
}
void CGridCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
#ifdef GRIDCONTROL_USE_TITLETIPS
// EFW - Bug Fix
m_TitleTip.Hide(); // hide any titletips
#endif
// TRACE0("CGridCtrl::OnLButtonDown\n");
// CWnd::OnLButtonDown(nFlags, point);
SetFocus();
m_CurCol = -1;
m_bLMouseButtonDown = TRUE;
m_LeftClickDownPoint = point;
m_LeftClickDownCell = GetCellFromPt(point);
if (!IsValid(m_LeftClickDownCell))
return;
m_CurRow = m_LeftClickDownCell.row;
// If the SHIFT key is not down, then the start of the selection area should be the
// cell just clicked. Otherwise, keep the previous selection-start-cell so the user
// can add to their previous cell selections in an intuitive way. If no selection-
// start-cell has been specified, then set it's value here and now.
if ((nFlags & MK_SHIFT) != MK_SHIFT)
m_SelectionStartCell = m_LeftClickDownCell;
else
{
if (!IsValid(m_SelectionStartCell))
m_SelectionStartCell = m_idCurrentCell;
}
EndEditing();
// tell the cell about it
CGridCellBase* pCell = GetCell(m_LeftClickDownCell.row, m_LeftClickDownCell.col);
if (pCell)
pCell->OnClickDown(GetPointClicked( m_LeftClickDownCell.row, m_LeftClickDownCell.col, point));
// Clicked in the text area? Only then will cell selection work
BOOL bInTextArea = FALSE;
if (pCell)
{
CRect rectCell;
if (GetCellRect(m_LeftClickDownCell.row, m_LeftClickDownCell.col, rectCell) &&
pCell->GetTextRect(rectCell))
{
bInTextArea = rectCell.PtInRect(point);
}
}
// If the user clicks on the current cell, then prepare to edit it.
// (If the user moves the mouse, then dragging occurs)
if (m_LeftClickDownCell == m_idCurrentCell &&
!(nFlags & MK_CONTROL) && bInTextArea &&
IsCellEditable(m_LeftClickDownCell))
{
m_MouseMode = MOUSE_PREPARE_EDIT;
return;
}
// If the user clicks on a selected cell, then prepare to drag it.
// (If the user moves the mouse, then dragging occurs)
else if (IsCellSelected(m_LeftClickDownCell))
{
SetFocusCell(m_LeftClickDownCell.row, m_LeftClickDownCell.col);
// If control is pressed then unselect the cell or row (depending on the list mode)
if (nFlags & MK_CONTROL)
{
SetFocusCell(m_LeftClickDownCell);
if (GetListMode())
SelectRows(m_LeftClickDownCell, TRUE, FALSE);
else
SelectCells(m_LeftClickDownCell, TRUE, FALSE);
return;
}
#ifndef GRIDCONTROL_NO_DRAGDROP
else if (m_bAllowDragAndDrop)
m_MouseMode = MOUSE_PREPARE_DRAG;
#endif
}
else if (m_MouseMode != MOUSE_OVER_COL_DIVIDE &&
m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
{
if (m_LeftClickDownCell.row >= GetFixedRowCount() &&
m_LeftClickDownCell.col >= GetFixedColumnCount())
{
SetFocusCell(m_LeftClickDownCell.row, m_LeftClickDownCell.col);
}
else
SetFocusCell(-1, -1);
//Don't set focus on any cell if the user clicked on a fixed cell - David Pritchard
//if (GetRowCount() > GetFixedRowCount() &&
// GetColumnCount() > GetFixedColumnCount())
// SetFocusCell(max(m_LeftClickDownCell.row, m_nFixedRows),
// max(m_LeftClickDownCell.col, m_nFixedCols));
}
SetCapture();
if (m_MouseMode == MOUSE_NOTHING)
{
if (m_bAllowColumnResize && MouseOverColumnResizeArea(point))
{
if (m_MouseMode != MOUSE_OVER_COL_DIVIDE)
{
#ifndef _WIN32_WCE_NO_CURSOR
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE));
#endif
m_MouseMode = MOUSE_OVER_COL_DIVIDE;
}
}
else if (m_bAllowRowResize && MouseOverRowResizeArea(point))
{
if (m_MouseMode != MOUSE_OVER_ROW_DIVIDE)
{
#ifndef _WIN32_WCE_NO_CURSOR
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS));
#endif
m_MouseMode = MOUSE_OVER_ROW_DIVIDE;
}
}
// else if (m_MouseMode != MOUSE_NOTHING)
//{
// SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
// m_MouseMode = MOUSE_NOTHING;
//}
}
if (m_MouseMode == MOUSE_OVER_COL_DIVIDE) // sizing column
{
m_MouseMode = MOUSE_SIZING_COL;
// Kludge for if we are over the last column...
if (GetColumnWidth(GetColumnCount()-1) < m_nResizeCaptureRange)
{
CRect VisRect;
GetVisibleNonFixedCellRange(VisRect);
if (abs(point.x - VisRect.right) < m_nResizeCaptureRange)
m_LeftClickDownCell.col = GetColumnCount()-1;
}
CPoint start;
if (!GetCellOrigin(0, m_LeftClickDownCell.col, &start))
return;
if( !m_bHiddenColUnhide)
{
// ignore columns that are hidden and look left towards first visible column
BOOL bLookForVisible = TRUE;
BOOL bIsCellRightBorder = point.x - start.x >= m_nResizeCaptureRange;
if( bIsCellRightBorder
&& m_LeftClickDownCell.col + 1 >= GetColumnCount() )
{
// clicked on last column's right border
// if last column is visible, don't do anything
if( m_LeftClickDownCell.col >= 0)
bLookForVisible = FALSE;
}
if( bLookForVisible)
{
// clicked on column divider other than last right border
BOOL bFoundVisible = FALSE;
int iOffset = 1;
if( bIsCellRightBorder)
iOffset = 0;
while( m_LeftClickDownCell.col - iOffset >= 0)
{
if( GetColumnWidth( m_LeftClickDownCell.col - iOffset) > 0)
{
bFoundVisible = TRUE;
break;
}
m_LeftClickDownCell.col--;
}
if( !bFoundVisible)
return;
}
}
CRect rect;
GetClientRect(rect);
CRect invertedRect(point.x, rect.top, point.x + 2, rect.bottom);
CDC* pDC = GetDC();
if (pDC)
{
pDC->InvertRect(&invertedRect);
ReleaseDC(pDC);
}
// If we clicked to the right of the colimn divide, then reset the click-down cell
// as the cell to the left of the column divide - UNLESS we clicked on the last column
// and the last column is teensy (kludge fix)
if (point.x - start.x < m_nResizeCaptureRange)
{
if (m_LeftClickDownCell.col < GetColumnCount()-1 ||
GetColumnWidth(GetColumnCount()-1) >= m_nResizeCaptureRange)
{
if (!GetCellOrigin(0, --m_LeftClickDownCell.col, &start))
return;
}
}
// Allow a cell resize width no greater than that which can be viewed within
// the grid itself
int nMaxCellWidth = rect.Width()-GetFixedColumnWidth();
rect.left = start.x + 1;
rect.right = rect.left + nMaxCellWidth;
ClientToScreen(rect);
#ifndef _WIN32_WCE_NO_CURSOR
ClipCursor(rect);
#endif
}
else if (m_MouseMode == MOUSE_OVER_ROW_DIVIDE) // sizing row
{
m_MouseMode = MOUSE_SIZING_ROW;
// Kludge for if we are over the last column...
if (GetRowHeight(GetRowCount()-1) < m_nResizeCaptureRange)
{
CRect VisRect;
GetVisibleNonFixedCellRange(VisRect);
if (abs(point.y - VisRect.bottom) < m_nResizeCaptureRange)
m_LeftClickDownCell.row = GetRowCount()-1;
}
CPoint start;
if (!GetCellOrigin(m_LeftClickDownCell, &start))
return;
if( !m_bHiddenRowUnhide)
{
// ignore rows that are hidden and look up towards first visible row
BOOL bLookForVisible = TRUE;
BOOL bIsCellBottomBorder = point.y - start.y >= m_nResizeCaptureRange;
if( bIsCellBottomBorder
&& m_LeftClickDownCell.row + 1 >= GetRowCount() )
{
// clicked on last row's bottom border
// if last row is visible, don't do anything
if( m_LeftClickDownCell.row >= 0)
bLookForVisible = FALSE;
}
if( bLookForVisible)
{
// clicked on row divider other than last bottom border
BOOL bFoundVisible = FALSE;
int iOffset = 1;
if( bIsCellBottomBorder)
iOffset = 0;
while( m_LeftClickDownCell.row - iOffset >= 0)
{
if( GetRowHeight( m_LeftClickDownCell.row - iOffset) > 0)
{
bFoundVisible = TRUE;
break;
}
m_LeftClickDownCell.row--;
}
if( !bFoundVisible)
return;
}
}
CRect rect;
GetClientRect(rect);
CRect invertedRect(rect.left, point.y, rect.right, point.y + 2);
CDC* pDC = GetDC();
if (pDC)
{
pDC->InvertRect(&invertedRect);
ReleaseDC(pDC);
}
// If we clicked below the row divide, then reset the click-down cell
// as the cell above the row divide - UNLESS we clicked on the last row
// and the last row is teensy (kludge fix)
if (point.y - start.y < m_nResizeCaptureRange) // clicked below border
{
if (m_LeftClickDownCell.row < GetRowCount()-1 ||
GetRowHeight(GetRowCount()-1) >= m_nResizeCaptureRange)
{
if (!GetCellOrigin(--m_LeftClickDownCell.row, 0, &start))
return;
}
}
int nMaxCellHeight = rect.Height()-GetFixedRowHeight();
rect.top = start.y + 1;
rect.bottom = rect.top + nMaxCellHeight;
ClientToScreen(rect);
#ifndef _WIN32_WCE_NO_CURSOR
ClipCursor(rect);
#endif
}
else
#ifndef GRIDCONTROL_NO_DRAGDROP
if (m_MouseMode != MOUSE_PREPARE_DRAG) // not sizing or editing -- selecting
#endif
{
SendMessageToParent(m_LeftClickDownCell.row, m_LeftClickDownCell.col, GVN_SELCHANGING);
// If Ctrl pressed, save the current cell selection. This will get added
// to the new cell selection at the end of the cell selection process
m_PrevSelectedCellMap.RemoveAll();
if (nFlags & MK_CONTROL)
{
for (POSITION pos = m_SelectedCellMap.GetStartPosition(); pos != NULL; )
{
DWORD key;
CCellID cell;
m_SelectedCellMap.GetNextAssoc(pos, key, (CCellID&)cell);
m_PrevSelectedCellMap.SetAt(key, cell);
}
}
if (m_LeftClickDownCell.row < GetFixedRowCount())
{
OnFixedRowClick(m_LeftClickDownCell);
#ifndef GRIDCONTROL_NO_DRAGDROP
if(m_AllowReorderColumn && m_LeftClickDownCell.col >= GetFixedColumnCount())
{
ResetSelectedRange(); // TODO : This is not the better solution, as we do not see why clicking in column header should reset selection
//but the state of selection is instable after drag (at least until someone debugs it), so better clear it allways.
m_MouseMode = MOUSE_PREPARE_DRAG;
m_CurCol = m_LeftClickDownCell.col;
}
#endif
}
else if (m_LeftClickDownCell.col < GetFixedColumnCount())
OnFixedColumnClick(m_LeftClickDownCell);
else
{
m_MouseMode = m_bListMode? MOUSE_SELECT_ROW : MOUSE_SELECT_CELLS;
OnSelecting(m_LeftClickDownCell);
m_nTimerID = SetTimer(WM_LBUTTONDOWN, m_nTimerInterval, 0);
}
}
m_LastMousePoint = point;
}
void CGridCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
// TRACE0("CGridCtrl::OnLButtonUp\n");
CWnd::OnLButtonUp(nFlags, point);
m_bLMouseButtonDown = FALSE;
#ifndef _WIN32_WCE_NO_CURSOR
ClipCursor(NULL);
#endif
if (GetCapture()->GetSafeHwnd() == GetSafeHwnd())
{
ReleaseCapture();
KillTimer(m_nTimerID);
m_nTimerID = 0;
}
CPoint pointClickedRel;
pointClickedRel = GetPointClicked( m_idCurrentCell.row, m_idCurrentCell.col, point);
// m_MouseMode == MOUSE_PREPARE_EDIT only if user clicked down on current cell
// and then didn't move mouse before clicking up (releasing button)
if (m_MouseMode == MOUSE_PREPARE_EDIT)
{
OnEditCell(m_idCurrentCell.row, m_idCurrentCell.col, pointClickedRel, VK_LBUTTON);
}
#ifndef GRIDCONTROL_NO_DRAGDROP
// m_MouseMode == MOUSE_PREPARE_DRAG only if user clicked down on a selected cell
// and then didn't move mouse before clicking up (releasing button)
else if (m_MouseMode == MOUSE_PREPARE_DRAG)
{
CGridCellBase* pCell = GetCell(m_idCurrentCell.row, m_idCurrentCell.col);
if (pCell)
pCell->OnClick( GetPointClicked( m_idCurrentCell.row, m_idCurrentCell.col, point) );
SendMessageToParent(m_LeftClickDownCell.row, m_LeftClickDownCell.col, NM_CLICK);
SendMessageToParent(m_LeftClickDownCell.row, m_LeftClickDownCell.col, GVN_SELCHANGING);
ResetSelectedRange();
SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_SELCHANGED);
}
#endif
else if (m_MouseMode == MOUSE_SIZING_COL)
{
CRect rect;
GetClientRect(rect);
CRect invertedRect(m_LastMousePoint.x, rect.top, m_LastMousePoint.x + 2, rect.bottom);
CDC* pDC = GetDC();
if (pDC)
{
pDC->InvertRect(&invertedRect);
ReleaseDC(pDC);
}
if (m_LeftClickDownPoint != point && (point.x != 0 || point.y != 0)) // 0 pt fix by [email protected]
{
CPoint start;
if (!GetCellOrigin(m_LeftClickDownCell, &start))
return;
int nColumnWidth = __max(point.x - start.x, m_bAllowColHide? 0 : 1);
SetColumnWidth(m_LeftClickDownCell.col, nColumnWidth);
ResetScrollBars();
Invalidate();
}
}
else if (m_MouseMode == MOUSE_SIZING_ROW)
{
CRect rect;
GetClientRect(rect);
CRect invertedRect(rect.left, m_LastMousePoint.y, rect.right, m_LastMousePoint.y + 2);
CDC* pDC = GetDC();
if (pDC)
{
pDC->InvertRect(&invertedRect);
ReleaseDC(pDC);
}
if (m_LeftClickDownPoint != point && (point.x != 0 || point.y != 0)) // 0 pt fix by [email protected]
{
CPoint start;
if (!GetCellOrigin(m_LeftClickDownCell, &start))
return;
int nRowHeight = __max(point.y - start.y, m_bAllowRowHide? 0 : 1);
SetRowHeight(m_LeftClickDownCell.row, nRowHeight);
ResetScrollBars();
Invalidate();
}
}
else
{
SendMessageToParent(m_idCurrentCell.row, m_idCurrentCell.col, GVN_SELCHANGED);
CGridCellBase* pCell = GetCell(m_idCurrentCell.row, m_idCurrentCell.col);
if (pCell)
pCell->OnClick( GetPointClicked( m_idCurrentCell.row, m_idCurrentCell.col, point) );
SendMessageToParent(m_LeftClickDownCell.row, m_LeftClickDownCell.col, NM_CLICK);
}
m_MouseMode = MOUSE_NOTHING;
#ifndef _WIN32_WCE_NO_CURSOR
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
#endif
if (!IsValid(m_LeftClickDownCell))
return;
CWnd *pOwner = GetOwner();
if (pOwner && IsWindow(pOwner->m_hWnd))
pOwner->PostMessage(WM_COMMAND, MAKELONG(GetDlgCtrlID(), BN_CLICKED),
(LPARAM) GetSafeHwnd());
}
#ifndef _WIN32_WCE
void CGridCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnRButtonDown(nFlags, point);
m_bRMouseButtonDown = TRUE;
#ifdef GRIDCONTROL_USE_TITLETIPS
TRACE0("Hiding TitleTip\n");
m_TitleTip.Hide(); // hide any titletips
#endif
}
// EFW - Added to forward right click to parent so that a context
// menu can be shown without deriving a new grid class.
void CGridCtrl::OnRButtonUp(UINT nFlags, CPoint point)
{
CWnd::OnRButtonUp(nFlags, point);
m_bRMouseButtonDown = FALSE;
CCellID FocusCell;
FocusCell = GetCellFromPt(point);
EndEditing(); // Auto-destroy any InPlaceEdit's
// If not a valid cell, pass -1 for row and column
if(!IsValid(FocusCell))
SendMessageToParent(-1, -1, NM_RCLICK);
else
{
SetFocusCell(-1,-1);
SetFocusCell(__max(FocusCell.row, m_nFixedRows),
__max(FocusCell.col, m_nFixedCols));
// tell the cell about it
CGridCellBase* pCell = GetCell(FocusCell.row, FocusCell.col);
if (pCell)
pCell->OnRClick( GetPointClicked( FocusCell.row, FocusCell.col, point) );
SendMessageToParent(FocusCell.row, FocusCell.col, NM_RCLICK);
}
}
#endif
#if !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl printing
// EFW - New print margin support functions
void CGridCtrl::SetPrintMarginInfo(int nHeaderHeight, int nFooterHeight,
int nLeftMargin, int nRightMargin, int nTopMargin,
int nBottomMargin, int nGap)
{
// If any parameter is -1, keep the existing setting
if(nHeaderHeight > -1)
m_nHeaderHeight = nHeaderHeight;
if(nFooterHeight > -1)
m_nFooterHeight = nFooterHeight;
if(nLeftMargin > -1)
m_nLeftMargin = nLeftMargin;
if(nRightMargin > -1)
m_nRightMargin = nRightMargin;
if(nTopMargin > -1)
m_nTopMargin = nTopMargin;
if(nBottomMargin > -1)
m_nBottomMargin = nBottomMargin;
if(nGap > -1)
m_nGap = nGap;
}
void CGridCtrl::GetPrintMarginInfo(int &nHeaderHeight, int &nFooterHeight,
int &nLeftMargin, int &nRightMargin, int &nTopMargin,
int &nBottomMargin, int &nGap)
{
nHeaderHeight = m_nHeaderHeight;
nFooterHeight = m_nFooterHeight;
nLeftMargin = m_nLeftMargin;
nRightMargin = m_nRightMargin;
nTopMargin = m_nTopMargin;
nBottomMargin = m_nBottomMargin;
nGap = m_nGap;
}
void CGridCtrl::Print(CPrintDialog* pPrntDialog /*=NULL*/)
{
CDC dc;
if (pPrntDialog == NULL)
{
CPrintDialog printDlg(FALSE);
if (printDlg.DoModal() != IDOK) // Get printer settings from user
return;
dc.Attach(printDlg.GetPrinterDC()); // attach a printer DC
}
else
dc.Attach(pPrntDialog->GetPrinterDC()); // attach a printer DC
dc.m_bPrinting = TRUE;
CString strTitle;
strTitle.LoadString(AFX_IDS_APP_TITLE);
if( strTitle.IsEmpty() )
{
CWnd *pParentWnd = GetParent();
while (pParentWnd)
{
pParentWnd->GetWindowText(strTitle);
if (strTitle.GetLength()) // can happen if it is a CView, CChildFrm has the title
break;
pParentWnd = pParentWnd->GetParent();
}
}
DOCINFO di; // Initialise print doc details
memset(&di, 0, sizeof (DOCINFO));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = strTitle;
BOOL bPrintingOK = dc.StartDoc(&di); // Begin a new print job
CPrintInfo Info;
Info.m_rectDraw.SetRect(0,0, dc.GetDeviceCaps(HORZRES), dc.GetDeviceCaps(VERTRES));
OnBeginPrinting(&dc, &Info); // Initialise printing
for (UINT page = Info.GetMinPage(); page <= Info.GetMaxPage() && bPrintingOK; page++)
{
dc.StartPage(); // begin new page
Info.m_nCurPage = page;
OnPrint(&dc, &Info); // Print page
bPrintingOK = (dc.EndPage() > 0); // end page
}
OnEndPrinting(&dc, &Info); // Clean up after printing
if (bPrintingOK)
dc.EndDoc(); // end a print job
else
dc.AbortDoc(); // abort job.
dc.Detach(); // detach the printer DC
}
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl printing overridables - for Doc/View print/print preview framework
// EFW - Various changes in the next few functions to support the
// new print margins and a few other adjustments.
void CGridCtrl::OnBeginPrinting(CDC *pDC, CPrintInfo *pInfo)
{
// OnBeginPrinting() is called after the user has committed to
// printing by OK'ing the Print dialog, and after the framework
// has created a CDC object for the printer or the preview view.
// This is the right opportunity to set up the page range.
// Given the CDC object, we can determine how many rows will
// fit on a page, so we can in turn determine how many printed
// pages represent the entire document.
ASSERT(pDC && pInfo);
if (!pDC || !pInfo) return;
// Get a DC for the current window (will be a screen DC for print previewing)
CDC *pCurrentDC = GetDC(); // will have dimensions of the client area
if (!pCurrentDC) return;
CSize PaperPixelsPerInch(pDC->GetDeviceCaps(LOGPIXELSX), pDC->GetDeviceCaps(LOGPIXELSY));
CSize ScreenPixelsPerInch(pCurrentDC->GetDeviceCaps(LOGPIXELSX), pCurrentDC->GetDeviceCaps(LOGPIXELSY));
// Create the printer font
int nFontSize = -10;
CString strFontName = _T("Arial");
m_PrinterFont.CreateFont(nFontSize, 0,0,0, FW_NORMAL, 0,0,0, DEFAULT_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, strFontName);
CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
// Get the average character width (in GridCtrl units) and hence the margins
m_CharSize = pDC->GetTextExtent(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSATUVWXYZ"),52);
m_CharSize.cx /= 52;
int nMargins = (m_nLeftMargin+m_nRightMargin)*m_CharSize.cx;
// Get the page sizes (physical and logical)
m_PaperSize = CSize(pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
if( m_bWysiwygPrinting)
{
m_LogicalPageSize.cx = ScreenPixelsPerInch.cx * m_PaperSize.cx / PaperPixelsPerInch.cx * 3 / 4;
m_LogicalPageSize.cy = ScreenPixelsPerInch.cy * m_PaperSize.cy / PaperPixelsPerInch.cy * 3 / 4;
}
else
{
m_PaperSize = CSize(pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
m_LogicalPageSize.cx = GetVirtualWidth()+nMargins;
#ifdef _WIN32_WCE
m_LogicalPageSize.cy = (m_LogicalPageSize.cx * m_PaperSize.cy) / m_PaperSize.cx;
#else
m_LogicalPageSize.cy = MulDiv(m_LogicalPageSize.cx, m_PaperSize.cy, m_PaperSize.cx);
#endif
}
m_nPageHeight = m_LogicalPageSize.cy - GetFixedRowHeight()
- (m_nHeaderHeight+m_nFooterHeight + 2*m_nGap)*m_CharSize.cy;
// Get the number of pages. Assumes no row is bigger than the page size.
int nTotalRowHeight = 0;
m_nNumPages = 1;
for (int row = GetFixedRowCount(); row < GetRowCount(); row++)
{
nTotalRowHeight += GetRowHeight(row);
if (nTotalRowHeight > m_nPageHeight) {
m_nNumPages++;
nTotalRowHeight = GetRowHeight(row);
}
}
// now, figure out how many additional pages must print out if rows ARE bigger
// than page size
int iColumnOffset = 0;
int i1;
for( i1=0; i1 < GetFixedColumnCount(); i1++)
{
iColumnOffset += GetColumnWidth( i1);
}
m_nPageWidth = m_LogicalPageSize.cx - iColumnOffset
- nMargins;
m_nPageMultiplier = 1;
if( m_bWysiwygPrinting)
{
int iTotalRowWidth = 0;
for( i1 = GetFixedColumnCount(); i1 < GetColumnCount(); i1++)
{
iTotalRowWidth += GetColumnWidth( i1);
if( iTotalRowWidth > m_nPageWidth)
{
m_nPageMultiplier++;
iTotalRowWidth = GetColumnWidth( i1);
}
}
m_nNumPages *= m_nPageMultiplier;
}
// Set up the print info
pInfo->SetMaxPage(m_nNumPages);
pInfo->m_nCurPage = 1; // start printing at page# 1
ReleaseDC(pCurrentDC);
pDC->SelectObject(pOldFont);
}
void CGridCtrl::OnPrint(CDC *pDC, CPrintInfo *pInfo)
{
if (!pDC || !pInfo)
return;
//CRect rcPage(pInfo->m_rectDraw);
CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
// Set the page map mode to use GridCtrl units, and setup margin
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(m_LogicalPageSize);
pDC->SetViewportExt(m_PaperSize);
pDC->SetWindowOrg(-m_nLeftMargin * m_CharSize.cx, 0);
// Header
pInfo->m_rectDraw.top = 0;
pInfo->m_rectDraw.left = 0;
pInfo->m_rectDraw.right = m_LogicalPageSize.cx - (m_nLeftMargin + m_nRightMargin) * m_CharSize.cx;
pInfo->m_rectDraw.bottom = m_nHeaderHeight * m_CharSize.cy;
PrintHeader(pDC, pInfo);
pDC->OffsetWindowOrg(0, -m_nHeaderHeight * m_CharSize.cy);
// Gap between header and column headings
pDC->OffsetWindowOrg(0, -m_nGap * m_CharSize.cy);
pDC->OffsetWindowOrg(0, -GetFixedRowHeight());
// We need to find out which row to start printing for this page.
int nTotalRowHeight = 0;
UINT nNumPages = 1;
m_nCurrPrintRow = GetFixedRowCount();
// Not only the row, but we need to figure out column, too
// Can print 4 pages, where page 1 and 2 represent the same rows but
// with different WIDE columns.
//
// .......
// .1 .2 . If representing page 3 --> iPageIfIgnoredWideCols = 2
// ....... iWideColPageOffset = 0
// .3 .4 . If representing page 2 --> iPageIfIgnoredWideCols = 1
// ....... iWideColPageOffset = 1
int iPageIfIgnoredWideCols = pInfo->m_nCurPage / m_nPageMultiplier;
int iWideColPageOffset = pInfo->m_nCurPage - ( iPageIfIgnoredWideCols * m_nPageMultiplier);
if( iWideColPageOffset > 0)
iPageIfIgnoredWideCols++;
if( iWideColPageOffset == 0)
iWideColPageOffset = m_nPageMultiplier;
iWideColPageOffset--;
// calculate current print row based on iPageIfIgnoredWideCols
while( m_nCurrPrintRow < GetRowCount()
&& (int)nNumPages < iPageIfIgnoredWideCols)
{
nTotalRowHeight += GetRowHeight(m_nCurrPrintRow);
if (nTotalRowHeight > m_nPageHeight) {
nNumPages++;
if ((int)nNumPages == iPageIfIgnoredWideCols) break;
nTotalRowHeight = GetRowHeight(m_nCurrPrintRow);
}
m_nCurrPrintRow++;
}
m_nPrintColumn = GetFixedColumnCount();
int iTotalRowWidth = 0;
int i1, i2;
// now, calculate which print column to start displaying
for( i1 = 0; i1 < iWideColPageOffset; i1++)
{
for( i2 = m_nPrintColumn; i2 < GetColumnCount(); i2++)
{
iTotalRowWidth += GetColumnWidth( i2);
if( iTotalRowWidth > m_nPageWidth)
{
m_nPrintColumn = i2;
iTotalRowWidth = 0;
break;
}
}
}
PrintRowButtons( pDC, pInfo); // print row buttons on each page
int iColumnOffset = 0;
for( i1=0; i1 < GetFixedColumnCount(); i1++)
{
iColumnOffset += GetColumnWidth( i1);
}
// Print the column headings
pInfo->m_rectDraw.bottom = GetFixedRowHeight();
if( m_nPrintColumn == GetFixedColumnCount())
{
// have the column headings fcn draw the upper left fixed cells
// for the very first columns, only
pDC->OffsetWindowOrg( 0, +GetFixedRowHeight());
m_nPageWidth += iColumnOffset;
m_nPrintColumn = 0;
PrintColumnHeadings(pDC, pInfo);
m_nPageWidth -= iColumnOffset;
m_nPrintColumn = GetFixedColumnCount();
pDC->OffsetWindowOrg( -iColumnOffset, -GetFixedRowHeight());
}
else
{
// changed all of this here to match above almost exactly same
pDC->OffsetWindowOrg( 0, +GetFixedRowHeight());
m_nPageWidth += iColumnOffset;
// print from column 0 ... last column that fits on the current page
PrintColumnHeadings(pDC, pInfo);
m_nPageWidth -= iColumnOffset;
pDC->OffsetWindowOrg( -iColumnOffset, -GetFixedRowHeight());
}
if (m_nCurrPrintRow >= GetRowCount()) return;
// Draw as many rows as will fit on the printed page.
// Clip the printed page so that there is no partially shown
// row at the bottom of the page (the same row which will be fully
// shown at the top of the next page).
BOOL bFirstPrintedRow = TRUE;
CRect rect;
rect.bottom = -1;
while (m_nCurrPrintRow < GetRowCount())
{
rect.top = rect.bottom+1;
rect.bottom = rect.top + GetRowHeight(m_nCurrPrintRow) - 1;
if (rect.bottom > m_nPageHeight) break; // Gone past end of page
rect.right = -1;
// modified to allow printing of wide grids on multiple pages
for (int col = m_nPrintColumn; col < GetColumnCount(); col++)
{
rect.left = rect.right+1;
rect.right = rect.left
+ GetColumnWidth( col)
- 1;
if( rect.right > m_nPageWidth)
break;
CGridCellBase* pCell = GetCell(m_nCurrPrintRow, col);
if (pCell)
pCell->PrintCell(pDC, m_nCurrPrintRow, col, rect);
if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
{
int Overlap = (col == 0)? 0:1;
pDC->MoveTo(rect.left-Overlap, rect.bottom);
pDC->LineTo(rect.right, rect.bottom);
if (m_nCurrPrintRow == 0) {
pDC->MoveTo(rect.left-Overlap, rect.top);
pDC->LineTo(rect.right, rect.top);
}
}
if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
{
int Overlap = (bFirstPrintedRow)? 0:1;
pDC->MoveTo(rect.right, rect.top-Overlap);
pDC->LineTo(rect.right, rect.bottom);
if (col == 0) {
pDC->MoveTo(rect.left, rect.top-Overlap);
pDC->LineTo(rect.left, rect.bottom);
}
}
}
m_nCurrPrintRow++;
bFirstPrintedRow = FALSE;
}
// Footer
pInfo->m_rectDraw.bottom = m_nFooterHeight * m_CharSize.cy;
pDC->SetWindowOrg( -m_nLeftMargin * m_CharSize.cx,
-m_LogicalPageSize.cy + m_nFooterHeight * m_CharSize.cy);
PrintFooter(pDC, pInfo);
// SetWindowOrg back for next page
pDC->SetWindowOrg(0,0);
pDC->SelectObject(pOldFont);
}
// added by M.Fletcher 12/17/00
void CGridCtrl::PrintFixedRowCells(int nStartColumn, int nStopColumn, int& row, CRect& rect,
CDC *pDC, BOOL& bFirst)
{
// print all cells from nStartColumn to nStopColumn on row
for (int col =nStartColumn; col < nStopColumn; col++)
{
rect.left = rect.right+1;
rect.right = rect.left + GetColumnWidth( col) - 1;
if( rect.right > m_nPageWidth)
break;
CGridCellBase* pCell = GetCell(row, col);
if (pCell)
pCell->PrintCell(pDC, row, col, rect);
if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
{
int Overlap = (col == 0)? 0:1;
pDC->MoveTo(rect.left-Overlap, rect.bottom);
pDC->LineTo(rect.right, rect.bottom);
if (row == 0)
{
pDC->MoveTo(rect.left-Overlap, rect.top);
pDC->LineTo(rect.right, rect.top);
}
}
if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
{
int Overlap = (row == 0)? 0:1;
pDC->MoveTo(rect.right, rect.top-Overlap);
pDC->LineTo(rect.right, rect.bottom);
if( bFirst)
{
pDC->MoveTo(rect.left-1, rect.top-Overlap);
pDC->LineTo(rect.left-1, rect.bottom);
bFirst = FALSE;
}
}
} // end of column cells loop
} // end of CGridCtrl::PrintFixedRowCells
void CGridCtrl::PrintColumnHeadings(CDC *pDC, CPrintInfo* /*pInfo*/)
{
CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
CRect rect;
rect.bottom = -1;
BOOL bFirst = TRUE;
BOOL bOriginal;
// modified to allow column hdr printing of multi-page wide grids
for (int row = 0; row < GetFixedRowCount(); row++)
{
rect.top = rect.bottom+1;
rect.bottom = rect.top + GetRowHeight(row) - 1;
rect.right = -1;
// if printColumn > fixedcolumncount we are on page 2 or more
// lets printout those fixed cell headings again the 1 or more that would be missed
// added by M.Fletcher 12/17/00
if(m_nPrintColumn>= GetFixedColumnCount())
{
bOriginal=bFirst;
// lets print the missing fixed cells on left first out to last fixed column
PrintFixedRowCells(0,GetFixedColumnCount(), row, rect, pDC, bFirst);
bFirst=bOriginal;
}
// now back to normal business print cells in heading after all fixed columns
PrintFixedRowCells(m_nPrintColumn, GetColumnCount(), row, rect, pDC, bFirst);
} // end of Row Loop
pDC->SelectObject(pOldFont);
} // end of CGridCtrl::PrintColumnHeadings
/*****************************************************************************
Prints line of row buttons on each page of the printout. Assumes that
the window origin is setup before calling
*****************************************************************************/
void CGridCtrl::PrintRowButtons(CDC *pDC, CPrintInfo* /*pInfo*/)
{
CFont *pOldFont = pDC->SelectObject(&m_PrinterFont);
CRect rect;
rect.right = -1;
BOOL bFirst = TRUE;
for( int iCol = 0; iCol < GetFixedColumnCount(); iCol++)
{
rect.left = rect.right+1;
rect.right = rect.left
+ GetColumnWidth( iCol)
- 1;
rect.bottom = -1;
for( int iRow = m_nCurrPrintRow; iRow < GetRowCount(); iRow++)
{
rect.top = rect.bottom+1;
rect.bottom = rect.top + GetRowHeight( iRow) - 1;
if( rect.bottom > m_nPageHeight)
break;
CGridCellBase* pCell = GetCell(iRow, iCol);
if (pCell)
pCell->PrintCell(pDC, iRow, iCol, rect);
if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_HORZ)
{
int Overlap = (iCol == 0)? 0:1;
pDC->MoveTo(rect.left-Overlap, rect.bottom);
pDC->LineTo(rect.right, rect.bottom);
if( bFirst) {
pDC->MoveTo(rect.left-Overlap, rect.top-1);
pDC->LineTo(rect.right, rect.top-1);
bFirst = FALSE;
}
}
if (m_nGridLines == GVL_BOTH || m_nGridLines == GVL_VERT)
{
int Overlap = (iRow == 0)? 0:1;
pDC->MoveTo(rect.right, rect.top-Overlap);
pDC->LineTo(rect.right, rect.bottom);
if (iCol == 0) {
pDC->MoveTo(rect.left, rect.top-Overlap);
pDC->LineTo(rect.left, rect.bottom);
}
}
}
}
pDC->SelectObject(pOldFont);
}
void CGridCtrl::PrintHeader(CDC *pDC, CPrintInfo *pInfo)
{
// print App title on top right margin
CString strRight;
strRight.LoadString(AFX_IDS_APP_TITLE);
// print parent window title in the centre (Gert Rijs)
CString strCenter;
CWnd *pParentWnd = GetParent();
while (pParentWnd)
{
pParentWnd->GetWindowText(strCenter);
if (strCenter.GetLength()) // can happen if it is a CView, CChildFrm has the title
break;
pParentWnd = pParentWnd->GetParent();
}
CFont BoldFont;
LOGFONT lf;
//create bold font for header and footer
VERIFY(m_PrinterFont.GetLogFont(&lf));
lf.lfWeight = FW_BOLD;
VERIFY(BoldFont.CreateFontIndirect(&lf));
CFont *pNormalFont = pDC->SelectObject(&BoldFont);
int nPrevBkMode = pDC->SetBkMode(TRANSPARENT);
CRect rc(pInfo->m_rectDraw);
if( !strCenter.IsEmpty() )
pDC->DrawText( strCenter, &rc, DT_CENTER | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
if( !strRight.IsEmpty() )
pDC->DrawText( strRight, &rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
pDC->SetBkMode(nPrevBkMode);
pDC->SelectObject(pNormalFont);
BoldFont.DeleteObject();
// draw ruled-line across top
pDC->SelectStockObject(BLACK_PEN);
pDC->MoveTo(rc.left, rc.bottom);
pDC->LineTo(rc.right, rc.bottom);
}
//print footer with a line and date, and page number
void CGridCtrl::PrintFooter(CDC *pDC, CPrintInfo *pInfo)
{
// page numbering on left
CString strLeft;
strLeft.Format(_T("Page %d of %d"), pInfo->m_nCurPage, pInfo->GetMaxPage() );
// date and time on the right
CString strRight;
COleDateTime t = COleDateTime::GetCurrentTime();
strRight = t.Format(_T("%c"));
CRect rc(pInfo->m_rectDraw);
// draw ruled line on bottom
pDC->SelectStockObject(BLACK_PEN);
pDC->MoveTo(rc.left, rc.top);
pDC->LineTo(rc.right, rc.top);
CFont BoldFont;
LOGFONT lf;
//create bold font for header and footer
m_PrinterFont.GetLogFont(&lf);
lf.lfWeight = FW_BOLD;
BoldFont.CreateFontIndirect(&lf);
CFont *pNormalFont = pDC->SelectObject(&BoldFont);
int nPrevBkMode = pDC->SetBkMode(TRANSPARENT);
// EFW - Bug fix - Force text color to black. It doesn't always
// get set to a printable color when it gets here.
pDC->SetTextColor(RGB(0, 0, 0));
if( !strLeft.IsEmpty() )
pDC->DrawText( strLeft, &rc, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
if( !strRight.IsEmpty() )
pDC->DrawText( strRight, &rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
pDC->SetBkMode(nPrevBkMode);
pDC->SelectObject(pNormalFont);
BoldFont.DeleteObject();
}
void CGridCtrl::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
m_PrinterFont.DeleteObject();
}
#endif // !defined(_WIN32_WCE_NO_PRINTING) && !defined(GRIDCONTROL_NO_PRINTING)
#ifndef _WIN32_WCE
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl persistance
BOOL CGridCtrl::Save(LPCTSTR filename, TCHAR chSeparator/*=_T(',')*/)
{
CStdioFile File;
CFileException ex;
CString strSeparator(chSeparator);
if (!File.Open(filename, CFile::modeWrite | CFile::modeCreate| CFile::typeText, &ex))
{
ex.ReportError();
return FALSE;
}
TRY
{
int nNumColumns = GetColumnCount();
for (int i = 0; i < GetRowCount(); i++)
{
for (int j = 0; j < nNumColumns; j++)
{
File.WriteString(GetItemText(i,j));
File.WriteString((j==(nNumColumns-1))? _T("\n"): strSeparator);
}
}
File.Close();
}
CATCH (CFileException, e)
{
AfxMessageBox(_T("Unable to save grid list"));
return FALSE;
}
END_CATCH
return TRUE;
}
BOOL CGridCtrl::Load(LPCTSTR filename, TCHAR chSeparator/*=_T(',')*/)
{
if (GetVirtualMode())
return FALSE;
TCHAR *token, *end;
TCHAR buffer[1024];
CStdioFile File;
CFileException ex;
if (!File.Open(filename, CFile::modeRead | CFile::typeText))
{
ex.ReportError();
return FALSE;
}
DeleteAllItems();
TRY
{
// Read Header off file
File.ReadString(buffer, 1024);
// Get first token
for (token=buffer, end=buffer;
*end && (*end != chSeparator) && (*end != _T('\n'));
end++)
;
if ((*end == _T('\0')) && (token == end))
token = NULL;
*end = _T('\0');
while (token)
{
InsertColumn(token);
// Get next token
for (token=++end; *end && (*end != chSeparator) && (*end != _T('\n'));
end++)
;
if ((*end == _T('\0')) && (token == end))
token = NULL;
*end = _T('\0');
}
// Read in rest of data
int nItem = 1;
while (File.ReadString(buffer, 1024))
{
// Get first token
for (token=buffer, end=buffer;
*end && (*end != chSeparator) && (*end != _T('\n')); end++)
;
if ((*end == _T('\0')) && (token == end))
token = NULL;
*end = _T('\0');
int nSubItem = 0;
while (token)
{
if (!nSubItem)
InsertRow(token);
else
SetItemText(nItem, nSubItem, token);
// Get next token
for (token=++end; *end && (*end != chSeparator) && (*end != _T('\n'));
end++)
;
if ((*end == _T('\0')) && (token == end))
token = NULL;
*end = _T('\0');
nSubItem++;
}
nItem++;
}
AutoSizeColumns(GetAutoSizeStyle());
File.Close();
}
CATCH (CFileException, e)
{
AfxMessageBox(_T("Unable to load grid data"));
return FALSE;
}
END_CATCH
return TRUE;
}
#endif
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl overrideables
#ifndef GRIDCONTROL_NO_DRAGDROP
// This is no longer needed since I've changed to OLE drag and drop - but it's
// still cool code. :)
CImageList* CGridCtrl::CreateDragImage(CPoint *pHotSpot)
{
CDC* pDC = GetDC();
if (!pDC)
return NULL;
CRect rect;
CCellID cell = GetFocusCell();
if (!GetCellRect(cell.row, cell.col, rect))
return NULL;
// Translate coordinate system
rect.BottomRight() = CPoint(rect.Width(), rect.Height());
rect.TopLeft() = CPoint(0, 0);
*pHotSpot = rect.BottomRight();
// Create a new imagelist (the caller of this function has responsibility
// for deleting this list)
CImageList* pList = new CImageList;
if (!pList || !pList->Create(rect.Width(), rect.Height(), ILC_MASK, 1, 1))
{
if (pList)
delete pList;
return NULL;
}
// Create mem DC and bitmap
CDC MemDC;
CBitmap bm;
MemDC.CreateCompatibleDC(pDC);
bm.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
CBitmap* pOldBitmap = MemDC.SelectObject(&bm);
MemDC.SetWindowOrg(0, 0);
// Draw cell onto bitmap in memDC
CGridCellBase* pCell = GetCell(cell.row, cell.col);
if (pCell)
pCell->Draw(&MemDC, cell.row, cell.col, rect, FALSE);
// Clean up
MemDC.SelectObject(pOldBitmap);
ReleaseDC(pDC);
// Add the bitmap we just drew to the image list.
pList->Add(&bm, GetDefaultCell(FALSE, FALSE)->GetBackClr());
bm.DeleteObject();
return pList;
}
#endif
void CGridCtrl::OnFixedRowClick(CCellID& cell)
{
if (!IsValid(cell))
return;
if (GetHeaderSort())
{
CWaitCursor waiter;
if (cell.col == GetSortColumn())
SortItems(cell.col, !GetSortAscending());
else
SortItems(cell.col, TRUE);
Invalidate();
}
// Did the user click on a fixed column cell (so the cell was within the overlap of
// fixed row and column cells) - (fix by David Pritchard)
if (GetFixedColumnSelection())
{
if (cell.col < GetFixedColumnCount())
{
m_MouseMode = MOUSE_SELECT_ALL;
OnSelecting(cell);
}
else
{
m_MouseMode = MOUSE_SELECT_COL;
OnSelecting(cell);
}
}
}
void CGridCtrl::OnFixedColumnClick(CCellID& cell)
{
if (!IsValid(cell))
return;
// if (m_bListMode && (GetItemState(cell.row, m_nFixedCols) & GVNI_SELECTED))
// {
// OnEditCell(cell.row, cell.col, VK_LBUTTON);
// return;
// }
// Did the user click on a fixed row cell (so the cell was within the overlap of
// fixed row and column cells) - (fix by David Pritchard)
if (GetFixedRowSelection())
{
if (cell.row < GetFixedRowCount())
{
m_MouseMode = MOUSE_SELECT_ALL;
OnSelecting(cell);
}
else
{
m_MouseMode = MOUSE_SELECT_ROW;
OnSelecting(cell);
}
}
}
// Gets the extent of the text pointed to by str (no CDC needed)
// By default this uses the selected font (which is a bigger font)
CSize CGridCtrl::GetTextExtent(int nRow, int nCol, LPCTSTR str)
{
CGridCellBase* pCell = GetCell(nRow, nCol);
if (!pCell)
return CSize(0, 0);
else
return pCell->GetTextExtent(str);
}
// virtual
void CGridCtrl::OnEditCell(int nRow, int nCol, CPoint point, UINT nChar)
{
#ifndef GRIDCONTROL_NO_TITLETIPS
m_TitleTip.Hide(); // hide any titletips
#endif
// Can we do it?
CCellID cell(nRow, nCol);
if (!IsValid(cell) || !IsCellEditable(nRow, nCol))
return;
// Can we see what we are doing?
EnsureVisible(nRow, nCol);
if (!IsCellVisible(nRow, nCol))
return;
// Where, exactly, are we gonna do it??
CRect rect;
if (!GetCellRect(cell, rect))
return;
// Check we can edit...
if (SendMessageToParent(nRow, nCol, GVN_BEGINLABELEDIT) >= 0)
{
// Let's do it...
CGridCellBase* pCell = GetCell(nRow, nCol);
if (pCell)
pCell->Edit(nRow, nCol, rect, point, IDC_INPLACE_CONTROL, nChar);
}
}
// virtual
void CGridCtrl::EndEditing()
{
CCellID cell = GetFocusCell();
if (!IsValid(cell)) return;
CGridCellBase *pCell = GetCell(cell.row, cell.col);
if (pCell)
pCell->EndEdit();
}
// virtual
void CGridCtrl::OnEndEditCell(int nRow, int nCol, CString str)
{
CString strCurrentText = GetItemText(nRow, nCol);
if (strCurrentText != str)
{
SetItemText(nRow, nCol, str);
if (ValidateEdit(nRow, nCol, str) &&
SendMessageToParent(nRow, nCol, GVN_ENDLABELEDIT) >= 0)
{
SetModified(TRUE, nRow, nCol);
RedrawCell(nRow, nCol);
}
else
{
SetItemText(nRow, nCol, strCurrentText);
}
}
CGridCellBase* pCell = GetCell(nRow, nCol);
if (pCell)
pCell->OnEndEdit();
}
// If this returns FALSE then the editing isn't allowed
// virtual
BOOL CGridCtrl::ValidateEdit(int nRow, int nCol, LPCTSTR str)
{
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return TRUE;
return pCell->ValidateEdit(str);
}
// virtual
CString CGridCtrl::GetItemText(int nRow, int nCol) const
{
if (nRow < 0 || nRow >= m_nRows || nCol < 0 || nCol >= m_nCols)
return _T("");
CGridCellBase* pCell = GetCell(nRow, nCol);
ASSERT(pCell);
if (!pCell)
return _T("");
return pCell->GetText();
}
void CGridCtrl::ResetVirtualOrder()
{
m_arRowOrder.resize(m_nRows);
for (int i = 0; i < m_nRows; i++)
{
m_arRowOrder[i] = i;
}
}
void CGridCtrl::Reorder(int From, int To)
{
// Set line From just after Line To
ASSERT(From>= GetFixedRowCount() && To>=GetFixedRowCount()-1 && From<m_nRows && To<m_nRows);
int Value = m_arRowOrder[From];
m_arRowOrder.erase(m_arRowOrder.begin()+From);
int Offset = (From>=To ? 1:0);
m_arRowOrder.insert(m_arRowOrder.begin()+To+Offset, Value);
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。