void CGridBtnCellCombo::OnEndEdit() { ASSERT( m_pBtnDataBase != NULL); m_ucEditing = FALSE; m_pBtnDataBase->SetEditWnd( NULL); // make sure that all pushbuttons appear in "up" state const int iCtlNbr = GetDrawCtlNbr(); for( int i1=0; i1 < iCtlNbr; i1++) { UINT uiType = GetDrawCtlType( i1); UINT uiState = GetDrawCtlState( i1); BOOL bIsScrollDown = ( uiType == DFC_SCROLL) && uiState & DFCS_SCROLLDOWN; BOOL bIsPushButton = ( uiType == DFC_BUTTON) && uiState & DFCS_BUTTONPUSH; if( bIsScrollDown || bIsPushButton) { ClickedCellCtl( WM_LBUTTONUP, // Command that invoked. e.g. WM_LBUTTONDOWN i1); // zero-based index of image to draw } } }
/***************************************************************************** May mean end of a button click sequence or to edit text *****************************************************************************/ BOOL CGridBtnCellCombo::Edit(int nRow, int nCol, CRect rect, CPoint point, UINT nID, UINT nChar) { int iCtlHit = RelPointInCtl( point); // Relative point coords // returns: Index of control that this point is within bounds of or -1 if no control matches BOOL bShowDropDownNow = FALSE; if( iCtlHit >= 0) { // if user clicked on scroll down button picture, then show // the drop-down, too UINT uiType = GetDrawCtlType( iCtlHit); UINT uiState = GetDrawCtlState( iCtlHit); bShowDropDownNow = ( uiType == DFC_SCROLL) && uiState & DFCS_SCROLLDOWN; } BOOL bHitNonComboButton = ( iCtlHit >= 0) && !bShowDropDownNow; if( HasCellText() && !bHitNonComboButton ) { m_ucEditing = TRUE; // InPlaceList auto-deletes itself CGridCtrl* pGrid = GetGrid(); CInPlaceList* pInPlaceList = new CInPlaceList( pGrid, rect, m_dwComboStyle, nID, nRow, nCol, GetTextClr(), GetBackClr(), m_StringArrayCombo, GetText(), nChar); if( bShowDropDownNow) { if( m_dwComboStyle & CBS_DROPDOWN || m_dwComboStyle & CBS_DROPDOWNLIST) { pInPlaceList->ShowDropDown( TRUE); } } m_pBtnDataBase->SetEditWnd( pInPlaceList); return TRUE; } // call base class, otherwise return CGridBtnCell::Edit( nRow, nCol, rect, point, nID, nChar); }
void CGridBtnCellCombo::OnClick( CPoint PointCellRelative) { // immediately edit if user clicked on scroll down button picture int iCtlHit = RelPointInCtl( PointCellRelative); // Relative point coords // returns: Index of control that this point is within bounds of or -1 if no control matches BOOL bHitScrollDown = FALSE; if( iCtlHit >= 0) { // if user clicked on scroll down button picture, then show // the drop-down, too UINT uiType = GetDrawCtlType( iCtlHit); UINT uiState = GetDrawCtlState( iCtlHit); bHitScrollDown = ( uiType == DFC_SCROLL) && uiState & DFCS_SCROLLDOWN; } if( bHitScrollDown) { // invoke the edit, now -- similar to CGridCtl::OnEditCell() logic CGridCtrl* pGrid = GetGrid(); ASSERT( pGrid != NULL); CRect rect; if (!pGrid->GetCellRect( m_iRow, m_iCol, rect)) return; SendMessageToParent(m_iRow, m_iCol, GVN_BEGINLABELEDIT); Edit( m_iRow, m_iCol, rect, PointCellRelative, IDC_INPLACE_CONTROL, VK_LBUTTON); return; } CGridBtnCell::OnClick( PointCellRelative); }
/***************************************************************************** Determines bounding rectangle to draw a button in a cell. Used to draw cells and for mouse hit testing. If a fixed-size control can't fit within the cell, the control will shrink in the required x and / or y dimension. I do this because I'm not using a clipping region when I display the cell -- that would be a better solution. *****************************************************************************/ BOOL CGridBtnCellBase::CalcDrawCtlRects(CRect* apRect, // returns: CRects with coordinates // last entry has optional leftover rect // available for text, etc. int aiNbrRectEntries, // nbr of Rects in above array const CRect& arRectCell)// cell rectangle to work with // returns: success / fail { ASSERT( apRect != NULL); if( aiNbrRectEntries < GetDrawCtlNbrMax() ) { ASSERT( FALSE); // need to allow for leftover rect return FALSE; } const int iCtlNbr = GetDrawCtlNbr(); if( iCtlNbr <= 0) return FALSE; int i1, i2; int iSpinBoxDownIdx = -1; // can have zero or 1 spin box -- no more // identifies placment of down arrow of the spin box int iWidth = 0; CTL_ALIGN CtlAlign; UINT uiType; UINT uiState; CRect* pRectSav = apRect; // calculate the width layout of buttons by examining // all of them and noting important info int iFixedSum = 0; int iSizeToFitCount = 0; for( i1=0; i1 < iCtlNbr; i1++) { CtlAlign = GetDrawCtlAlign( i1); if( CtlAlign == CTL_ALIGN_CENTER) { // forget all calculations if any are centered, all controls // just overwrite each other and expand to fit cell for( i2=0; i2 < iCtlNbr; i2++) { apRect->operator=( arRectCell); // copy initial rectangle apRect++; } apRect->operator=( CRect(0,0,0,0) ); // no text leftover return TRUE; } iWidth = GetDrawCtlWidth( i1); if( iWidth > 0) iFixedSum += iWidth; else iSizeToFitCount++; // spin box rectangles are stacked on top of each other // thus, avoid doubling spin box width if( iSpinBoxDownIdx < 0) { uiState = GetDrawCtlState( i1); if( GetDrawCtlType( i1) == DFC_SCROLL && !( uiState & (DFCS_SCROLLCOMBOBOX | DFCS_SCROLLDOWN | DFCS_SCROLLLEFT | DFCS_SCROLLRIGHT | DFCS_SCROLLSIZEGRIP) ) ) // checking for DFCS_SCROLLUP // but it is not a bit, it is 0x0! { if( i1 + 1 < iCtlNbr) { // at least 1 more -- see if we got spin box match if( GetDrawCtlType( i1 + 1) == DFC_SCROLL && ( GetDrawCtlState( i1 + 1) & DFCS_SCROLLDOWN) ) { // it's a spin box i1++; // skip looking at next control iSpinBoxDownIdx = i1; } } } } } int iSizeToFitWidth = 0; int iFitWidthsTotal = arRectCell.Width() - iFixedSum; if( iSizeToFitCount > 0) { iSizeToFitWidth = iFitWidthsTotal / iSizeToFitCount; } int iSizeToFitCountWrk = iSizeToFitCount; int iWidthSoFar = 0; for( i1=0; i1 < iCtlNbr; i1++) { if( iSpinBoxDownIdx == i1) { // skip down arrow of spin box for calculations apRect++; continue; } apRect->operator=( arRectCell); // copy initial rectangle apRect->left += iWidthSoFar; apRect->right = apRect->left; iWidth = GetDrawCtlWidth( i1); if( iWidth > 0) iWidthSoFar += iWidth; // fixed width else { iSizeToFitCountWrk--; // found another one // may shrink width if control is square -- saves screen real-estate uiType = GetDrawCtlType( i1); uiState = GetDrawCtlState( i1); BOOL bIsRectangle = ( uiType == DFC_BUTTON && (uiState & DFCS_BUTTONPUSH) == DFCS_BUTTONPUSH); // all other buttons are square if( !bIsRectangle && arRectCell.Height() < iSizeToFitWidth) { // it is square -- make width the cell height iWidthSoFar += arRectCell.Height(); // recalulate size to fit iFitWidthsTotal -= arRectCell.Height(); if( iSizeToFitCountWrk > 0) iSizeToFitWidth = iFitWidthsTotal / iSizeToFitCountWrk; } else { iWidthSoFar += iSizeToFitWidth; iFitWidthsTotal -= iSizeToFitWidth; } } apRect->right = iWidthSoFar + arRectCell.left; apRect++; } if( iFitWidthsTotal < (int)GetMargin() ) { // no leftover rectangle available apRect->SetRectEmpty(); } else { // calc leftover rectangle apRect->operator=( arRectCell); apRect->left += iWidthSoFar; } // I've been assuming that all controls are left-aligned. Programmer // may have defined controls as right-aligned, too. Note that left // and right controls can be declared in the array in any order // Ok, here's the kludge. Since I know that the heights of each control // are the same, use these unused height values in the CRect array to // help me calculate the proper order of the widths apRect = pRectSav; int iSavedTop = arRectCell.left; // seed first result for( i1=0; i1 < iCtlNbr; i1++) { if( iSpinBoxDownIdx == i1) { // skip down arrow of spin box for calculations apRect++; continue; } // analyzing just Left-aligned controls CtlAlign = GetDrawCtlAlign( i1); ASSERT( CtlAlign != CTL_ALIGN_CENTER); // should've taken care of if( CtlAlign == CTL_ALIGN_LEFT) { apRect->top = iSavedTop; iSavedTop += apRect->Width(); apRect->bottom = iSavedTop; } apRect++; } // text rectangle appears between left and right aligned controls // and width has been saved in last rectangle apRect = pRectSav + iCtlNbr; apRect->top = iSavedTop + GetMargin(); // put some margin so doesn't overwrite iSavedTop += apRect->Width(); apRect->bottom = iSavedTop; iSavedTop += GetMargin(); // some more margin... apRect = pRectSav; for( i1=0; i1 < iCtlNbr; i1++) { if( iSpinBoxDownIdx == i1) { // skip down arrow of spin box for calculations apRect++; continue; } // finally, look at right-aligned controls CtlAlign = GetDrawCtlAlign( i1); if( CtlAlign == CTL_ALIGN_RIGHT) { apRect->top = iSavedTop; iSavedTop += apRect->Width(); apRect->bottom = iSavedTop; } apRect++; } // flip everything back and we're done apRect = pRectSav; for( i1=0; i1 <= iCtlNbr; i1++) // note that I'll get leftover rect, too { apRect->left = apRect->top; if( apRect->left < arRectCell.left ) apRect->left = arRectCell.left; // can't go beyond cell if( apRect->left > arRectCell.right ) apRect->left = arRectCell.right; apRect->right = apRect->bottom; if( apRect->right > arRectCell.right) apRect->right = arRectCell.right; if( apRect->right < arRectCell.left) apRect->right = arRectCell.left; apRect->bottom = arRectCell.bottom; apRect->top = arRectCell.top; apRect++; } // but wait -- special calculations for the spin box if( iSpinBoxDownIdx >= 0) { CRect* pRectSpinUp = pRectSav + iSpinBoxDownIdx - 1; apRect = pRectSav + iSpinBoxDownIdx; apRect->left = pRectSpinUp->left; apRect->right = pRectSpinUp->right; apRect->bottom = pRectSpinUp->bottom; int iHalf = apRect->top + ( (apRect->bottom - apRect->top) / 2); apRect->top = iHalf; pRectSpinUp->bottom = iHalf; } return TRUE; }
/***************************************************************************** Called during all the mouse events associated with clicking a control embedded within a cell. Override to have more elaborate handling like implementing radio button logic. *****************************************************************************/ BOOL CGridBtnCellBase::ClickedCellCtl( UINT uMsg, // Command that invoked. e.g. WM_LBUTTONDOWN int aiWhich) // zero-based index of image to draw // returns: T=redraw occurred / F=no redraw { if( aiWhich < 0 || aiWhich >= GetDrawCtlNbrMax() ) { ASSERT( FALSE); return FALSE; } UINT uiState = GetDrawCtlState( aiWhich); if( uiState & DFCS_INACTIVE) return FALSE; // button is inactive -- don't do anything m_sLastCtlClicked = (short)aiWhich; UINT iType = GetDrawCtlType( aiWhich); switch( uMsg) { case WM_LBUTTONDOWN: // appears pushed in uiState |= DFCS_PUSHED; SetDrawCtlState( aiWhich, uiState); break; case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: // appears pushed out uiState &= (~DFCS_PUSHED); // auto check / uncheck controls, too if( iType == DFC_BUTTON ) { BOOL bIsMbrRadioGrp = GetDrawCtlIsMbrRadioGrp( aiWhich); if( uiState & DFCS_BUTTONRADIO || bIsMbrRadioGrp ) { // radio buttons or any button flagged as being part // of a radio group will be made to look pressed down // while pushing-up / unchecking all other members // of the radio group const int iCtlNbr = GetDrawCtlNbr(); UINT uiStateRadio; for( int i1=0; i1 < iCtlNbr; i1++) { if( i1 != aiWhich) { uiStateRadio = GetDrawCtlState( i1); bIsMbrRadioGrp = GetDrawCtlIsMbrRadioGrp( i1); if( uiStateRadio & DFCS_BUTTONRADIO || bIsMbrRadioGrp ) { uiStateRadio &= (~( DFCS_PUSHED | DFCS_CHECKED) ); // push out and uncheck SetDrawCtlState( i1, uiStateRadio); } } } uiState |= DFCS_CHECKED; // check if( !(uiState & DFCS_BUTTONRADIO) ) uiState |= DFCS_PUSHED; // press in if not real radio button } else if( !( uiState & ALL_BUT_BTN_CHK) ) { // not a pushbutton -- it's a check box // (can't check for DFCS_BUTTONCHECK directly since it is bit 0) if( uiState & DFCS_CHECKED) uiState &= (~DFCS_CHECKED); // uncheck else uiState |= DFCS_CHECKED; // check } } SetDrawCtlState( aiWhich, uiState); break; default: ASSERT( FALSE); // gotta handle new message return FALSE; } CGridCtrl* pGrid = GetGrid(); ASSERT( pGrid); pGrid->RedrawCell( m_iRow, m_iCol); return TRUE; }
BOOL CGridBtnCellBase::DrawBtnCell(CDC* pDC, int /* nRow */, int /* nCol */, CRect* prect, BOOL /* bEraseBkgnd */) { CGridCtrl* pGrid = GetGrid(); if (!pGrid || !pDC) return FALSE; if( prect->Width() <= 0 || prect->Height() <= 0) // prevents imagelist item from drawing even return FALSE; // though cell is hidden int nSavedDC = pDC->SaveDC(); // draw any button images ASSERT( MAX_NBR_CTLS_INCELL > GetDrawCtlNbrMax() ); // whoa! const int iCtlNbr = GetDrawCtlNbr(); CRect RectAry[ MAX_NBR_CTLS_INCELL]; if( iCtlNbr > 0) { if( GetState() & GVIS_SELECTED || GetState() & GVIS_DROPHILITED ) { // draw the rectangle around the grid -- // we may be filling cells with controls pDC->SelectStockObject(BLACK_PEN); pDC->SelectStockObject(NULL_BRUSH); pDC->Rectangle(*prect); } pDC->SetBkMode(TRANSPARENT); prect->DeflateRect( GetMargin(), 0); CFont* pOldFont = pDC->SelectObject(GetFontObject()); if( CalcDrawCtlRects( RectAry, // returns: CRects with coordinates // last entry has optional leftover rect // available for text, etc. MAX_NBR_CTLS_INCELL,// nbr of Rects in above array *prect) ) // cell rectangle to work with { for( int i1=0; i1 < iCtlNbr; i1++) { UINT uiType = GetDrawCtlType( i1); UINT uiState = GetDrawCtlState( i1); pDC->DrawFrameControl( RectAry[ i1], uiType, uiState); // if button has text, draw it, too const char* pszBtnText = GetDrawCtlBtnText( i1); if( pszBtnText != NULL) { COLORREF ColorCurrent = pDC->GetTextColor(); if( uiState & DFCS_INACTIVE) { // button is grayed-out // draw the text so that it matches MS's look RectAry[ i1].OffsetRect( 1, 1); pDC->SetTextColor( RGB( 255,255,255) ); pDC->DrawText( pszBtnText, -1, RectAry[ i1], DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS); RectAry[ i1].OffsetRect( -1, -1); pDC->SetTextColor( ::GetSysColor(COLOR_GRAYTEXT)); } else { pDC->SetTextColor( ::GetSysColor(COLOR_BTNTEXT)); } pDC->DrawText( pszBtnText, -1, RectAry[ i1], DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS); pDC->SetTextColor( ColorCurrent); } } // allowable text area has shrunk up *prect = RectAry[ iCtlNbr]; } pDC->SelectObject(pOldFont); // maybe there's nothing left to draw if( prect->Width() <= 0) { pDC->RestoreDC(nSavedDC); return TRUE; } } pDC->RestoreDC(nSavedDC); return TRUE; }