// Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved //*************************************************************************** // // (c) 1996 by Microsoft Corporation // // grid.cpp // // This file contains the implementation for the HMOM object viewer grid. // // a-larryf 17-Sept-96 Created. // //*************************************************************************** #include "precomp.h" #include "resource.h" #include "grid.h" #include "globals.h" #include "notify.h" #include "gca.h" #include "gridhdr.h" #include "celledit.h" #include "core.h" #include "utils.h" #include "globals.h" #include #include #define CY_HEADER 16 #define CY_BORDER_MERGE 0 #define GS_TRUE _T("TRUE") #define GS_FALSE _T("FALSE") #define GS_EMPTY_STRING _T("") ///////////////////////////////////////////////////////////////////////////// // CGridSync // // This class provides you with a simple way to syncronize the grid cell editor // before making changes to the grid or accessing the values in the grid. // // To syncronize the grid with the grid cell editor, just declare an instance of // the CGridSync class in the method that will be doing the editing. CGridSync::CGridSync(CGrid* pGrid) { m_pGrid = pGrid; m_sc = pGrid->BeginSerialize(); } CGridSync::~CGridSync() { m_pGrid->EndSerialize(); } ///////////////////////////////////////////////////////////////////////////// // CGrid ///////////////////////////////////////////////////////////////////////////// // CGrid // // This is grid class that most clients will want to use. It contains // both CGridHdr and a CGridCore client windows. // ///////////////////////////////////////////////////////////////////////////// CGrid::CGrid() { m_bUIActive = FALSE; m_pcore = new CGridCore; m_phdr = new CHdrWnd(this); m_cxHeaderScrollOffset = 0; m_phdr->Header().m_pGrid = this; m_cxRowHandles = 0; m_pcore->SetParent(this); m_bmAscendingIndicator.LoadBitmap(IDB_ASCENDING_INDICATOR); m_bmDescendingIndicator.LoadBitmap(IDB_DESCENDING_INDICATOR); m_icolSortIndicator = NULL_INDEX; } CGrid::~CGrid() { delete m_pcore; delete m_phdr; } BEGIN_MESSAGE_MAP(CGrid, CWnd) //{{AFX_MSG_MAP(CGrid) ON_WM_SHOWWINDOW() ON_WM_SIZE() ON_WM_PAINT() ON_WM_SETFOCUS() ON_WM_KILLFOCUS() //}}AFX_MSG_MAP END_MESSAGE_MAP() //************************************************************* // CGrid::Create // // Create the CGrid window. // // Parameters: // CRect& rc // The rectangle in the parent's client area where this // CGrid window will be placed. // // CWnd* pwndParent // A pointer to the parent window. // // UINT nId // The window ID // // BOOL bVisible // TRUE if the CGrid window should be initially visible, FALSE // otherwise. // // Returns: // BOOL // TRUE if the window was created successfully. // //*************************************************************** BOOL CGrid::Create(CRect& rc, CWnd* pwndParent, UINT nId, BOOL bVisible) { DWORD dwStyle = WS_BORDER | WS_CHILD; if (bVisible) { dwStyle |= WS_VISIBLE; } // Create this window. It will be the parent of the header control and // the CGridCore windows. BOOL bDidCreate = CWnd::Create(NULL, _T("CGridCore"), dwStyle, rc, pwndParent, nId); if (!bDidCreate) { return FALSE; } // Create the header control as a child window. CRect rcHeader; rcHeader.left = 0; rcHeader.top = 0; rcHeader.right = rc.right - rc.left; rcHeader.bottom = CY_HEADER; dwStyle = WS_CHILD | HDS_BUTTONS | HDS_HORZ; if (bVisible) { dwStyle |= WS_VISIBLE; } bDidCreate = m_phdr->Create(dwStyle, rcHeader, this, nId); if (!bDidCreate) { return FALSE; } InitializeHeader(); // Create the CGridCore window as a child of this window. CRect rcGridCore = rcHeader; rcGridCore.top = rcHeader.bottom + CY_BORDER_MERGE; rcGridCore.bottom = rc.bottom; bDidCreate = m_pcore->Create(rcGridCore, nId, bVisible); if (!bDidCreate) { return FALSE; } SizeChildren(); return bDidCreate; } CGridCore* CGrid::GridCore() { return m_pcore; } void CGrid::UpdateScrollRanges() { m_pcore->UpdateScrollRanges(); } void CGrid::OnCellContentChange(int iRow, int iCol) { // The default behavior for this virtual function is to do nothing. } void CGrid::OnCellClicked(int iRow, int iCol) { // The default behavior for this virtual function is to do nothing. } void CGrid::OnCellClickedEpilog(int iRow, int iCol) { // The default behavior for this virtual function is to do nothing. } BOOL CGrid::OnCellKeyDown(int iRow, int iCol, UINT nChar, UINT nRepCnt, UINT nFlags) { return FALSE; } BOOL CGrid::OnCellChar(int iRow, int iCol, UINT nChar, UINT nRepCnt, UINT nFlags) { return FALSE; } BOOL CGrid::OnRowKeyDown(int iRow, UINT nChar, UINT nRepCnt, UINT nFlags) { return FALSE; } BOOL CGrid::OnBeginCellEditing(int iRow, int iCol) { return TRUE; } BOOL CGrid::OnQueryRowExists(int iRow) { return TRUE; } void CGrid::OnHeaderItemClick(int iItem) { } BOOL CGrid::GetCellEditContextMenu(int iRow, int iCol, CWnd*& pwndTarget, CMenu& menu, BOOL& bWantEditCommands) { return FALSE; } void CGrid::ModifyCellEditContextMenu(int iRow, int iCol, CMenu& menu) { } BOOL CGrid::OnCellEditContextMenu(CWnd* pwnd, CPoint ptContextMenu) { return FALSE; } int CGrid::CompareRows(int iRow1, int iRow2, int iSortOrder) { return 0; } void CGrid::BeginCellEditing() { m_pcore->BeginCellEditing(); } void CGrid::EndCellEditing() { m_pcore->EndCellEditing(); } BOOL CGrid::IsEditingCell() { return m_pcore->IsEditingCell(); } void CGrid::RefreshCellEditor() { m_pcore->RefreshCellEditor(); } SCODE CGrid::BeginSerialize() { return m_pcore->SyncCellEditor(); } void CGrid::EndSerialize() { } SCODE CGrid::SyncCellEditor() { return m_pcore->SyncCellEditor(); } int CGrid::RowHeight() { return m_pcore->RowHeight(); } void CGrid::SelectCell(int iRow, int iCol, BOOL bForceBeginEdit) { m_pcore->SelectCell(iRow, iCol, bForceBeginEdit); } void CGrid::SelectRow(int iRow) { m_pcore->SelectRow(iRow); } void CGrid::EnsureRowVisible(int iRow) { m_pcore->EnsureRowVisible(iRow); } CGridCell& CGrid::GetAt(int iRow, int iCol) { return m_pcore->GetAt(iRow, iCol); } int CGrid::GetRows() { return m_pcore->GetRows(); } int CGrid::GetCols() { return m_pcore->GetCols(); } int CGrid::GetSelectedRow() { return m_pcore->GetSelectedRow(); } void CGrid::GetSelectedCell(int& iRow, int& iCol) { m_pcore->GetSelectedCell(iRow, iCol); } BOOL CGrid::EntireRowIsSelected() { return m_pcore->EntireRowIsSelected(); } void CGrid::InsertRowAt(int iRow) { m_pcore->InsertRowAt(iRow); } int CGrid::AddRow() { return m_pcore->AddRow(); } void CGrid::DeleteRowAt(int iRow, BOOL bUpdateWindow) { m_pcore->DeleteRowAt(iRow, bUpdateWindow); } //void CGrid::InsertColumnAt(int iCol, int iWidth, LPCTSTR pszTitle ); // void Clear(BOOL bUpdateWindow=TRUE); void CGrid::ClearRows(BOOL bUpdateWindow) { m_pcore->ClearRows(bUpdateWindow); } BOOL CGrid::WasModified() { return m_pcore->WasModified(); } void CGrid::SetModified(BOOL bWasModified) { m_pcore->SetModified(bWasModified); } int CGrid::ColWidth(int iCol) { return m_pcore->ColWidth(iCol); } int CGrid::ColWidthFromHeader(int iCol) { return m_phdr->Header().ColWidthFromHeader(iCol); } CString& CGrid::ColTitle(int iCol) { return m_pcore->ColTitle(iCol); } void CGrid::SetCellModified(int iRow, int iCol, BOOL bWasModified) { m_pcore->SetCellModified(iRow, iCol, bWasModified); } int CGrid::GetRowHandleWidth() { return m_pcore->GetRowHandleWidth(); } int CGrid::CompareCells(int iRow1, int iRow2, int iCol) { return CompareCells(iRow1, iCol, iRow2, iCol); } void CGrid::NotifyCellModifyChange() { m_pcore->NotifyCellModifyChange(); } //********************************************************* // CGrid::AddColumn // // Add a column to the grid header. // // Parameters: // int iWidth // The column width // // LPCTSTR pszTitle // A pointer to the column title. // // Returns: // Nothing. // //******************************************************** void CGrid::AddColumn(int iWidth, LPCTSTR pszTitle) { InsertColumnAt(GetCols(), iWidth, pszTitle); } //******************************************************** // CGrid::Clear // // Delete all rows and columns from the grid. // // Parameters: // [in] BOOL bUpdateWindow // TRUE if the window should be updated after clearing the grid. // // Returns: // Nothing. // //********************************************************** void CGrid::Clear(BOOL bUpdateWindow) { m_pcore->Clear(bUpdateWindow); int nCols = m_phdr->Header().GetItemCount(); while (--nCols >= 0) { m_phdr->Header().DeleteItem(nCols); } m_phdr->Header().SelectColumn(0); m_aSortAscending.RemoveAll(); if (bUpdateWindow) { if (m_phdr->m_hWnd) { m_phdr->RedrawWindow(); } } } //******************************************************** // CGrid::InsertColumnAt // // Insert a new column at the specified index. // // Parameters: // int iCol // The column index where the new column will be inserted. // // int iWidth // The width of the column. // // LPCTSTR pszTitle // Pointer to the column's title. // // Returns: // Nothing. // //******************************************************** void CGrid::InsertColumnAt(int iCol, int iWidth, LPCTSTR pszTitle ) { ASSERT(m_aSortAscending.GetSize() == GetCols()); m_aSortAscending.InsertAt(iCol, TRUE); m_pcore->InsertColumnAt(iCol, iWidth, pszTitle); ASSERT(m_aSortAscending.GetSize() == GetCols()); // If the header doesn't exist or doesn't have a window // yet, then we must delay adding the title to the header // until it is initialized later. if (m_phdr->m_hWnd!=NULL) { HD_ITEM item; item.mask = HDI_FORMAT | HDI_TEXT | HDI_WIDTH | HDI_LPARAM | HDI_BITMAP; item.hbm = NULL; item.fmt = HDF_LEFT; item.lParam = MAKELPARAM(TRUE,iWidth); item.cxy = iWidth; item.pszText = (LPTSTR) (void*) pszTitle; item.cchTextMax = _tcslen(pszTitle); m_phdr->Header().InsertItem(iCol, &item); } } //******************************************************** // CGrid::InitializeHeader // // Initialize the header control with the column titles. // // Parameters: // None. // // Returns: // Nothing. // //********************************************************* void CGrid::InitializeHeader() { HD_ITEM item; item.mask = HDI_FORMAT | HDI_TEXT | HDI_WIDTH | HDI_LPARAM | HDI_BITMAP; item.hbm = NULL; item.fmt = HDF_LEFT; item.lParam = FALSE; m_phdr->Header().SetFont(&m_pcore->GetFont()); int nCols = GetCols(); for (int iCol = 0; iCol < nCols; ++iCol) { item.cxy = ColWidth(iCol); item.lParam = MAKELPARAM(TRUE,item.cxy); CString* psTitle = &ColTitle(iCol); item.pszText = (LPTSTR) (void*) (LPCTSTR) *psTitle; item.cchTextMax = psTitle->GetLength(); m_phdr->Header().InsertItem(iCol, &item); } } //*********************************************************** // CGrid::SetHeaderSortIndicator // // Set the sort indicator on the column header to show whether // the column is sorted in ascending or descending order. // // Parameters: // int iCol // The column index of the cell that was double clicked. // // BOOL bAscending // TRUE if the ASCENDING sort indicator should be shown, // FALSE if the DESCENDING sort indicator should be shown. // // BOOL bNone // TRUE if no sort indicator should be displayed for the // specified column. // // Returns: // Nothing. // //************************************************************ void CGrid::SetHeaderSortIndicator(int iCol, BOOL bAscending, BOOL bNone) { HD_ITEM item; CHeaderCtrl* phdr = &m_phdr->Header(); TCHAR szTitle[128]; // Remove the sort indicator from the current sort indicator column // by deleting the header item and then inserting a header item with // just the text. Don't do this if the column hasn't changed because // we will just delete it and insert it again anyway. static CString sTitle; if (m_icolSortIndicator!=NULL_INDEX) { if (iCol != m_icolSortIndicator) { item.mask = HDI_TEXT | HDI_WIDTH | HDI_LPARAM; szTitle[0] = 0; ASSERT(m_icolSortIndicator != -1); item.pszText = szTitle; // a-judypo fix item.cchTextMax = sizeof(szTitle) - 1; phdr->GetItem(m_icolSortIndicator, &item); item.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH | HDI_LPARAM | HDI_BITMAP; item.pszText = szTitle; item.cchTextMax = sizeof(szTitle) - 1; item.cchTextMax = _tcslen(szTitle); item.fmt = HDF_LEFT; item.hbm = NULL; ASSERT(m_icolSortIndicator != -1); phdr->DeleteItem(m_icolSortIndicator); ASSERT(m_icolSortIndicator != -1); phdr->InsertItem(m_icolSortIndicator, &item); } } if (bNone) { return; } // At this point, the previous sort indicator has been // removed. Now insert a new one. item.pszText = szTitle; item.cchTextMax = sizeof(szTitle) - 1; sTitle.Empty(); item.mask = HDI_TEXT | HDI_WIDTH | HDI_FORMAT | HDI_LPARAM; item.fmt = HDF_LEFT; phdr->GetItem(iCol, &item); item.mask = HDI_TEXT | HDI_FORMAT | HDI_LPARAM; item.cchTextMax = _tcslen(szTitle); phdr->DeleteItem(iCol); CBitmap* pbm = NULL; if (!bNone) { item.mask = item.mask | HDI_BITMAP; item.fmt = item.fmt | HDF_BITMAP_ON_RIGHT; if (bAscending) { item.hbm = (HBITMAP) m_bmAscendingIndicator; } else { item.hbm = (HBITMAP) m_bmDescendingIndicator; } } int iColInsert = phdr->InsertItem(iCol, &item); m_icolSortIndicator = iCol; phdr->RedrawWindow(); } //*********************************************************** // CGrid::OnCellDoubleClicked // // This is the default handler for double clicking a cell. It // does nothing, but derived classes can override this method // to do something more interesting. // // Parameters: // int iRow // The row index of the cell that was double clicked. // // int iCol // The column index of the cell that was double clicked. // // Returns: // Nothing. // //************************************************************ void CGrid::OnCellDoubleClicked(int iRow, int iCol) { } //*********************************************************** // CGrid::OnRowHandleDoubleClicked // // This is the default handler for double clicking a row handle. It // does nothing, but derived classes can override this method // to do something more interesting. // // Parameters: // int iRow // The row index of the cell that was double clicked. // // Returns: // Nothing. // //************************************************************ void CGrid::OnRowHandleDoubleClicked(int iRow) { } //*********************************************************** // CGrid::OnRowhandleClicked // // This is the default handler for clicking a row handle. It // does nothing, but derived classes can override this method // to do something more interesting. // // Parameters: // int iRow // The row index of the cell that was double clicked. // // Returns: // Nothing. // //************************************************************ void CGrid::OnRowHandleClicked(int iRow) { } //*********************************************************** // CGrid::OnShowWindow // // Handle the ShowWindow command. Since CGrid contains two sibling // windows (CGridCore and CHeaderCtrl) it is necessary to pass // the ShowWindow command on to both siblings. // // Parameters: // See the MFC documentation for CWnd::ShowWindow // // Returns: // Nothing. // //************************************************************ void CGrid::OnShowWindow(BOOL bShow, UINT nStatus) { CWnd::OnShowWindow(bShow, nStatus); int nShowCmd = bShow ? SW_SHOW : SW_HIDE; if (m_pcore->m_hWnd) { m_pcore->ShowWindow(nShowCmd); } if (m_phdr->m_hWnd) { m_phdr->ShowWindow(nShowCmd); } } //*********************************************************** // CGrid::OnSize // // Handle the WM_SIZE command. Since CGrid contains two sibling // windows (CGridCore and CHeaderCtrl) it is necessary to pass // the WM_SIZE command on to both siblings. // // Parameters: // See the MFC documentation for CWnd::OnSize // // Returns: // Nothing. // //************************************************************ void CGrid::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); SizeChildren(); } //****************************************************************** // CGrid::GetColumnPos // // Get the position of the left edge of the specified column. // // Parameters: // int iCol // The index of the desired column. // // Returns: // The position of the left edge of the column. // //****************************************************************** int CGrid::GetColumnPos(int iCol) { int ix = 0; for (int iLoop=0; iLoop < iCol; ++iLoop) { ix += ColWidth(iLoop); } return ix; } //***************************************************************** // CGrid::SizeChildren // // Size the child windows of this grid. // // Parameters: // None. // // Returns: // Nothing. // //***************************************************************** void CGrid::SizeChildren() { CRect rcClient; GetClientRect(rcClient); int cx = rcClient.Width(); int cy = rcClient.Height(); // At this point in time, we haven't decided whether the row handles // should go in the CGrid or CGridCore, but regardless of where they // are drawn, we want to have the corner stub. int cxRowHandles = m_cxRowHandles; if (cxRowHandles == 0) { cxRowHandles = m_pcore->GetRowHandleWidth(); } // Adjust the width of the header control, but not its height CRect rc(0, 0, cx, CY_HEADER); if (m_phdr->m_hWnd) { CRect rcHeader; rcHeader.left = cxRowHandles; rcHeader.right = rcClient.right; rcHeader.top = 0; rcHeader.bottom = CY_HEADER; m_phdr->MoveWindow(rcHeader); m_phdr->OnGridSizeChanged(); } // Adjust the size of the CGridCore window to be the entire client // area minus the rectangle consumed by the header control. rc.top = rc.bottom - CY_BORDER_MERGE; rc.bottom = cy; rc.left = m_cxRowHandles; if (m_pcore->m_hWnd) { m_pcore->MoveWindow(rc); } } //************************************************************* // CGrid::OnCellFocusChange // // This is a virtual method that may be overridden in derived // classes to catch the "focus" change event. // // Parameters: // [in] int iRow // The row index of the affected cell. // // [in] int iCol // The column index of the affected cell. // // [in] int iNextRow // The next row that will be selected. This parameter is provided // as a hint and is valid only if bGotFocus is FALSE. // // [in] int iNextCol // The column index of the next cell that will get the focus when the // current cell is loosing focus. This parameter is provided as a hint and // is valid only if bGotFocus is FALSE. // // [in] BOOL bGotFocus // TRUE if focus is being changed to the specified cell. // FALSE if this is a request for the current cell to // release the focus. // // Returns: // If bGotFocus is TRUE, then return TRUE to allow the focus to // be set to the new cell or return FALSE to prevent the focus // from being set to the new cell. // // If bGotFocus is FALSE, then this is a request to release the // focus from the specified cell. Return TRUE to allow the specified // cell to loose the focus. Return FALSE to prevent the specified cell // from loosing the focus. // //******************************************************************* BOOL CGrid::OnCellFocusChange(int iRow, int iCol, int iNextRow, int iNextCol, BOOL bGotFocus) { return TRUE; } void CGrid::SetHeaderScrollOffset(int iCol, int cxOffset) { // Adjust the width of the header control, but not its height int dxScroll = cxOffset + m_cxHeaderScrollOffset; m_cxHeaderScrollOffset = cxOffset; if (m_phdr->m_hWnd) { m_phdr->SetScrollOffset(iCol, cxOffset); m_phdr->UpdateWindow(); } } //****************************************************************** // CGrid::PointToCell // // Convert a point in CGrid's client coordinates to corresponding // grid cell's coordinates. // // Parameters: // [in] CPoint pt // The point in CGrid's client coordinate space. // // [out] int& iRow // The place where the cell's row index is returned. // // [out] int& iCol // The place where the cell's column index is returned. // // Returns: // TRUE if the point hit a cell, otherwise FALSE. // //******************************************************************* BOOL CGrid::PointToCell(CPoint pt, int& iRow, int& iCol) { // GridCore's version of PointToCell works in its own client // coordinates, so we need to offset the vertical coordinate // to account for the space consumed by CGrid's header control. pt.y = pt.y - ( CY_HEADER + CY_BORDER_MERGE ); return m_pcore->PointToCell(pt, iRow, iCol); } //****************************************************************** // CGrid::PointToRow // // Convert a point in CGrid's client coordinates to corresponding // row index. // // Parameters: // [in] CPoint pt // The point in CGrid's client coordinate space. // // [out] int& iRow // The place where the row index is returned. // // // Returns: // TRUE if the point hit a row, otherwise FALSE. // //******************************************************************* BOOL CGrid::PointToRow(CPoint pt, int& iRow) { // GridCore's version of PointToRow works in its own client // coordinates, so we need to offset the vertical coordinate // to account for the space consumed by CGrid's header control. pt.y = pt.y - ( CY_HEADER + CY_BORDER_MERGE ); return m_pcore->PointToRow(pt, iRow); } //****************************************************************** // CGrid::PointToRowHandle // // Convert a point in CGrid's client coordinates to corresponding // row handle index. // // Parameters: // [in] CPoint pt // The point in CGrid's client coordinate space. // // [out] int& iRow // The place where the row index is returned. // // // Returns: // TRUE if the point hit a row handle, otherwise FALSE. // //******************************************************************* BOOL CGrid::PointToRowHandle(CPoint pt, int& iRow) { // GridCore's version of PointToHandle works in its own client // coordinates, so we need to offset the vertical coordinate // to account for the space consumed by CGrid's header control. pt.y = pt.y - ( CY_HEADER + CY_BORDER_MERGE ); return m_pcore->PointToRowHandle(pt, iRow); } //******************************************************************** // CGrid::DrawCornerStub // // Draw the "stub" in the top-left corner of the window when row handles // are displayed. // // Parameters: // CDC* pdc // Pointer to the display context. // // Returns: // Nothing. // //******************************************************************* void CGrid::DrawCornerStub(CDC* pdc) { // Check to see if the grid has row handles. If so, draw the little rectangle // at the top left corner that is bordered on the right by the grid header and // bordered on the bottom by the row handles. int cxRowHandles = m_cxRowHandles; if (cxRowHandles == 0) { cxRowHandles = m_pcore->GetRowHandleWidth(); } if (cxRowHandles == 0) { return; } CRect rcFill; CBrush br3DSHADOW(GetSysColor(COLOR_3DSHADOW)); CBrush br3DFACE(GetSysColor(COLOR_3DFACE)); CBrush br3DHILIGHT(GetSysColor(COLOR_3DHILIGHT)); // Draw a two-pixel 3D highlight along the top edge. rcFill.left = 0; rcFill.right = cxRowHandles - 1; rcFill.top = 0; rcFill.bottom = 2; pdc->FillRect(rcFill, &br3DHILIGHT); // Draw a two-pixel 3D highlight along the left edge. rcFill.left = 0; rcFill.right = 2; rcFill.top = 0; rcFill.bottom = CY_HEADER - 1; pdc->FillRect(rcFill, &br3DHILIGHT); // Fill the face of the button. rcFill.left = 2; rcFill.right = cxRowHandles - 2; rcFill.top = 2; rcFill.bottom = CY_FONT - 2; pdc->FillRect(rcFill, &br3DFACE); // Draw a one-pixel shadow on the bottom edge. rcFill.left = 2; rcFill.right = cxRowHandles - 1; rcFill.top = CY_FONT - 2; rcFill.bottom = CY_FONT - 1; pdc->FillRect(rcFill, &br3DSHADOW); rcFill.left = 1; rcFill.right = cxRowHandles - 1; rcFill.top = CY_FONT - 1; rcFill.bottom = CY_FONT; pdc->FillRect(rcFill, &br3DSHADOW); // Draw a one-pixel shadow on the right edge. rcFill.left = cxRowHandles - 3; rcFill.right = cxRowHandles - 2; rcFill.top = 2; rcFill.bottom = CY_FONT - 2; pdc->FillRect(rcFill, &br3DSHADOW); // Draw the second vertical part of the shadow on the right. rcFill.left = cxRowHandles - 2; rcFill.right = cxRowHandles - 1; rcFill.top = 1; rcFill.bottom = CY_FONT - 1; pdc->FillRect(rcFill, &br3DSHADOW); // Draw a one-pixel black border on the right edge. rcFill.left = cxRowHandles - 1; rcFill.right = cxRowHandles; rcFill.top = 0; rcFill.bottom = CY_FONT; pdc->FillRect(rcFill, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); // Draw a one-pixel black border on the bottom edge. rcFill.left = 0; rcFill.right = cxRowHandles; rcFill.top = CY_FONT; rcFill.bottom = CY_FONT + 1; pdc->FillRect(rcFill, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); } //******************************************************************** // CGrid::DrawRowhandles // // Draw the row handles along the left side of the grid. // // Parameters: // CDC* pdc // Pointer to the display context. // // Returns: // Nothing. // //******************************************************************* void CGrid::DrawRowHandles(CDC* pdc) { // If there are no row handles, do nothing. int cxHandle = m_cxRowHandles; if (cxHandle <= 0) { return; } // If the row doesn't have a height, do nothing. int cyRow = m_pcore->RowHeight(); if (cyRow <= 0) { return; } CRect rcClient; GetClientRect(rcClient); CRect rcClientChild; m_pcore->GetClientRect(rcClientChild); CRect rcRowHandles; GetClientRect(rcRowHandles); rcRowHandles.top += CY_HEADER; rcRowHandles.right = m_cxRowHandles; rcRowHandles.bottom = rcRowHandles.top + rcClientChild.Height(); pdc->IntersectClipRect(rcRowHandles); // If the row handles are completely clipped out, then do nothing. CRect rcPaint; if (pdc->GetClipBox(rcPaint) != NULLREGION ) { // The top and bottom of the paint rectangle need to be aligned with the top and // bottom edges of partially obscured row handles. rcPaint.top = ((rcPaint.top - CY_HEADER) / cyRow) * cyRow + CY_HEADER; rcPaint.bottom = ((rcPaint.bottom - CY_HEADER) + (cyRow - 1))/cyRow * cyRow + CY_HEADER; } else { rcPaint = rcRowHandles; } // Draw the vertical line on the right that separates the row handles from // the data. CRect rcFill; rcFill.left = rcRowHandles.right - 1; rcFill.right = rcRowHandles.right; rcFill.top = rcRowHandles.top; rcFill.bottom = rcRowHandles.bottom; pdc->FillRect(rcFill, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); // Define a rectangle for a handle that slides from the top to // the bottom of the stack of row handles as they are drawn. CRect rcHandle(rcRowHandles); rcHandle.top = rcRowHandles.top; rcHandle.bottom = rcHandle.top + cyRow; CBrush br3DSHADOW(GetSysColor(COLOR_3DSHADOW)); CBrush br3DFACE(GetSysColor(COLOR_3DFACE)); CBrush br3DHILIGHT(GetSysColor(COLOR_3DHILIGHT)); int nHandles = rcPaint.Height() / cyRow; while (--nHandles >= 0) { // The horizontal divider at the bottom of the row handle. rcFill.left = rcHandle.left; rcFill.right = rcHandle.right; rcFill.top = rcHandle.bottom - 1; rcFill.bottom = rcHandle.bottom; pdc->FillRect(rcFill, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); // The horizontal highlight across the top of the row handle. rcFill.left = rcHandle.left; rcFill.right = rcHandle.right - 1; rcFill.top = rcHandle.top; rcFill.bottom = rcHandle.top + 1; pdc->FillRect(rcFill, &br3DHILIGHT); // The vertical highlight on the left of the row handle. rcFill.left = rcHandle.left; rcFill.right = rcHandle.left + 1; rcFill.top = rcHandle.top + 1; rcFill.bottom = rcHandle.bottom - 1; pdc->FillRect(rcFill, &br3DHILIGHT); // The face of the row handle. rcFill.left = rcHandle.left + 1; rcFill.right = rcHandle.right - 1; rcFill.top = rcHandle.top + 1; rcFill.bottom = rcHandle.bottom - 1; pdc->FillRect(rcFill, &br3DFACE); // Increment to the next row. rcHandle.top += cyRow; rcHandle.bottom += cyRow; } // don't clip. pdc->SelectClipRgn(NULL, RGN_COPY); // If the CGridCore displays a horizontal scroll bar, then the // CGrid must fill in the "empty" area just to the left of the // horizontal scroll bar. // // The CGridCore displays the horizontal scroll bar, then the // height of m_GridCore will be less that the height of the // client area plus the header height. int cyHScrollBarChild = (rcClient.Height() - CY_HEADER) - rcClientChild.Height(); if (cyHScrollBarChild <= 0) { return; } // CBrush brScrollBar(GetSysColor(COLOR_SCROLLBAR)); CRect rcStub; rcStub.left = 0; rcStub.right = m_cxRowHandles; rcStub.bottom = rcClient.bottom; rcStub.top = rcClient.bottom - cyHScrollBarChild; pdc->FillRect(rcStub, &br3DFACE); rcFill.top = rcStub.top; rcFill.left = rcStub.left; rcFill.right = rcStub.right; rcFill.bottom = rcStub.top + 1; pdc->FillRect(rcFill, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); rcFill.top = rcStub.top; rcFill.bottom = rcStub.bottom; rcFill.left = rcStub.right - 1; rcFill.right = rcStub.right; pdc->FillRect(rcFill, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); // The dark gray line on the left rcFill.top = rcStub.top + 3; rcFill.bottom = rcStub.bottom - 2; rcFill.left = rcStub.left + 2; rcFill.right = rcStub.left + 3; pdc->FillRect(rcFill, &br3DSHADOW); // The dark gray line on the top rcFill.top = rcStub.top + 3; rcFill.bottom = rcStub.top + 4; rcFill.left = rcStub.left + 1; rcFill.right = rcStub.right - 2; pdc->FillRect(rcFill, &br3DSHADOW); // The highlight on the bottom rcFill.top = rcStub.bottom - 3; rcFill.bottom = rcStub.bottom - 2; rcFill.left = rcStub.left + 2; rcFill.right = rcStub.right - 2;; pdc->FillRect(rcFill, &br3DHILIGHT); // The highlight on the right rcFill.top = rcStub.top + 2; rcFill.bottom = rcStub.bottom - 2; rcFill.left = rcStub.right - 4; rcFill.right = rcStub.right - 3;; pdc->FillRect(rcFill, &br3DHILIGHT); } //****************************************************************** // CGrid::ColumnIsAscending // // Return the sort order flag for the specified column. // // Parameters: // [in] const int iCol // The index of the desired column. // // Returns: // BOOL // TRUE if the specified column's sort order is ascending, FALSE // if it is descending. // //****************************************************************** // BOOL CGrid::ColumnIsAscending(const int iCol) const { ASSERT(iCol>= 0 && iCol < m_aSortAscending.GetSize()); if (iCol <0 || iCol >= m_aSortAscending.GetSize()) { return TRUE; } return m_aSortAscending[iCol]; } //**************************************************************** // CGrid::SetSortDirection // // Set the sort direction for the given column. Note that this does // not actually resort any of the grid rows. To resort the grid, you // must call SortGrid. // // Parameters: // const int iCol // The column who's sort order flag will be changed. // // const BOOL bSortAscending // TRUE to set the sort direction for the column to ascending, // FALSE to set the sort direction to descending. // // Returns: // Nothing. // //****************************************************************** void CGrid::SetSortDirection(const int iCol, const BOOL bSortAscending) { ASSERT( iCol >=0 && iCol < m_aSortAscending.GetSize()); if (iCol < 0 || iCol >= m_aSortAscending.GetSize()) { return; } m_aSortAscending[iCol] = (WORD) bSortAscending; } //******************************************************************* // CGrid::ClearAllSortDirectionFlags // // Clears the sort direction flags for all of the columns. The // sort order for each column is set to ascending. Note that this // changes the state of the sort order flags, but does not resort the // grid. Call SortGrid to resort the grid. // // Parameters: // None. // // Returns: // Nothing. // //******************************************************************* void CGrid::ClearAllSortDirectionFlags() { INT_PTR nFlags = m_aSortAscending.GetSize(); for (INT_PTR iFlag=0; iFlagHeader().SelectColumn(iSortColumn); BOOL bColumnIsAscending = ColumnIsAscending(iSortColumn); m_pcore->SortGrid(iRowFirst, iRowLast, iSortColumn, bColumnIsAscending, bRedrawWindow); } //******************************************************************* // CGrid::CompareCells // // This method compares two cells in different rows but the same column. // // Parameters: // int iRow1 // The row index of the first cell. // // int iCol1 // The column index of the first cell. // // int iRow2 // The row index of the second row. // // int iCol2 // The column index of the second cell. // // Returns: // >0 if row 1 is greater than row 2 // 0 if row 1 equal zero // <0 if row 1 is less than zero. // //******************************************************************** int CGrid::CompareCells(int iRow1, int iCol1, int iRow2, int iCol2) { CGridCell* pgc1 = &GetAt(iRow1, iCol1); CGridCell* pgc2 = &GetAt(iRow2, iCol2); int iResult = pgc1->Compare(pgc2); return iResult; #if 0 CString sValue1; CString sValue2; int iResult; VARTYPE vt1; VARTYPE vt2; GetAt(iRow1, iCol1).GetValue(sValue1, vt1); GetAt(iRow2, iCol2).GetValue(sValue2, vt2); iResult = sValue1.Compare(sValue2); if (iResult == 0) { iResult = vt1 > vt2; } #endif //0 return iResult; } void CGrid::OnPaint() { CPaintDC dc(this); // device context for painting DrawCornerStub(&dc); DrawRowHandles(&dc); // Do not call CWnd::OnPaint() for painting messages } //********************************************************** // CGrid::RedrawRow // // Redraw the specified row of the grid. // // Parameters: // [in] int iRow // The row index. // // Returns: // Nothing. // //********************************************************** void CGrid::RedrawRow(int iRow) { ASSERT(iRow < GetRows()); CGridRow& row = GetRowAt(iRow); row.Redraw(); } //********************************************************** // CGrid::RedrawCell // // Redraw the specified grid cell. // // Parameters: // [in] int iRow // The row index. // // [in] iCol // The column index. // // Returns: // Nothing. // //********************************************************** void CGrid::RedrawCell(int iRow, int iCol) { if (!::IsWindow(m_hWnd)) { return; } m_pcore->DrawCell(iRow, iCol); } void CGrid::SetColumnWidth(int iCol, int cx, BOOL bRedraw) { m_phdr->Header().SetColumnWidth(iCol, cx, FALSE); m_pcore->SetColumnWidth(iCol, cx, FALSE); if (bRedraw && (m_hWnd!=NULL)) { RedrawWindow(); } } //************************************************************************ // CGrid::PreTranslateMessage // // PreTranslateMessage is hooked out to detect the OnContextMenu event. // // Parameters: // See the MFC documentation. // // Returns: // TRUE if the message is handled here. // //************************************************************************* BOOL CGrid::PreTranslateMessage(MSG* pMsg) { // CG: This block was added by the Pop-up Menu component { // Shift+F10: show pop-up menu. if ((((pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) && // If we hit a key and (pMsg->wParam == VK_F10) && (GetKeyState(VK_SHIFT) & ~1)) != 0) || // it's Shift+F10 OR (pMsg->message == WM_CONTEXTMENU)) // Natural keyboard key { CRect rect; GetClientRect(rect); ClientToScreen(rect); CPoint point = rect.TopLeft(); point.Offset(5, 5); OnContextMenu(NULL, point); return TRUE; } } return CWnd::PreTranslateMessage(pMsg); } #if 0 //*************************************************************** // CGrid::FindCellPosition // // Given a cell pointer, find its row and column indexes. This is // required because the cell has no other way to directly determine // its position. // // Parameters: // [in] CGridCell* pgc // Pointer to the grid cell to find. // // [out] int& iRow // The row index of the cell if found, NULL_INDEX if the // cell is not found. // // [out] int& iCol // The column index of the cell if found, NULL_INDEX if the cell // is not found. // // Returns: // The row and column indexes of the cell by reference. // //*************************************************************** void CGrid::FindCellPosition(CGridCell* pgc, int& iRow, int& iCol) { pgc->FindCellPos(iRow, iCol); } #endif //0 BOOL CGrid::NumberRows(BOOL bNumberRows, BOOL bRedraw) { BOOL bWasNumberingRows = m_pcore->NumberRows(bNumberRows, FALSE); if (bRedraw && m_hWnd!=NULL) { RedrawWindow(); } return bWasNumberingRows; } BOOL CGrid::IsNumberingRows() { return m_pcore->IsNumberingRows(); } CFont& CGrid::GetGridFont() { return m_pcore->GetFont(); } void CGrid::NotifyRowHandleWidthChanged() { SizeChildren(); } void CGrid::OnGetIWbemServices(LPCTSTR szNamespace, VARIANT FAR* pvarUpdatePointer, VARIANT FAR* pvarServices, VARIANT FAR* pvarSc, VARIANT FAR* pvarUserCancel) { // Do nothing unless this virtual function is overridden. If nothing is done // it just means that no one was listening to the event. return; } //******************************************************* // CGrid::EditCellObject // // Edit a cell containing an embedded object. // // Parameters: // [in] CGridCell* pgc // The grid cell to edit. // // [in] int iRow // The row index of the cell to edit. // // [in] int iCol // The column index of the cell to edit. // // Returns: // Nothing. // //******************************************************** void CGrid::EditCellObject(CGridCell* pgc, int iRow, int iCol) { } //******************************************************* // CGrid::EditCellObject // // Edit a cell containing an embedded object. // // Parameters: // [in] int iRow // The row index of the cell to edit. // // [in] int iCol // The column index of the cell to edit. // // Returns: // Nothing. // //******************************************************** void CGrid::OnChangedCimtype(int iRow, int iCol) { } //******************************************************* // CGrid::OnRequestUIActive() // // Notify the derived class that there is a request to // become UI active. // // Parameters: // None. // // Returns: // Nothing. // //******************************************************** void CGrid::OnRequestUIActive() { } void CGrid::OnSetFocus(CWnd* pOldWnd) { CWnd::OnSetFocus(pOldWnd); // TODO: Add your message handler code here if (!m_bUIActive) { m_bUIActive = TRUE; OnRequestUIActive(); if (m_pcore && ::IsWindow(m_pcore->m_hWnd)) { m_pcore->SetFocus(); } } } void CGrid::OnKillFocus(CWnd* pNewWnd) { CWnd::OnKillFocus(pNewWnd); // TODO: Add your message handler code here m_bUIActive = FALSE; } //********************************************************** // CGrid::GetCellEnumStrings // // The cell editor calls this method to get any enumeration // strings that should be displayed for the grid cell in a // drop-down combo. // // Parameters: // [in] int iRow // The cell's row index. // // [in] int iCol // The cell's column index. // // [out] CStringArray& sa // The enumeration strings are returned in this string array. // // Returns; // Nothing. // //*********************************************************** void CGrid::GetCellEnumStrings(int iRow, int iCol, CStringArray& sa) { sa.RemoveAll(); } //********************************************************** // CGrid::OnEnumSelection // // The cell editor calls this method when the user makes a // selection from a drop-down combo. // // Parameters: // [in] int iRow // The cell's row index. // // [in] int iCol // The cell's column index. // // // Returns; // Nothing. // //*********************************************************** void CGrid::OnEnumSelection(int iRow, int iCol) { } //********************************************************** // CGrid::PreModalDialog // // The grid calls this method just prior to putting up a modal // dialog. OleControls can hook this virtual function out to // call COleControl::PreModalDialog when necessary. // // Parameters: // None. // // Returns: // Nothing. // //********************************************************** void CGrid::PreModalDialog() { } //********************************************************** // CGrid::PostModalDialog // // The grid calls this method just after putting up a modal // dialog. OleControls can hook this virtual function out to // call COleControl::PostModalDialog when necessary. // // Parameters: // None. // // Returns: // Nothing. // //********************************************************** void CGrid::PostModalDialog() { } //********************************************************** // CGrid::GetRowAt // // The cell editor calls this method when the user makes a // selection from a drop-down combo. // // Parameters: // [in] int iRow // The row index. // // Returns; // CGridRow& // A reference to the specified grid row. // //*********************************************************** CGridRow& CGrid::GetRowAt(int iRow) { return m_pcore->GetRowAt(iRow); } //********************************************************** // CGrid::SetRowModified // // This method marks a row as modified or unmodified depending // on the bModified parameter. // // Parameters: // [in] int iRow // The row index. // // [in] BOOL bModified // TRUE if the row should be marked as "modified". // // Returns; // Nothing. // //*********************************************************** void CGrid::SetRowModified(int iRow, BOOL bModified) { CGridRow& row = GetRowAt(iRow); row.SetModified(bModified); } //********************************************************** // CGrid::GetRowModified // // This method gets the "modified" state of a given row. // // Parameters: // [in] int iRow // The row index. // // Returns; // BOOL // TRUE if the row was modified. // //*********************************************************** BOOL CGrid::GetRowModified(int iRow) { CGridRow& row = GetRowAt(iRow); BOOL bModified = row.GetModified(); return bModified; } //********************************************************** // CGrid::GetWbemServices // // Get the WBEM services pointer for a given namespace. // // This method is called when editing an embedded object and // the embedded object contains a property with an embedded // object. The SingleView control that lives on the object // editor dialog will fire a GetWbemServices event which is // propagated up through the control hierarchy. // // Parameters: // [in] LPCTSTR szNamespace // The namespace. // // [out] VARIANT FAR* pvarUpdatePointer // // [out] VARIANT FAR* pvarServices // // [out] VARIANT FAR* pvarSc // The status code is returned here. // // [out] VARIANT FAR* pvarUserCancel // A boolean flag indicating whether or not the // user cancelled the login. // // // Returns; // BOOL // TRUE if the row was modified. // //*********************************************************** void CGrid::GetWbemServices(LPCTSTR szNamespace, VARIANT FAR* pvarUpdatePointer, VARIANT FAR* pvarServices, VARIANT FAR* pvarSc, VARIANT FAR* pvarUserCancel) { } void CGrid::SetColTagValue(int iCol, DWORD dwTagValue) { m_pcore->SetColTagValue(iCol, dwTagValue); } DWORD CGrid::GetColTagValue(int iCol) { return m_pcore->GetColTagValue(iCol); } void CGrid::SetRowTagValue(int iRow, DWORD dwTagValue) { m_pcore->SetRowTagValue(iRow, dwTagValue); } DWORD CGrid::GetRowTagValue(int iRow) { return m_pcore->GetRowTagValue(iRow); } void CGrid::SetColVisibility(int iCol, BOOL bVisible) { m_phdr->Header().SetColVisibility(iCol, bVisible); } void CGrid::SetNullCellDrawMode(BOOL bShowEmptyText) { m_pcore->SetNullCellDrawMode(bShowEmptyText); } BOOL CGrid::ShowNullAsEmpty() { return m_pcore->ShowNullAsEmpty(); } int CGrid::GetMaxValueWidth(int iCol) { return m_pcore->GetMaxValueWidth(iCol); } void CGrid::SwapRows(int iRow1, int iRow2, BOOL bRedraw) { m_pcore->SwapRows(iRow1, iRow2); if (bRedraw) { RedrawRow(iRow1); RedrawRow(iRow2); UpdateWindow(); } } void CGrid::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default int iSelectedRow; CWnd::OnChar(nChar, nRepCnt, nFlags); switch (nChar) { case VK_TAB: iSelectedRow = GetSelectedRow(); if (GetKeyState(VK_SHIFT) < 0) { // Tab forward should move to the next row. if (iSelectedRow == NULL_INDEX) { SelectRow(0); } else if (iSelectedRow < (GetRows() - 1)) { SelectRow(iSelectedRow + 1); } } else { // Shift tab moves the row up one. if (iSelectedRow == NULL_INDEX) { SelectRow(0); } else if (iSelectedRow > 0) { SelectRow(iSelectedRow - 1); } } return; } }