//------------------------------------------------------------------------ //! Check if current image index blocks for editing of cell label //! //! @param owner The list control starting edit //! @param nRow The index of the row for the cell //! @param nCol The index of the column for the cell //! @param pt The position clicked, in client coordinates. //! @return Is cell read only ? (true / false) //------------------------------------------------------------------------ bool CGridColumnTraitImage::IsCellReadOnly(CGridListCtrlEx& owner, int nRow, int nCol, CPoint pt) const { if (!m_ColumnState.m_Editable) return true; // Check if current cell image blocks for starting cell editor if (m_ImageCellEdit.GetSize() != 0) { int nCurImageIdx = -1; for (int i = 0; i < m_ImageCellEdit.GetSize(); ++i) { if (!m_ImageCellEdit.GetValueAt(i)) { if (nCurImageIdx == -1) { if (pt != CPoint(-1, -1)) { CRect rect; VERIFY(owner.GetCellRect(nRow, nCol, LVIR_LABEL, rect)); if (!rect.PtInRect(pt)) break; } nCurImageIdx = owner.GetCellImage(nRow, nCol); if (nCurImageIdx == -1) break; } if (nCurImageIdx == m_ImageCellEdit.GetKeyAt(i)) return true; } } } return false; // editable }
//------------------------------------------------------------------------ //! Returns the proper rectangle, which an editor should fit in //------------------------------------------------------------------------ CRect CGridColumnTraitText::GetCellEditRect(CGridListCtrlEx& owner, int nRow, int nCol) { // Find the required height according to font int requiredHeight = GetCellFontHeight(owner); // Get position of the cell to edit CRect rectCell; VERIFY( owner.GetCellRect(nRow, nCol, LVIR_LABEL, rectCell) ); // Adjust position to font height if (!owner.UsingVisualStyle()) { if ((requiredHeight + 2*::GetSystemMetrics(SM_CXEDGE)) > rectCell.Height()) { rectCell.top -= ::GetSystemMetrics(SM_CXEDGE); rectCell.bottom += ::GetSystemMetrics(SM_CXEDGE); } } if (owner.GetExtendedStyle() & LVS_EX_GRIDLINES) { if ((requiredHeight + 2*::GetSystemMetrics(SM_CXEDGE) + ::GetSystemMetrics(SM_CXBORDER)) < rectCell.Height()) rectCell.bottom -= ::GetSystemMetrics(SM_CXBORDER); } if (owner.GetExtendedStyle() & LVS_EX_SUBITEMIMAGES) { if (owner.GetImageList(LVSIL_SMALL)!=NULL && owner.GetCellImage(nRow,nCol)>=0) rectCell.left += ::GetSystemMetrics(SM_CXBORDER); } return rectCell; }
//------------------------------------------------------------------------ //! Returns the proper rectangle, which a cell value editor should fit in //! //! @param owner The list control for the inplace cell value editor //! @param nRow The index of the row //! @param nCol The index of the column //! @return Rectangle where the inplace cell value editor should be placed. //------------------------------------------------------------------------ CRect CGridColumnTraitText::GetCellEditRect(CGridListCtrlEx& owner, int nRow, int nCol) { // Get position of the cell to edit CRect rectCell; VERIFY( owner.GetCellRect(nRow, nCol, LVIR_LABEL, rectCell) ); // Adjust cell rectangle according to grid-lines if (owner.GetExtendedStyle() & LVS_EX_GRIDLINES) rectCell.bottom -= ::GetSystemMetrics(SM_CXBORDER); if (owner.GetExtendedStyle() & LVS_EX_SUBITEMIMAGES) { // Add margin to cell image if (owner.GetImageList(LVSIL_SMALL)!=NULL && owner.GetCellImage(nRow,nCol)!=I_IMAGECALLBACK) rectCell.left += ::GetSystemMetrics(SM_CXBORDER); } // Check if there is enough room for normal margin int requiredHeight = GetCellFontHeight(owner); requiredHeight += 2*::GetSystemMetrics(SM_CXEDGE); if (requiredHeight > rectCell.Height()) rectCell.bottom = rectCell.top + requiredHeight; return rectCell; }
//------------------------------------------------------------------------ //! Returns dimensions of the cell text clicked //! //! @param owner The list control being clicked //! @param nRow The index of the row //! @param nCol The index of the column //! @param cellText The contents of the cell clicked //! @return The dimensions of the cell text //------------------------------------------------------------------------ CRect CGridColumnTraitHyperLink::GetTextRect(CGridListCtrlEx& owner, int nRow, int nCol, const CString& cellText) { CRect rect; ASSERT(nRow != -1); CDC* pDC = owner.GetDC(); CFont* pOldFont = pDC->SelectObject(owner.GetCellFont()); CSize size = pDC->GetTextExtent(cellText); pDC->SelectObject(pOldFont); owner.ReleaseDC(pDC); owner.GetCellRect(nRow, nCol, LVIR_LABEL, rect); HDITEM hditem = { 0 }; hditem.mask = HDI_FORMAT; owner.GetHeaderCtrl()->GetItem(nCol, &hditem); // First item (Label) doesn't have a margin (Subitems does) if (nCol != 0 && !(hditem.fmt & HDF_CENTER)) { if (hditem.fmt & HDF_RIGHT) rect.OffsetRect(-7, 0); else rect.OffsetRect(4, 0); } if (hditem.fmt & HDF_CENTER) rect.DeflateRect((rect.Width() - size.cx) / 2, 0); else if (hditem.fmt & HDF_RIGHT) rect.left = rect.right - size.cx; else rect.right = rect.left + size.cx; return rect; }
//------------------------------------------------------------------------ //! Checks if the mouse click should start the cell editor (OnEditBegin) //! Normally the cell needs to have focus first before cell editor can be started //! - Except when using ToggleSelection, and have clicked a checkbox (image) //! - Except when using SingleClickEdit, which makes it impossible to do double click //! //! @param owner The list control being clicked //! @param nRow The index of the row //! @param nCol The index of the column //! @param pt The position clicked, in client coordinates. //! @param bDblClick Whether the position was double clicked //! @return How should the cell editor be started (0 = No editor, 1 = Start Editor, 2 = Start Editor and block click-event) //------------------------------------------------------------------------ int CGridColumnTraitImage::OnClickEditStart(CGridListCtrlEx& owner, int nRow, int nCol, CPoint pt, bool bDblClick) { // Begin edit if the cell has focus already bool startEdit = false; if (nRow != -1 && nCol != -1 && !bDblClick) { if (m_SingleClickEdit) startEdit = true; else if (owner.GetFocusRow() == nRow && owner.GetFocusCell() == nCol) startEdit = true; } // Check if the cell-image / cell-checkbox can be edited without having focus first if (m_ToggleSelection) { if (nCol == 0 && owner.GetExtendedStyle() & LVS_EX_CHECKBOXES) { CRect iconRect; if (!owner.GetCellRect(nRow, nCol, LVIR_ICON, iconRect) || !iconRect.PtInRect(pt)) { CRect labelRect; if (owner.GetCellRect(nRow, nCol, LVIR_LABEL, labelRect) && !labelRect.PtInRect(pt)) return 1; // Clicked the checkbox for the label-column } } if (m_ImageCellEdit.GetSize()>1) { CRect iconRect; if (owner.GetCellRect(nRow, nCol, LVIR_ICON, iconRect) && iconRect.PtInRect(pt)) return 2; // Clicked the image-icon (Don't change focus or change selection) } } return startEdit ? 1 : 0; }
//------------------------------------------------------------------------ //! Checks if the mouse click should start the cell editor (OnEditBegin) //! Validates that the click was on the text-link within the label-part //! //! @param owner The list control being clicked //! @param nRow The index of the row //! @param nCol The index of the column //! @param pt The position clicked, in client coordinates. //! @param bDblClick Whether the position was double clicked //! @return How should the cell editor be started (0 = No editor, 1 = Start Editor, 2 = Start Editor and block click-event) //------------------------------------------------------------------------ int CGridColumnTraitHyperLink::OnClickEditStart(CGridListCtrlEx& owner, int nRow, int nCol, CPoint pt, bool bDblClick) { int startEdit = CGridColumnTraitImage::OnClickEditStart(owner, nRow, nCol, pt, bDblClick); if (startEdit) { // Check if mouse click was inside the label-part of the cell CRect labelRect; if (owner.GetCellRect(nRow, nCol, LVIR_LABEL, labelRect) && labelRect.PtInRect(pt)) { // Check if mouse click was inside the text-link of the cell CString cellText = owner.GetItemText(nRow, nCol); if (GetTextRect(owner, nRow, nCol, cellText).PtInRect(pt)) return startEdit; else return 0; } } return startEdit; }
//------------------------------------------------------------------------ //! Overrides the custom draw handler, to allow custom coloring of rows. //! - Fix white background for icon images //! - Fix white background between icon and cell text //! //! @param owner The list control drawing //! @param pLVCD Pointer to NMLVCUSTOMDRAW structure //! @param pResult Modification to the drawing stage (CDRF_NEWFONT, etc.) //------------------------------------------------------------------------ void CGridRowTraitXP::OnCustomDraw(CGridListCtrlEx& owner, NMLVCUSTOMDRAW* pLVCD, LRESULT* pResult) { if (owner.UsingVisualStyle()) { // Perform standard drawing CGridRowTraitText::OnCustomDraw(owner, pLVCD, pResult); return; } // We are using classic- or XP-style int nRow = (int)pLVCD->nmcd.dwItemSpec; // Repair the standard drawing switch (pLVCD->nmcd.dwDrawStage) { case CDDS_ITEMPREPAINT | CDDS_SUBITEM: { // We want to fix cell images *pResult |= CDRF_NOTIFYPOSTPAINT; } break; case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: { // Fix CListCtrl selection drawing bug with white background for icon image // Fix CListCtrl selection drawing bug with white margin between icon and text int nCol = pLVCD->iSubItem; if (CRect(pLVCD->nmcd.rc)==CRect(0,0,0,0)) break; int nImage = owner.GetCellImage(nRow, nCol); if (nImage == I_IMAGECALLBACK) break; CImageList* pImageList = owner.GetImageList(LVSIL_SMALL); if (pImageList==NULL) break; COLORREF backColor = COLORREF(-1); if (owner.GetExtendedStyle() & LVS_EX_TRACKSELECT && owner.GetHotItem()==nRow) { #if(WINVER >= 0x0500) backColor = ::GetSysColor(COLOR_HOTLIGHT); #else if (owner.IsRowSelected(nRow)) backColor = ::GetSysColor(COLOR_HIGHLIGHT); else break; #endif } else if (owner.IsRowSelected(nRow)) { if (!(owner.GetExtendedStyle() & LVS_EX_FULLROWSELECT)) break; // No drawing of selection color without full-row-select if (m_InvertCellSelection && owner.GetFocusRow()==nRow && owner.GetFocusCell()==nCol) { // No drawing of selection color for focus cell if (pLVCD->clrTextBk > RGB(255,255,255)) break; backColor = pLVCD->clrTextBk; } else { if (owner.GetFocus()!=&owner && !owner.IsCellEditorOpen()) { // Selection color is different when not having focus if (owner.GetStyle() & LVS_SHOWSELALWAYS) backColor = ::GetSysColor(COLOR_BTNFACE); else break; // no drawing of selection color when not in focus } else { backColor = ::GetSysColor(COLOR_HIGHLIGHT); } } } else { // Redraw with the given background color if (pLVCD->clrTextBk > RGB(255,255,255)) break; // If a color is more than white, then it is invalid backColor = pLVCD->clrTextBk; } CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); CRect rcIcon, rcCell; VERIFY( owner.GetCellRect(nRow, nCol, LVIR_ICON, rcIcon) ); VERIFY( owner.GetCellRect(nRow, nCol, LVIR_BOUNDS, rcCell) ); // When the label column is placed first it has a left-margin if (nCol==0 && nCol==owner.GetFirstVisibleColumn()) { int cxborder = ::GetSystemMetrics(SM_CXBORDER); rcCell.left += cxborder*2; } // Remove white margin between cell-image and cell-text rcCell.right = rcIcon.right + 2; CBrush brush(backColor); pDC->FillRect(&rcCell, &brush); // Draw icon COLORREF oldBkColor = pImageList->SetBkColor(backColor); pImageList->Draw ( pDC, nImage, rcIcon.TopLeft(), ILD_NORMAL ); pImageList->SetBkColor(oldBkColor); if (nCol==0 && owner.GetExtendedStyle() & LVS_EX_CHECKBOXES) { CImageList* pStateImageList = owner.GetImageList(LVSIL_STATE); if (pImageList==NULL) break; int checkState = owner.GetCheck(nRow); COLORREF oldStateBkColor = pStateImageList->SetBkColor(backColor); pStateImageList->Draw ( pDC, checkState, rcCell.TopLeft(), ILD_NORMAL ); pStateImageList->SetBkColor(oldStateBkColor); } } break; } // Perform standard drawing CGridRowTraitText::OnCustomDraw(owner, pLVCD, pResult); }
//------------------------------------------------------------------------ //! Overrides the custom draw handler, to allow custom coloring of rows. //! - Focus rectangle display //! - Use font size to increase row-height, but keep cell font-size //! - Alternate row coloring //! //! @param owner The list control drawing //! @param pLVCD Pointer to NMLVCUSTOMDRAW structure //! @param pResult Modification to the drawing stage (CDRF_NEWFONT, etc.) //------------------------------------------------------------------------ void CGridRowTraitText::OnCustomDraw(CGridListCtrlEx& owner, NMLVCUSTOMDRAW* pLVCD, LRESULT* pResult) { int nRow = (int)pLVCD->nmcd.dwItemSpec; switch (pLVCD->nmcd.dwDrawStage) { case CDDS_ITEMPREPAINT | CDDS_SUBITEM: { // Remove the selection color for the focus cell, to make it easier to see focus if (m_InvertCellSelection) { int nCol = pLVCD->iSubItem; if (pLVCD->nmcd.uItemState & CDIS_SELECTED) { if (owner.GetFocusCell()==nCol && owner.GetFocusRow()==nRow) { pLVCD->nmcd.uItemState &= ~CDIS_SELECTED; } } } // Bug in Vista causes the cell color from previous cell to be used in the next // even if having reverted the cell coloring in subitem-post-paint if (pLVCD->clrText <= RGB(255,255,255) || pLVCD->clrTextBk <= RGB(255,255,255)) { pLVCD->clrText = CLR_DEFAULT; pLVCD->clrTextBk = CLR_DEFAULT; if (UpdateTextColor(nRow, pLVCD->clrText)) *pResult |= CDRF_NEWFONT; if (UpdateBackColor(nRow, pLVCD->clrTextBk)) *pResult |= CDRF_NEWFONT; if (owner.OnDisplayRowColor(nRow, pLVCD->clrText, pLVCD->clrTextBk)) *pResult |= CDRF_NEWFONT; } } break; // Before painting a row case CDDS_ITEMPREPAINT: { if (UpdateTextColor(nRow, pLVCD->clrText)) *pResult |= CDRF_NEWFONT; if (UpdateBackColor(nRow, pLVCD->clrTextBk)) *pResult |= CDRF_NEWFONT; if (owner.OnDisplayRowColor(nRow, pLVCD->clrText, pLVCD->clrTextBk)) *pResult |= CDRF_NEWFONT; LOGFONT newFont = {0}; if (owner.OnDisplayRowFont(nRow, newFont)) { // New font provided CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); CFont* pNewFont = new CFont; VERIFY( pNewFont->CreateFontIndirect(&newFont) ); m_pOldFont = pDC->SelectObject(pNewFont); m_FontAllocated = true; *pResult |= CDRF_NOTIFYPOSTPAINT; // We need to restore the original font *pResult |= CDRF_NEWFONT; } else { if (owner.GetFont()!=owner.GetCellFont()) { // Using special cell font because of SetMargin() CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); m_pOldFont = pDC->SelectObject(owner.GetCellFont()); *pResult |= CDRF_NOTIFYPOSTPAINT; // We need to restore the original font *pResult |= CDRF_NEWFONT; } } if (pLVCD->nmcd.uItemState & CDIS_FOCUS) { if (owner.GetFocus() != &owner) break; // If drawing focus row, then remove focus state and request to draw it later // - Row paint request can come twice, with and without focus flag // - Only respond to the one with focus flag, else DrawFocusRect XOR will cause solid or blank focus-rectangle if (owner.GetFocusRow() == nRow) { if (owner.GetFocusCell() >= 0) { // We want to draw a cell-focus-rectangle instead of row-focus-rectangle pLVCD->nmcd.uItemState &= ~CDIS_FOCUS; *pResult |= CDRF_NOTIFYPOSTPAINT; } else if (owner.GetExtendedStyle() & LVS_EX_GRIDLINES) { // Avoid bug where bottom of focus rectangle is missing when using grid-lines // - Draw the focus-rectangle for the entire row (explicit) pLVCD->nmcd.uItemState &= ~CDIS_FOCUS; *pResult |= CDRF_NOTIFYPOSTPAINT; } } } } break; // After painting a row case CDDS_ITEMPOSTPAINT: { if (m_pOldFont!=NULL) { // Restore the original font CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); CFont* pNewFont = pDC->SelectObject(m_pOldFont); if (m_FontAllocated) { m_FontAllocated = false; delete pNewFont; } m_pOldFont = NULL; } if (CRect(pLVCD->nmcd.rc)==CRect(0,0,0,0)) break; if (owner.GetFocusRow()!=nRow) break; if (owner.GetFocus() != &owner) break; // Perform the drawing of the focus rectangle if (owner.GetFocusCell() >= 0) { // Draw the focus-rectangle for a single-cell CRect rcHighlight; CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); VERIFY( owner.GetCellRect(nRow, owner.GetFocusCell(), LVIR_BOUNDS, rcHighlight) ); int cxborder = ::GetSystemMetrics(SM_CXBORDER); // When the label column is placed first it has a left-margin if (owner.GetFocusCell()==0 && owner.GetFocusCell()==owner.GetFirstVisibleColumn()) { rcHighlight.left += cxborder*2; } else // Prevent focus rectangle to overlap with cell-image (Only room for this when not first column) if (owner.GetFirstVisibleColumn()!=owner.GetFocusCell()) { rcHighlight.left -= cxborder; } // Adjust rectangle according to grid-lines if (owner.GetExtendedStyle() & LVS_EX_GRIDLINES) { rcHighlight.bottom -= cxborder; } pDC->DrawFocusRect(rcHighlight); } else if (owner.GetExtendedStyle() & LVS_EX_GRIDLINES) { // Avoid bug where bottom of focus rectangle is missing when using grid-lines // - Draw the focus-rectangle for the entire row (explicit) CRect rcHighlight; CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); // Using LVIR_BOUNDS to get the entire row-rectangle VERIFY( owner.GetItemRect(nRow, rcHighlight, LVIR_BOUNDS) ); // Adjust rectangle according to grid-lines int cxborder = ::GetSystemMetrics(SM_CXBORDER); rcHighlight.bottom -= cxborder; pDC->DrawFocusRect(rcHighlight); } } break; } }