/******************************************************************** created: 2005/06/03 created: 3:6:2005 13:22 filename: x:\Software\Mfc\Source\Controls\Buttons\RoundButton2.cpp file path: x:\Software\Mfc\Source\Controls\Buttons file base: RoundButton2 file ext: cpp author: Markus Zocholl purpose: CRoundButton2 defines a universal Button-Control with the following features: * Shape is a rounded Rectangle * Button includes Border and Button-Face * Many parameters to get an individual look * Functions of Button to be en- or disabled: - Button (disabled means a static control with userdefined styles) - Hover *********************************************************************/ #include "StdAfx.h" #include #include ".\RoundButton2.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /************************************************************************/ /* Construction and Destruction */ /************************************************************************/ //! Construction CRoundButton2::CRoundButton2(void): m_bDefaultButton(false) , m_bIsCheckButton(false) , m_bIsRadioButton(false) , m_bIsHotButton(false) , m_bMouseOnButton(false) , m_bIsChecked(false) , m_ptRoundButtonStyle(NULL) , m_rBtnSize(CRect(0, 0, 0, 0)) , m_bRedraw(false) , m_sOldCaption(_T("")) { // Set Standards in Font-Style m_tLogFont.lfHeight = 16; m_tLogFont.lfWidth = 0; m_tLogFont.lfEscapement = 0; m_tLogFont.lfOrientation = 0; m_tLogFont.lfWeight = FW_BOLD; m_tLogFont.lfItalic = false; m_tLogFont.lfUnderline = false; m_tLogFont.lfStrikeOut = false; m_tLogFont.lfCharSet = DEFAULT_CHARSET; m_tLogFont.lfOutPrecision = OUT_DEFAULT_PRECIS; m_tLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; m_tLogFont.lfQuality = ANTIALIASED_QUALITY; m_tLogFont.lfPitchAndFamily = DEFAULT_PITCH; //strcpy(m_tLogFont.lfFaceName, "Tahoma"); lstrcpy(m_tLogFont.lfFaceName, _T("Tahoma")); m_tBtnFont.CreateFontIndirect(&m_tLogFont); // Set Standard Font-Color m_tTextColor.m_tDisabled = RGB(64, 64, 64); m_tTextColor.m_tEnabled = RGB( 0, 0, 0); m_tTextColor.m_tClicked = RGB( 0, 0, 0); m_tTextColor.m_tPressed = RGB( 0, 0, 0); m_tTextColor.m_tHot = RGB( 0, 0, 0); } //! Destruction CRoundButton2::~CRoundButton2(void) { } /************************************************************************/ /* public Functions */ /************************************************************************/ // Set Style of Button bool CRoundButton2::SetRoundButtonStyle(CRoundButtonStyle* _ptRoundButtonStyle) { // Check, if Button-Style is given if (_ptRoundButtonStyle == NULL) return false; // Set Pointer to ButtonStyle m_ptRoundButtonStyle = _ptRoundButtonStyle; // Redraw Button m_bRedraw = true; // All Done return false; } bool CRoundButton2::SetFont(CFont* _ptCFont) { if (_ptCFont == NULL) return false; LOGFONT logFont; _ptCFont->GetLogFont(&logFont); return SetFont(&logFont); } // Set Font of Button bool CRoundButton2::SetFont(LOGFONT* _ptLogFont) { if (_ptLogFont == NULL) return false; // Delete Font, if already given if (m_tBtnFont.m_hObject != NULL) m_tBtnFont.DeleteObject(); // Store Infos local memcpy(&m_tLogFont, _ptLogFont, sizeof(LOGFONT)); // Create new Font m_tBtnFont.CreateFontIndirect(&m_tLogFont); // Button should be redrawn m_bRedraw = true; return true; } // Set Font of Button bool CRoundButton2::GetFont(LOGFONT* _ptLogFont) { if (_ptLogFont == NULL) return false; // Store Infos local memcpy(_ptLogFont, &m_tLogFont, sizeof(LOGFONT)); return true; } //! Set Color of Caption bool CRoundButton2::SetTextColor(tColorScheme* _ptTextColor) { if (_ptTextColor == NULL) return false; // Store Infos locally memcpy(&m_tTextColor, _ptTextColor, sizeof(tColorScheme)); // Button should be redrawn m_bRedraw = true; return true; } //! Get Color of Caption bool CRoundButton2::GetTextColor(tColorScheme* _ptTextColor) { if (_ptTextColor == NULL) return false; // Store Infos locally memcpy(_ptTextColor, &m_tTextColor, sizeof(tColorScheme)); return true; } /************************************************************************/ /* Own Drawing-Functions */ /************************************************************************/ //! Generate Bitmaps to hold Buttons void CRoundButton2::GenButtonBMPs(CDC* _pDC, CRect _rRect) { if (m_tBmpBtn.m_hObject != NULL) m_tBmpBtn.DeleteObject(); m_tBmpBtn.m_hObject = NULL; // Generate Bitmap if (m_tBmpBtn.CreateCompatibleBitmap(_pDC, _rRect.Width(), _rRect.Height() * BS_LAST_STATE) == FALSE) { m_rBtnSize = CRect(0, 0, 0, 0); } else { m_rBtnSize = _rRect; } } //! Draw Button-Face void CRoundButton2::DrawButtonFace(CDC* _pDC) { // We need an attached style if (m_ptRoundButtonStyle == NULL) return; // Get Pointer to Bitmap of Masks CBitmap* pButtonMasks = m_ptRoundButtonStyle->GetButtonEdge(_pDC); // Create Memory-DC CDC SourceDC; SourceDC.CreateCompatibleDC(_pDC); // Select Working Objects into DCs HGDIOBJ hOldBmp1 = SourceDC.SelectObject(pButtonMasks); int nState; CSize tEdgeSize = m_ptRoundButtonStyle->GetEdgeSize(); CSize tCorrectedEdgeSize; CSize tMaskSize = m_ptRoundButtonStyle->GetMaskSize(); // Correct Edge-Size for smaller Buttons tCorrectedEdgeSize.cx = __min(tEdgeSize.cx, __min(m_rBtnSize.Width() / 2, m_rBtnSize.Height() / 2)); tCorrectedEdgeSize.cy = tCorrectedEdgeSize.cx; for (nState = 0; nState < BS_LAST_STATE; nState++) { /************************************************************************/ /* Draw Edges */ /************************************************************************/ // Left-Top _pDC->StretchBlt( 0, nState * m_rBtnSize.Height(), tCorrectedEdgeSize.cx, tCorrectedEdgeSize.cy, &SourceDC, 0, nState * tMaskSize.cy, tEdgeSize.cx, tEdgeSize.cy, SRCCOPY); // Left-Bottom _pDC->StretchBlt( 0, nState * m_rBtnSize.Height() + m_rBtnSize.Height() - tCorrectedEdgeSize.cy, tCorrectedEdgeSize.cx, tCorrectedEdgeSize.cy, &SourceDC, 0, nState * tMaskSize.cy + tMaskSize.cy - tEdgeSize.cy, tEdgeSize.cx, tEdgeSize.cy, SRCCOPY); // Right-Top _pDC->StretchBlt( m_rBtnSize.Width() - tCorrectedEdgeSize.cx, nState * m_rBtnSize.Height(), tCorrectedEdgeSize.cx, tCorrectedEdgeSize.cy, &SourceDC, tMaskSize.cx - tEdgeSize.cx, nState * tMaskSize.cy, tEdgeSize.cx, tEdgeSize.cy, SRCCOPY); // Right-Bottom _pDC->StretchBlt( m_rBtnSize.Width() - tCorrectedEdgeSize.cx, nState * m_rBtnSize.Height() + m_rBtnSize.Height() - tCorrectedEdgeSize.cy, tCorrectedEdgeSize.cx, tCorrectedEdgeSize.cy, &SourceDC, tMaskSize.cx - tEdgeSize.cx, nState * tMaskSize.cy + tMaskSize.cy - tEdgeSize.cy, tEdgeSize.cx, tEdgeSize.cy, SRCCOPY); /************************************************************************/ /* Draw Sides */ /************************************************************************/ // Top _pDC->StretchBlt( tCorrectedEdgeSize.cx, nState * m_rBtnSize.Height(), m_rBtnSize.Width() - 2 * tCorrectedEdgeSize.cx, tCorrectedEdgeSize.cy, &SourceDC, tEdgeSize.cx, nState * tMaskSize.cy, 1, tEdgeSize.cy, SRCCOPY); // Bottom _pDC->StretchBlt( tCorrectedEdgeSize.cx, nState * m_rBtnSize.Height() + m_rBtnSize.Height() - tCorrectedEdgeSize.cy, m_rBtnSize.Width() - 2 * tCorrectedEdgeSize.cx, tCorrectedEdgeSize.cy, &SourceDC, tEdgeSize.cx, nState * tMaskSize.cy + tMaskSize.cy - tEdgeSize.cy, 1, tEdgeSize.cy, SRCCOPY); // Left _pDC->StretchBlt( 0, nState * m_rBtnSize.Height() + tCorrectedEdgeSize.cy, tCorrectedEdgeSize.cx, m_rBtnSize.Height() - 2 * tCorrectedEdgeSize.cy, &SourceDC, 0, nState * tMaskSize.cy + tEdgeSize.cy, tEdgeSize.cx, 1, SRCCOPY); // Right _pDC->StretchBlt( m_rBtnSize.Width() - tCorrectedEdgeSize.cx, nState * m_rBtnSize.Height() + tCorrectedEdgeSize.cy, tCorrectedEdgeSize.cx, m_rBtnSize.Height() - 2 * tCorrectedEdgeSize.cy, &SourceDC, tMaskSize.cx - tEdgeSize.cx, nState * tMaskSize.cy + tEdgeSize.cy, tEdgeSize.cx, 1, SRCCOPY); /************************************************************************/ /* Filling */ /************************************************************************/ _pDC->StretchBlt( tCorrectedEdgeSize.cx, nState * m_rBtnSize.Height() + tCorrectedEdgeSize.cy, m_rBtnSize.Width() - 2* tCorrectedEdgeSize.cx, m_rBtnSize.Height() - 2 * tCorrectedEdgeSize.cy, &SourceDC, tEdgeSize.cx, nState * tMaskSize.cy + tEdgeSize.cy, 1, 1, SRCCOPY); } // Select Old Objects into DCs SourceDC.SelectObject(hOldBmp1); } //! Draw Caption on Button void CRoundButton2::DrawButtonCaption(CDC *_pDC) { // Select Transparency for Background int nOldBckMode = _pDC->SetBkMode(TRANSPARENT); // Get old Text-Color COLORREF tOldColor = _pDC->SetTextColor(RGB(0,0,0)); // Select Font into DC HGDIOBJ hOldFont = _pDC->SelectObject(&m_tBtnFont); // Get Caption of Button CString sCaption; this->GetWindowText(sCaption); for (int nState = 0; nState < BS_LAST_STATE; nState++) { switch(nState) { case BS_ENABLED: _pDC->SetTextColor(m_tTextColor.m_tEnabled); break; case BS_CLICKED: _pDC->SetTextColor(m_tTextColor.m_tClicked); break; case BS_PRESSED: _pDC->SetTextColor(m_tTextColor.m_tPressed); break; case BS_HOT: _pDC->SetTextColor(m_tTextColor.m_tHot); break; case BS_DISABLED: default: _pDC->SetTextColor(m_tTextColor.m_tDisabled); break; } _pDC->DrawText( sCaption, CRect( m_rBtnSize.left, nState * m_rBtnSize.Height() + m_rBtnSize.top, m_rBtnSize.right, nState * m_rBtnSize.Height() + m_rBtnSize.bottom), DT_CENTER | DT_VCENTER | DT_SINGLELINE); } // Select Old Font back _pDC->SelectObject(hOldFont); // Set old Background-Mode _pDC->SetBkMode(nOldBckMode); // Set old Text-Color _pDC->SetTextColor(tOldColor); } /************************************************************************/ /* Overwritten Functions for Init and Draw of Button */ /************************************************************************/ //! Presubclass-Window-Function void CRoundButton2::PreSubclassWindow() { #ifdef _DEBUG // We really should be only sub classing a button control TCHAR buffer[255]; GetClassName (m_hWnd, buffer, sizeof(buffer) / sizeof(TCHAR)); ASSERT (CString (buffer) == _T("Button")); #endif // Check if it's a default button if (GetStyle() & 0x0FL) m_bDefaultButton = true; // Make the button owner-drawn ModifyStyle (0x0FL, BS_OWNERDRAW | BS_AUTOCHECKBOX, SWP_FRAMECHANGED); CButton::PreSubclassWindow(); } //! Draw-Item-Function /*! This Function is called each time, the Button needs a redraw */ void CRoundButton2::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // Get DC of Item CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); ASSERT (pDC != NULL); // Should Buttons be generated? bool bGenerate = !m_rBtnSize.EqualRect(&lpDrawItemStruct->rcItem) || m_bRedraw; // If Rectangles of Button are not the same if (bGenerate) { // Generate Bitmap to hold Buttons GenButtonBMPs(pDC, lpDrawItemStruct->rcItem); // Redraw done m_bRedraw = false; } // Generate DC to draw in Memory CDC MemDC; MemDC.CreateCompatibleDC(pDC); HGDIOBJ hOldBmp = MemDC.SelectObject(m_tBmpBtn); CString sActualCaption; // Get actual caption GetWindowText(sActualCaption); // Check, if caption has changed if (sActualCaption != m_sOldCaption) bGenerate = true; // Store old caption m_sOldCaption = sActualCaption; // If Rectangles of Button are not the same if (bGenerate) { // Draw Buttons DrawButtonFace(&MemDC); // Draw Button-Caption DrawButtonCaption(&MemDC); } int nButtonState; nButtonState = BS_ENABLED; if (m_bIsHotButton && m_bMouseOnButton) nButtonState = BS_HOT; if ((lpDrawItemStruct->itemState & ODS_DISABLED) == ODS_DISABLED) nButtonState = BS_DISABLED; else { if ((lpDrawItemStruct->itemState & ODS_SELECTED) == ODS_SELECTED) nButtonState = BS_PRESSED; else { if (this->m_bIsChecked) { nButtonState = BS_CLICKED; } } } // Copy correct Bitmap to Screen pDC->BitBlt( lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.top, m_rBtnSize.Width(), m_rBtnSize.Height(), &MemDC, 0, m_rBtnSize.Height() * nButtonState, SRCCOPY); MemDC.SelectObject(hOldBmp); } BEGIN_MESSAGE_MAP(CRoundButton2, CButton) ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_CAPTURECHANGED() END_MESSAGE_MAP() void CRoundButton2::OnLButtonUp(UINT nFlags, CPoint point) { if (m_bIsCheckButton) { m_bIsChecked = !m_bIsChecked; } if (m_bIsRadioButton) { m_bIsChecked = true; } CButton::OnLButtonUp(nFlags, point); } void CRoundButton2::OnMouseMove(UINT nFlags, CPoint point) { CRect rcClient; // Get Rectangle of Client GetClientRect(rcClient); // Check, if Mouse is on Control if (rcClient.PtInRect(point)) { // We only need to redraw, if the mouse enters bool bRedrawNeeded = !m_bMouseOnButton; // Mouse is on Control m_bMouseOnButton = true; // Set Capture to recognize, when the mouse leaves the control SetCapture(); // Redraw Control, if Button is hot if (m_bIsHotButton) Invalidate(); } else { // We have lost the mouse-capture, so the mouse has left the buttons face m_bMouseOnButton = false; // Mouse has left the button ReleaseCapture(); // Redraw Control, if Button is hot if (m_bIsHotButton) Invalidate(); } CButton::OnMouseMove(nFlags, point); } void CRoundButton2::OnCaptureChanged(CWnd *pWnd) { // Check, if we lost the mouse-capture if (GetCapture() != this) { // We have lost the mouse-capture, so the mouse has left the buttons face m_bMouseOnButton = false; // Redraw Control, if Button is hot if (m_bIsHotButton) Invalidate(); } CButton::OnCaptureChanged(pWnd); }