// CoolButton.cpp : implementation file // #include "stdafx.h" #include "p4win.h" #include "CoolBtn.h" #if _MFC_VER >= 0x0700 #define USE_THEMES #include #endif #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif const int nDropBtnWidth = 16; ///////////////////////////////////////////////////////////////////////////// // CCoolButton // ======== // // To Use: // 1. In the RC file, create the button as BS_OWNERDRAW. // Optionally, create a Bitmap resource 16x15 Pixels // - normal default size for a toolbar bitmap. // // 2. in the header for the dialog class: // #include "CoolBtn.h" // then create the necessary button widgets in the //{{AFX_DATA() section: // CCoolButton m_DblBtn; // // 3. In the InitDialog() for the dialog: // // a) Optionally, Call SetButtonImage specificing the // Transparency color reference. // (Usally RGB(255, 0, 255) magenta) // m_DblBtn.SetButtonImage(IDC_BTNIMAGE, RGB(255,0,255)); // // b) Either call CCoolButton's SetIDs function: // m_DblBtn.SetIDs(IDC_BTNPUSH, IDC_BTNMENU); // the first ID must be the same as the ID in the RC file! // --or-- // Add menu item(s) to the button: // m_DblBtn.AddMenuItem(nMenuId1, strMenu1, nMenuFlags1); // m_DblBtn.AddMenuItem(nMenuId2, strMenu2, nMenuFlags2); // // If you do both SetIDs() and AddMenuItem()s, only the AddMenuItem // will be called and the second ID will be ignored. // // c) Optionally, call CCoolButton's show split function: // m_DblBtn.SetSplit(bSplit); // The default is to show the split (TRUE). If set to FALSE, only // the AddMenuItem()s or 2nd ID in SetIDs() will be called and the // base id (1st ID in SetIDs()==ID in RC file) will be ignored. // // d) Optionally call m_DblBtn.SetAsDefaultBtn(TRUE) to make the button // the default button. // // 4. Add the appropiate ON_COMMAND handlers in the parent windows message map // // 5. You can also call SetIDs() or SetSplit() later to dynamically change the // button and you can later add more AddMenuItem(). //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: CCoolButton // // DESCRIPTION: // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin as CCoolBtn // L Brasington 03/06/06 2.0 Rewritten as CCoolButton // //////////////////////////////////////////////////////////////////////////////// CCoolButton::CCoolButton() { m_nID2 = 0; m_bPushed = FALSE; m_bLoaded = FALSE; m_bMenuPushed = FALSE; m_bMenuLoaded = FALSE; m_bDefaultBtn = m_bActAsDefaultBtn = FALSE; m_bSplit = TRUE; m_bOverControl = FALSE; m_bTracking = FALSE; ZeroMemory(&m_bm, sizeof m_bm); m_menu.CreatePopupMenu(); m_themeLib = LoadLibrary(_T("UxTheme.dll")); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: ~CCoolButton // // DESCRIPTION: // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // //////////////////////////////////////////////////////////////////////////////// CCoolButton::~CCoolButton() { if(m_themeLib) FreeLibrary(m_themeLib); } BEGIN_MESSAGE_MAP(CCoolButton, CButton) //{{AFX_MSG_MAP(CCoolButton) ON_WM_KEYDOWN() ON_WM_SYSKEYDOWN() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_SETFOCUS() ON_WM_KILLFOCUS() ON_WM_SYSCOLORCHANGE() ON_WM_GETDLGCODE() //}}AFX_MSG_MAP ON_MESSAGE(BM_SETSTYLE, OnSetStyle) ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover ) ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave ) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CCoolButton message handlers //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: SetIDs // // DESCRIPTION: Set the IDs // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // L Brasington 03/06/06 2.0 Rewritten as CCoolButton // //////////////////////////////////////////////////////////////////////////////// BOOL CCoolButton::SetIDs( UINT nID1, UINT nID2 ) { m_nID1 = nID1; m_nID2 = nID2; return TRUE; } //////////////////////////////////////////////////////////////////////////////// UINT CCoolButton::OnGetDlgCode() { UINT nCode = CButton::OnGetDlgCode(); nCode |= (m_bActAsDefaultBtn ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON); return nCode; } // mask for control's type #undef BS_TYPEMASK #define BS_TYPEMASK SS_TYPEMASK LRESULT CCoolButton::OnSetStyle(WPARAM wParam, LPARAM lParam) { m_bActAsDefaultBtn = wParam & BS_DEFPUSHBUTTON; // can't change control type after owner-draw is set. // let the system process changes to other style bits // and redrawing, while keeping owner-draw style return DefWindowProc(BM_SETSTYLE, (wParam & ~BS_TYPEMASK) | BS_OWNERDRAW, lParam); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: DrawItem // // DESCRIPTION: Called in response to draw the button // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // L Brasington 03/06/06 2.0 Rewritten as CCoolButton, Added XP support // //////////////////////////////////////////////////////////////////////////////// #ifdef USE_THEMES typedef HTHEME(__stdcall *PFNOPENTHEMEDATA)(HWND hwnd, LPCWSTR pszClassList); typedef HRESULT(__stdcall *PFNCLOSETHEMEDATA)(HTHEME hTheme); typedef HRESULT(__stdcall *PFNDRAWTHEMEBACKGROUND)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, OPTIONAL const RECT *pClipRect); typedef HRESULT(__stdcall *PFNGETTHEMEBACKGROUNDCONTENTRECT)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, RECT *pContentRect); #endif void CCoolButton::DrawItem(DRAWITEMSTRUCT* lpDIS) { if (lpDIS->CtlType != ODT_BUTTON) return; CFont *pFont = CFont::FromHandle((HFONT)GetStockObject(DEFAULT_GUI_FONT)); CDC dcMem; CBitmap bmp; CRect btnRect(lpDIS->rcItem); CRect trueRect(btnRect); CRect rectFocus(btnRect); CDC *pDC = CDC::FromHandle(lpDIS->hDC); BOOL bDisabled = ODS_DISABLED & lpDIS->itemState; #ifdef USE_THEMES HTHEME hTheme = 0; if(m_themeLib) { PFNOPENTHEMEDATA pOpenThemeData = (PFNOPENTHEMEDATA) GetProcAddress(m_themeLib, "OpenThemeData"); if(pOpenThemeData) hTheme = (pOpenThemeData)(m_hWnd, L"Button"); } if(hTheme) { // attempt to map owner draw states to theme pushbutton states int iState = PBS_NORMAL; if(m_bOverControl)//lpDIS->itemState & ODS_HOTLIGHT) iState = PBS_HOT; else if(m_bDefaultBtn || m_bActAsDefaultBtn)//lpDIS->itemState & ODS_DEFAULT) iState = PBS_DEFAULTED; else if(lpDIS->itemState & ODS_SELECTED) iState = PBS_PRESSED; HRESULT hr; HDC hDC = lpDIS->hDC; RECT rc = lpDIS->rcItem; // let theme draw background PFNDRAWTHEMEBACKGROUND pDrawThemeBackground = (PFNDRAWTHEMEBACKGROUND) GetProcAddress(m_themeLib, "DrawThemeBackground"); if(!pDrawThemeBackground) return; hr = (pDrawThemeBackground)(hTheme, hDC, BP_PUSHBUTTON, iState, &rc, 0); if(hr != S_OK) return; // get rect to put content in RECT rcContent; PFNGETTHEMEBACKGROUNDCONTENTRECT pGetThemeBackgroundContentRect = (PFNGETTHEMEBACKGROUNDCONTENTRECT) GetProcAddress(m_themeLib, "GetThemeBackgroundContentRect"); if(!pGetThemeBackgroundContentRect) return; hr = (pGetThemeBackgroundContentRect)(hTheme, hDC, BP_PUSHBUTTON, iState, &rc, &rcContent); if(hr != S_OK) return; rectFocus = rcContent; if (!m_bMenuPushed) rectFocus.OffsetRect(m_bPushed,m_bPushed); if (m_bSplit) rectFocus.right -= nDropBtnWidth; if (lpDIS->itemState & ODS_FOCUS || m_bPushed) { if (!m_bMenuPushed) ::DrawFocusRect(lpDIS->hDC, &rcContent); } } else #endif { //////////////////////////////////////// // Button Background // //////////////////////////////////////// CBrush brush(GetSysColor(COLOR_BTNFACE)); pDC->FillRect(trueRect,&brush); if (m_bDefaultBtn) btnRect.DeflateRect(1,1); rectFocus.DeflateRect(4,4); if (!m_bMenuPushed) rectFocus.OffsetRect(m_bPushed,m_bPushed); if (m_bSplit) rectFocus.right -= nDropBtnWidth; //////////////////////////////////////// // Button in a normal state // //////////////////////////////////////// if (!m_bPushed || m_bMenuPushed) pDC->DrawFrameControl(&btnRect,DFC_BUTTON,DFCS_BUTTONPUSH); //////////////////////////////////////// // Default Button State // //////////////////////////////////////// if ((m_bDefaultBtn || m_bPushed) && !bDisabled) { pDC->FrameRect(&lpDIS->rcItem,CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); if (m_bPushed && !m_bMenuPushed) pDC->FrameRect(&btnRect,CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); } //////////////////////////////////////// // State Focus // //////////////////////////////////////// if (lpDIS->itemState & ODS_FOCUS || m_bPushed) { pDC->FrameRect(&lpDIS->rcItem,CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); if (!m_bMenuPushed) pDC->DrawFocusRect(&rectFocus); } //////////////////////////////////////// // Action Focus // //////////////////////////////////////// if ((lpDIS->itemAction & ODA_FOCUS)) if (!m_bMenuPushed) pDC->DrawFocusRect(&rectFocus); } //////////////////////////////////////// // Draw out bitmap // //////////////////////////////////////// // Draw out bitmap if (m_bLoaded) { if (!bDisabled) { m_IL.DrawIndirect(pDC,0,CPoint(6+m_bPushed,6+m_bPushed), CSize(m_bm.bmWidth, m_bm.bmHeight), CPoint(0,0),ILD_NORMAL); } else { pDC->DrawState(CPoint(6+m_bPushed,6+m_bPushed), CSize(m_bm.bmWidth, m_bm.bmHeight), m_hbmpDisabled, DST_BITMAP | DSS_DISABLED); } } //////////////////////////////////////// // Draw out text // //////////////////////////////////////// pDC->SelectObject(pFont); CRect rectText(rectFocus); if (!m_bSplit) rectText.right -= nDropBtnWidth; rectText.left += m_bm.bmWidth + 2; CString strCaption; GetWindowText(strCaption); pDC->SetBkMode(TRANSPARENT); pDC->SetBkColor(GetSysColor(COLOR_BTNFACE)); if (ODS_DISABLED & lpDIS->itemState) { rectText.OffsetRect(1,1); pDC->SetTextColor(GetSysColor(COLOR_WINDOW)); pDC->DrawText(strCaption,rectText,DT_SINGLELINE|DT_CENTER|DT_VCENTER); rectText.OffsetRect(-1,-1); pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); pDC->DrawText(strCaption,rectText,DT_SINGLELINE|DT_CENTER|DT_VCENTER); } else { pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT)); pDC->DrawText(strCaption,rectText,DT_SINGLELINE|DT_CENTER|DT_VCENTER); } CRect rectSplit(btnRect); rectSplit.DeflateRect(2,2); rectSplit.right -= nDropBtnWidth; //////////////////////////////////////// // Drop down split // //////////////////////////////////////// if (m_bSplit) { CPen brFace(PS_SOLID,1,GetSysColor(COLOR_3DSHADOW)); pDC->SelectObject(&brFace); pDC->MoveTo(rectSplit.right, rectSplit.top); pDC->LineTo(rectSplit.right, rectSplit.bottom); CPen brLite(PS_SOLID,1,GetSysColor(COLOR_3DHILIGHT)); pDC->SelectObject(&brLite); pDC->MoveTo(rectSplit.right+1 , rectSplit.top); pDC->LineTo(rectSplit.right+1, rectSplit.bottom); } //////////////////////////////////////// // Draw Arrow // //////////////////////////////////////// rectSplit.left = rectSplit.right; rectSplit.right += nDropBtnWidth; CPoint pt(rectSplit.CenterPoint()); pt += CPoint(m_bPushed,m_bPushed); CPen penBlack(PS_SOLID, 1, bDisabled ? GetSysColor(COLOR_GRAYTEXT) : GetSysColor(COLOR_WINDOWTEXT)); pDC->SelectObject(&penBlack); DrawArrow(pDC,pt); //////////////////////////////////////// // Drop down state // //////////////////////////////////////// if (m_bMenuPushed && !bDisabled) { rectSplit.InflateRect(1,1); pDC->DrawEdge(rectSplit,BDR_SUNKENOUTER, BF_RECT); } if(hTheme) { PFNCLOSETHEMEDATA pCloseThemeData = (PFNCLOSETHEMEDATA)GetProcAddress(m_themeLib, "CloseThemeData"); if(!pCloseThemeData) return; (pCloseThemeData)(hTheme); } } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: DrawArrow // // DESCRIPTION: Draws drop down arrow, we could use DrawFrameControl - a bit too // messy // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // //////////////////////////////////////////////////////////////////////////////// void CCoolButton::DrawArrow(CDC* pDC,CPoint ArrowTip) { CPoint ptDest; pDC->SetPixel(ArrowTip,RGB(0,0,0)); ArrowTip -= CPoint(1,1); pDC->MoveTo(ArrowTip); ptDest = ArrowTip; ptDest += CPoint(3,0); pDC->LineTo(ptDest); ArrowTip -= CPoint(1,1); pDC->MoveTo(ArrowTip); ptDest = ArrowTip; ptDest += CPoint(5,0); pDC->LineTo(ptDest); ArrowTip -= CPoint(1,1); pDC->MoveTo(ArrowTip); ptDest = ArrowTip; ptDest += CPoint(7,0); pDC->LineTo(ptDest); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: OnLButtonDown // // DESCRIPTION: handles button pressed state, including drop down menu // // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // L Brasington 03/06/06 2.0 Rewritten as CCoolButton // //////////////////////////////////////////////////////////////////////////////// void CCoolButton::OnLButtonDown(UINT nFlags, CPoint point) { SetFocus(); if (m_bMenuPushed) { m_bMenuPushed = FALSE; Invalidate(); return; } if (!m_bSplit || HitMenuBtn(point)) { OnMenuBtn(); return; } m_bPushed = TRUE; Invalidate(); CButton::OnLButtonDown(nFlags, point); } BOOL CCoolButton::OnMenuBtn() { if (!m_bMenuLoaded) { if (m_nID2) { GetParent()->PostMessage(WM_COMMAND, m_nID2, 0); m_bMenuPushed = TRUE; Invalidate(); return TRUE; } } else { m_bMenuPushed = TRUE; Invalidate(); CRect rc; GetWindowRect(rc); int x = rc.left; int y = rc.bottom; m_menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON,x,y,GetParent()); m_bMenuPushed = FALSE; Invalidate(); return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: OnLButtonUp // // DESCRIPTION: Redraws button in normal state // // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // L Brasington 03/06/06 2.0 Rewritten as CCoolButton // //////////////////////////////////////////////////////////////////////////////// void CCoolButton::ClearButtonPushed() { if (m_bPushed) { m_bPushed = FALSE; Invalidate(); } if (m_bMenuPushed) { m_bMenuPushed = FALSE; Invalidate(); } } void CCoolButton::OnLButtonUp(UINT nFlags, CPoint point) { ClearButtonPushed(); CButton::OnLButtonUp(nFlags, point); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: OnMouseMove // // DESCRIPTION: Tracks mouse whilst pressed // // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // L Brasington 03/06/06 2.0 Rewritten as CCoolButton // //////////////////////////////////////////////////////////////////////////////// LRESULT CCoolButton::OnMouseHover(WPARAM wparam, LPARAM lparam) { m_bOverControl=TRUE; Invalidate(); return 1; } LRESULT CCoolButton::OnMouseLeave(WPARAM wparam, LPARAM lparam) { m_bTracking = FALSE; m_bOverControl = FALSE; Invalidate(FALSE); return 0; } void CCoolButton::OnMouseMove(UINT nFlags, CPoint point) { if (m_bPushed) { ClientToScreen(&point); if (WindowFromPoint(point) != this) { m_bPushed = FALSE; Invalidate(); } } if (!m_bTracking) { TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.hwndTrack = m_hWnd; tme.dwFlags = TME_LEAVE|TME_HOVER; tme.dwHoverTime = 1; m_bTracking = _TrackMouseEvent(&tme) != FALSE; } CButton::OnMouseMove(nFlags, point); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: SetButtonImage // // DESCRIPTION: Sets the button image, COLORREF crMask specifics the transparency // color // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // //////////////////////////////////////////////////////////////////////////////// BOOL CCoolButton::SetButtonImage(UINT nResourceId, COLORREF crMask) { // The ID must exist also as a bitmap resource!!! m_btnImage.LoadBitmap(nResourceId); m_btnImage.GetObject(sizeof m_bm, &m_bm); m_IL.Create( nResourceId, m_bm.bmWidth, 1, crMask ); m_bLoaded = TRUE; m_crMask = crMask; HBITMAP bmTemp; COLORMAP mapColor; mapColor.from = crMask; mapColor.to = RGB(255,255,255); bmTemp = (HBITMAP)::CreateMappedBitmap(AfxGetApp()->m_hInstance, nResourceId, IMAGE_BITMAP, &mapColor, 1); m_hbmpDisabled = (HBITMAP)::CopyImage(bmTemp, IMAGE_BITMAP, m_bm.bmWidth, m_bm.bmHeight, LR_COPYDELETEORG); return m_bLoaded; } void CCoolButton::OnSetFocus(CWnd* pOldWnd) { CButton::OnSetFocus(pOldWnd); Invalidate(); } void CCoolButton::OnKillFocus(CWnd* pNewWnd) { CButton::OnKillFocus(pNewWnd); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: OnSysColorChange // // DESCRIPTION: Called when system colors change, force a button redraw // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // //////////////////////////////////////////////////////////////////////////////// void CCoolButton::OnSysColorChange() { CButton::OnSysColorChange(); Invalidate(); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: HitMenuBtn // // DESCRIPTION: Helper function to test for menu button hit... // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // L Brasington 03/06/06 2.0 Rewritten as CCoolButton // //////////////////////////////////////////////////////////////////////////////// BOOL CCoolButton::HitMenuBtn(CPoint point) { if (!m_bMenuLoaded && !m_nID2) return FALSE; // Don't allow menu button drop down if no menu items are loaded ClientToScreen(&point); CRect rect; GetWindowRect(rect); rect.left = rect.right - nDropBtnWidth; return rect.PtInRect(point); } //////////////////////////////////////////////////////////////////////////////// // // FUNCTION: AddMenuItem // // DESCRIPTION: Adds a menu item and id to our menu. // // NOTES: // // MAINTENANCE: // Name: Date: Version: Notes: // NT ALMOND 210100 1.0 Origin // //////////////////////////////////////////////////////////////////////////////// BOOL CCoolButton::AddMenuItem(UINT nMenuId,const CString strMenu, UINT nMenuFlags) { BOOL bRet = m_menu.AppendMenu(nMenuFlags | MF_STRING, nMenuId, (LPCTSTR)strMenu); m_bMenuLoaded |= bRet; return bRet; } //////////////////////////////////////////////////////////////////////////////// void CCoolButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar == 'F' && GetKeyState(VK_CONTROL) & 0x8000) // ^F == Find GetParent()->PostMessage(WM_COMMAND, ID_POSITIONTOPATTERN, 0); else if (nChar == VK_F3) GetParent()->PostMessage(WM_COMMAND, GetKeyState(VK_SHIFT) & 0x8000 ? ID_FINDPREV : ID_FINDNEXT, 0); else CButton::OnChar(nChar, nRepCnt, nFlags); } void CCoolButton::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch ( nChar ) { case VK_DOWN: OnMenuBtn(); break; } CWnd::OnSysKeyDown(nChar, nRepCnt, nFlags); }