// ========================================================================== // Class Implementation : COXBitmapButton // ========================================================================== // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXBitmapButton.h" #include "UTB64Bit.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif void AFXAPI DDX_Toggle(CDataExchange *pDX, int nIDC, int& value) { HWND hWnd=pDX->PrepareCtrl(nIDC); ASSERT(hWnd!=NULL); COXBitmapButton* pButton=(COXBitmapButton*)CWnd::FromHandle(hWnd); if(pDX->m_bSaveAndValidate) { if(!pButton->IsToggleButton()) { value=-1; } else if(pButton->IsIndeterminate()) { value=2; } else if(pButton->IsChecked()) { value=1; } else { value=0; } } else { if(pButton->IsToggleButton()) { switch(value) { case 1: pButton->SetStateEx((pButton->GetStateEx()|OXBB_STATE_CHECKED)& ~OXBB_STATE_INDETERMINATE); break; case 2: pButton->SetStateEx(pButton->GetStateEx()| (OXBB_STATE_CHECKED|OXBB_STATE_INDETERMINATE)); break; case 0: default: value=0; pButton->SetStateEx(pButton->GetStateEx()& ~(OXBB_STATE_CHECKED|OXBB_STATE_INDETERMINATE)); break; } } } } IMPLEMENT_DYNAMIC(COXBitmapButton, CButton) #define new DEBUG_NEW // We define the structs GRPICONDIR, GRPICONDIRENTRY and ICONIMAGE // which represent an icon directory and an icon from resource // See also 'Icons in Win32' (John Hornick - Microsoft Corporation) // ... #pragmas are used here to insure that the structure's // packing in memory matches the packing of the EXE or DLL. #pragma pack( push ) #pragma pack( 2 ) typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // how many bytes in this resource? WORD nID; // the ID } GRPICONDIRENTRY, *LPGRPICONDIRENTRY; typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource type (1 for icons) WORD idCount; // How many images? GRPICONDIRENTRY idEntries[1]; // The entries for each image (idCount of 'em) } GRPICONDIR, *LPGRPICONDIR; typedef struct { BITMAPINFOHEADER icHeader; // DIB header RGBQUAD icColors[1]; // Color table (more than 1!) BYTE icXOR[1]; // DIB bits for XOR mask (more than 1!) BYTE icAND[1]; // DIB bits for AND mask (more than 1!) } ICONIMAGE, *LPICONIMAGE; #pragma pack( pop ) ///////////////////////////////////////////////////////////////////////////// // Definition of static members // Button with image (and text) layout // +----------------------------------------+ // | | // | By | // | | // | +------------------+ | // | | | | // | | | | // | | | | // | | | | // | Bx | B I T M A P | Bx | // | | | | // | | | | // | | | | // | | | | // | +------------------+ | // | | // | By | // | | // | +----------------------+ | // | Tx | T E X T | Tx | // | +----------------------+ | // | | // | Ty | // | | // +----------------------------------------+ // Bx = m_ptImageOffset.x // By = m_ptImageOffset.y // Tx = m_ptTextOffset.x // Ty = m_ptTextOffset.y // Position of the images in the image list const int COXBitmapButton::m_nNormalImageIndex = 0; const int COXBitmapButton::m_nInactiveImageIndex = 1; const int COXBitmapButton::m_nDisabledImageIndex = 2; // The default color of the face of a button COLORREF COXBitmapButton::m_defaultButtonColor = ::GetSysColor(COLOR_BTNFACE); // Data members ------------------------------------------------------------- // protected: // CImageList m_imageList; // --- Image list containing all the images needed for this button // CPalette m_palette; // --- The palette of the image, read from resource // COLORREF m_textColor; // --- The color used to draw text // CFont m_textFont; // --- The font used to draw text // CToolTipCtrl m_toolTip; // --- The tooltip window of this button (if created) // BOOL m_bTrackLook; // --- Whether 'track look' should be used // BOOL m_bMouseOverButton; // --- Whether the mouse is over the button at the moment // BOOL m_bMouseDown; // --- Whether the mouse button is down (while mouse is captured) // BOOL m_bHyperLook; // --- Whether this button is in hyper look mode // CImageList m_backgroundImage; // --- A grabbed image of the button's background (needed in hyper look mode) // BOOL m_bBackgroundGrabbed; // --- Whether the background image has been grabbed // (and thus the contents of m_backgroundImage is valid) // UINT m_nDefaultCursorID; // --- The ID of the default cursor (or 0 when not et) // HCURSOR m_hDefaultCursor; // --- The default cursor (or NULL when not et) // UINT m_nDisabledCursorID; // --- The ID of the disabled cursor (or 0 when not et) // HCURSOR m_hDisabledCursor; // --- The disabled cursor (or NULL when not et) // BOOL m_bPseudoDisableMode; // --- Whether this button is in pseudo disabled mode // BOOL m_bEnabled; // --- Whether this button is enabled. // This value is only valid in pseudeo disable mode // BOOL m_bHasTabStop; // --- Whether this button had a tab stop before it was disabled in pseudeo disable mode // private: // Member functions --------------------------------------------------------- // public: BEGIN_MESSAGE_MAP(COXBitmapButton, CButton) //{{AFX_MSG_MAP(COXBitmapButton) ON_WM_SYSCOLORCHANGE() ON_WM_MOUSEMOVE() ON_WM_KEYUP() ON_WM_KILLFOCUS() ON_WM_LBUTTONUP() ON_WM_MBUTTONUP() ON_WM_SYSKEYUP() ON_WM_LBUTTONDOWN() ON_WM_ERASEBKGND() ON_WM_SYSKEYDOWN() ON_WM_KEYDOWN() ON_WM_SIZE() ON_WM_MOVE() ON_WM_ENABLE() ON_WM_SETCURSOR() //}}AFX_MSG_MAP ON_MESSAGE(WM_SETTEXT, OnSetText) ON_MESSAGE(WM_CHECK_TRACK_LOOK, OnCheckTrackLook) ON_CONTROL_REFLECT_EX(BN_CLICKED,OnClicked) ON_MESSAGE(BM_CLICK, OnClick) ON_WM_LBUTTONDBLCLK() END_MESSAGE_MAP() COXBitmapButton::COXBitmapButton() : m_textColor(RGB(0, 0, 0)), m_bTrackLook(FALSE), m_bMouseOverButton(FALSE), m_bMouseDown(FALSE), m_bHyperLook(FALSE), m_bBackgroundGrabbed(FALSE), m_nDefaultCursorID(0), m_hDefaultCursor(NULL), m_nDisabledCursorID(0), m_hDisabledCursor(NULL), m_bPseudoDisableMode(FALSE), m_bEnabled(FALSE), m_bPlaying(FALSE), m_bDrawDropdownSeparator(TRUE) { ASSERT_VALID(this); m_nDropDownArrowWidth=GetDropDownArrowWidth(); m_dwStyleEx=0; m_dwStateEx=0; // ... The number of pixels the picture is offset from the button border (or the text) m_ptImageOffset = CPoint(-5,-5); // ... The number of pixels the text is offset from the button border m_ptTextOffset = CPoint(-7,-6); // ... The number of pixels the outer focus rect is offset from the button border m_ptOuterFocusOffset = CPoint(0,0); // ... The number of pixels the inner focus rect is offset from the button border m_ptInnerFocusOffset = CPoint(-4,-4); // ... The size in pixels of the focus rect when in hyper look mode m_hyperFocusSize = CPoint(7, 6); // ... How much the image and the text must move when the button is down m_ptDownOffset = CPoint(2,2); // ... How much the image and the text must move when the button is checked m_ptCheckedOffset = CPoint(1,1); // ... How much the image and the text must move when the mouse // is over the button in hyper look (and track look is also enabled) m_ptHyperOffset = CPoint(-1,-1); // ... The number of pixels the dropdown arrow rect is offset from the button border m_ptArrowOffset = CPoint(-2,-5); } BOOL COXBitmapButton::LoadBitmap(LPCTSTR lpszBitmapResource, BOOL bResize /* = TRUE */, COLORREF crMask /* = CLR_NONE */) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); #ifdef _DEBUG if (AfxFindResourceHandle(lpszBitmapResource, RT_BITMAP) == NULL) TRACE0("COXBitmapButton::LoadBitmap : Specified bitmap resource not found\n"); #endif // _DEBUG // BS_BITMAP style is no longer supported // This button must have the BS_OWNERDRAW style ASSERT((GetStyle() & BS_BITMAP) != BS_BITMAP); ASSERT((GetStyle() & BS_OWNERDRAW) == BS_OWNERDRAW); // Clear possible previous contents if (m_imageList.m_hImageList != NULL) VERIFY(m_imageList.DeleteImageList()); // Get and compute the necessary bitmaps CBitmap bitmap; CBitmap grayBitmap; CBitmap disabledBitmap; // Try to load the new bitmap if (!LoadBitmap(lpszBitmapResource, bitmap, m_palette)) { TRACE(_T("COXBitmapButton::LoadBitmap : Failed to load bitmap \n")); return FALSE; } // Build the gray bitmap if (!BuildGrayBitmap(lpszBitmapResource, crMask, &grayBitmap)) { TRACE(_T("COXBitmapButton::LoadBitmap : Failed to build the gray scale bitmap, continuing\n")); } BITMAP bmInfo; ZeroMemory(&bmInfo, sizeof(bmInfo)); VERIFY(bitmap.GetObject(sizeof(bmInfo), &bmInfo) == sizeof(bmInfo)); CSize bitmapSize(bmInfo.bmWidth, bmInfo.bmHeight); BOOL bMask = crMask != CLR_NONE; // Let the image list use a device-dependent bitmap for its internal data structure // instead of the default ILC_COLOR4 (4-bit (16 color) device-independent bitmap (DIB) section) // Otherwise the colors will shift (e.g. disabled button look) VERIFY(m_imageList.Create(bitmapSize.cx, bitmapSize.cy, bMask | ILC_COLORDDB, 0, OX_MAX_IMAGE_COUNT)); VERIFY(m_imageList.Add(&bitmap, crMask) == m_nNormalImageIndex); VERIFY(m_imageList.Add(&grayBitmap, crMask) == m_nInactiveImageIndex); // Build the disabled image HICON hSourceIcon = m_imageList.ExtractIcon(m_nNormalImageIndex); HICON hDestIcon = NULL; if(BuildDisabledImage(hSourceIcon, bitmapSize, hDestIcon)) { // Add the disabled image to the image list and use the // mask of the normal image VERIFY(m_imageList.Add(hDestIcon) == m_nDisabledImageIndex); VERIFY(::DestroyIcon(hDestIcon)); } else { TRACE0("COXBitmapButton::LoadBitmap : Failed to build the disabled image, continuing\n"); } VERIFY(::DestroyIcon(hSourceIcon)); // close any animation if any was loaded and // destroy the window that is attached to the animate control if (::IsWindow(m_animateCtrl)) { m_animateCtrl.Close(); m_animateCtrl.DestroyWindow(); } m_bPlaying=FALSE; // Size to content if (bResize) SizeToContent(); return TRUE; } BOOL COXBitmapButton::LoadInactiveBitmap(LPCTSTR lpszBitmapResource) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); // image list must have at least one image if(m_imageList.GetImageCount()<1) { TRACE(_T("COXBitmapButton::LoadInactiveBitmap: before calling the function you must associate default image with the button!\n")); return FALSE; } #ifdef _DEBUG if (AfxFindResourceHandle(lpszBitmapResource, RT_BITMAP) == NULL) TRACE0("COXBitmapButton::LoadInactiveBitmap : Specified bitmap resource not found\n"); #endif // _DEBUG // Get the necessary bitmap CBitmap bitmap; // Try to load the new bitmap CPalette palette; if(!LoadBitmap(lpszBitmapResource,bitmap,palette)) { TRACE(_T("COXBitmapButton::LoadInactiveBitmap: Failed to load bitmap \n")); return FALSE; } VERIFY(m_imageList.Replace(m_nInactiveImageIndex,&bitmap,NULL)); return TRUE; } BOOL COXBitmapButton::LoadDisabledBitmap(LPCTSTR lpszBitmapResource) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); // image list must have at least one image if(m_imageList.GetImageCount()<1) { TRACE(_T("COXBitmapButton::LoadDisabledBitmap: before calling the function you must associate default image with the button!\n")); return FALSE; } #ifdef _DEBUG if (AfxFindResourceHandle(lpszBitmapResource, RT_BITMAP) == NULL) TRACE0("COXBitmapButton::LoadDisabledBitmap : Specified bitmap resource not found\n"); #endif // _DEBUG // Get the necessary bitmap CBitmap bitmap; // Try to load the new bitmap CPalette palette; if(!LoadBitmap(lpszBitmapResource,bitmap,palette)) { TRACE(_T("COXBitmapButton::LoadDisabledBitmap: Failed to load bitmap \n")); return FALSE; } VERIFY(m_imageList.Replace(m_nDisabledImageIndex,&bitmap,NULL)); return TRUE; } BOOL COXBitmapButton::LoadIcon(LPCTSTR lpszIconResource, BOOL bResize /* = TRUE */, UINT nWidth /* = 0 */, UINT nHeight /* = 0 */) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); #ifdef _DEBUG if (AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON) == NULL) TRACE0("COXBitmapButton::LoadIcon : Specified icon resource not found\n"); #endif // _DEBUG // BS_BITMAP style is no longer supported // This button must have the BS_OWNERDRAW style ASSERT((GetStyle() & BS_BITMAP) != BS_BITMAP); ASSERT((GetStyle() & BS_OWNERDRAW) == BS_OWNERDRAW); // Clear possible previous contents if (m_imageList.m_hImageList != NULL) VERIFY(m_imageList.DeleteImageList()); // Get and compute the necessary bitmaps HICON hIcon; CBitmap grayBitmap; CBitmap disabledBitmap; // Try to load the new bitmap if(nWidth==0) nWidth=::GetSystemMetrics(SM_CXICON); if(nHeight==0) nHeight=::GetSystemMetrics(SM_CYICON); hIcon = (HICON)::LoadImage(AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON), lpszIconResource,IMAGE_ICON,nWidth,nHeight,LR_DEFAULTCOLOR); if (hIcon == NULL) { TRACE0("COXBitmapButton::LoadIcon : Failed to load icon\n"); return FALSE; } // Get the icon's palette if (!GetIconPalette(lpszIconResource, m_palette)) TRACE0("COXBitmapButton::LoadIcon : Failed to get the palette, continuing\n"); // Build the gray bitmap and gray palette HICON hGrayIcon = NULL; if (!BuildGrayIcon(lpszIconResource, &hGrayIcon)) TRACE0("COXBitmapButton::LoadIcon : Failed to build the gray scale icon, continuing\n"); ICONINFO iconInfo; ZeroMemory(&iconInfo, sizeof(iconInfo)); VERIFY(::GetIconInfo(hIcon, &iconInfo)); BITMAP bmInfo; ZeroMemory(&bmInfo, sizeof(bmInfo)); VERIFY(::GetObject(iconInfo.hbmColor, sizeof(bmInfo), &bmInfo) == sizeof(bmInfo)); CSize iconSize(bmInfo.bmWidth, bmInfo.bmHeight); // Let the image list use a device-dependent bitmap for its internal data structure // instaed of the default ILC_COLOR4 (4-bit (16 color) device-independent bitmap (DIB) section) // Otherwise the colors will shift (e.g. disabled button look) VERIFY(m_imageList.Create(iconSize.cx, iconSize.cy, TRUE | ILC_COLORDDB, 0, OX_MAX_IMAGE_COUNT)); VERIFY(m_imageList.Add(hIcon) == m_nNormalImageIndex); VERIFY(m_imageList.Add(hGrayIcon) == m_nInactiveImageIndex); if(hGrayIcon!=NULL) ::DestroyIcon(hGrayIcon); // Build the disabled bitmap HICON hDestIcon = NULL; if (BuildDisabledImage(hIcon, iconSize, hDestIcon)) { // Add the disabled image to the image list and use the // mask of the normal image VERIFY(m_imageList.Add(hDestIcon) == m_nDisabledImageIndex); VERIFY(::DestroyIcon(hDestIcon)); } else TRACE0("COXBitmapButton::LoadIcon : Failed to build the disabled image, continuing\n"); ::DeleteObject(iconInfo.hbmColor); iconInfo.hbmColor = NULL; // ... Delete the mask bitmap ::DeleteObject(iconInfo.hbmMask); iconInfo.hbmMask = NULL; // close any animation if any was loaded and // destroy the window that is attached to the animate control if (::IsWindow(m_animateCtrl)) { m_animateCtrl.Close(); m_animateCtrl.DestroyWindow(); } m_bPlaying=FALSE; // Size to content if (bResize) SizeToContent(); return TRUE; } BOOL COXBitmapButton::LoadInactiveIcon(LPCTSTR lpszIconResource, UINT nWidth /* = 0 */, UINT nHeight /* = 0 */) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); #ifdef _DEBUG if (AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON) == NULL) TRACE0("COXBitmapButton::LoadInactiveIcon : Specified icon resource not found\n"); #endif // _DEBUG // Get and compute the necessary bitmaps HICON hIcon; // Try to load the new bitmap if(nWidth==0) nWidth=::GetSystemMetrics(SM_CXICON); if(nHeight==0) nHeight=::GetSystemMetrics(SM_CYICON); hIcon = (HICON)::LoadImage(AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON), lpszIconResource,IMAGE_ICON,nWidth,nHeight,LR_DEFAULTCOLOR); if (hIcon == NULL) { TRACE0("COXBitmapButton::LoadInactiveIcon : Failed to load icon\n"); return FALSE; } VERIFY(m_imageList.Replace(m_nInactiveImageIndex,hIcon)); return TRUE; } BOOL COXBitmapButton::LoadDisabledIcon(LPCTSTR lpszIconResource, UINT nWidth /* = 0 */, UINT nHeight /* = 0 */) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); #ifdef _DEBUG if (AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON) == NULL) TRACE0("COXBitmapButton::LoadDisabledIcon : Specified icon resource not found\n"); #endif // _DEBUG // Get and compute the necessary bitmaps HICON hIcon; // Try to load the new bitmap if(nWidth==0) nWidth=::GetSystemMetrics(SM_CXICON); if(nHeight==0) nHeight=::GetSystemMetrics(SM_CYICON); hIcon = (HICON)::LoadImage(AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON), lpszIconResource,IMAGE_ICON,nWidth,nHeight,LR_DEFAULTCOLOR); if (hIcon == NULL) { TRACE0("COXBitmapButton::LoadDisabledIcon: Failed to load icon\n"); return FALSE; } VERIFY(m_imageList.Replace(m_nDisabledImageIndex,hIcon)); return TRUE; } // Load AVI from a resource or from a file BOOL COXBitmapButton::LoadAvi(UINT nIDAviResource, LPCTSTR lpszFileName /* = NULL */, BOOL bResize /* = TRUE */) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); // BS_BITMAP style is no longer supported // This button must have the BS_OWNERDRAW style ASSERT((GetStyle() & BS_BITMAP) != BS_BITMAP); ASSERT((GetStyle() & BS_OWNERDRAW) == BS_OWNERDRAW); CRect rect; GetClientRect(rect); if (!::IsWindow(m_animateCtrl)) { // try to create animate control if(!m_animateCtrl.Create(WS_CHILD|WS_VISIBLE|ACS_TRANSPARENT,rect,this,0)) { return FALSE; } } else { // close AVI if any was opened m_animateCtrl.Close(); } m_bPlaying=FALSE; if(lpszFileName!=NULL) { if(!m_animateCtrl.Open(lpszFileName)) { return FALSE; } } else { if(!m_animateCtrl.Open(nIDAviResource)) { return FALSE; } } // Clear possible previous contents if (m_imageList.m_hImageList != NULL) VERIFY(m_imageList.DeleteImageList()); // Size to content if (bResize) SizeToContent(); return TRUE; } // Remove any image associated with control (bitmap, icon or avi) BOOL COXBitmapButton::RemoveImage(BOOL bResize /* = TRUE */) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); // BS_BITMAP style is no longer supported // This button must have the BS_OWNERDRAW style ASSERT((GetStyle() & BS_BITMAP) != BS_BITMAP); ASSERT((GetStyle() & BS_OWNERDRAW) == BS_OWNERDRAW); if (::IsWindow(m_animateCtrl)) { // close AVI if any was opened m_animateCtrl.Close(); } m_bPlaying=FALSE; // Clear all contents if (m_imageList.m_hImageList != NULL) VERIFY(m_imageList.DeleteImageList()); // Size to content if (bResize) SizeToContent(); return TRUE; } CPalette* COXBitmapButton::GetPalette() { return &m_palette; } void COXBitmapButton::SizeToContent() { ASSERT(::IsWindow(m_hWnd)); CSize oldButtonSize = GetButtonSize(); CSize newButtonSize = GetFitButtonSize(); VERIFY(SetWindowPos(NULL, -1, -1, newButtonSize.cx, newButtonSize.cy, SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOACTIVATE)); // Invalidate the union of the old and the new button rect CRect invalidRect; invalidRect.UnionRect(CRect(CPoint(0, 0), oldButtonSize), CRect(CPoint(0, 0), newButtonSize)); // ... Invalidate through the parent window, because this button may have shrunk // and thus part of the parent window has to be invalidated as well ClientToScreen(invalidRect); ASSERT(GetParent() != NULL); GetParent()->ScreenToClient(invalidRect); GetParent()->InvalidateRect(invalidRect); } BOOL COXBitmapButton::GetTrackLook() const { return m_bTrackLook; } BOOL COXBitmapButton::SetTrackLook(BOOL bTrackLook /* = TRUE */) { // This control should already be created ASSERT(::IsWindow(m_hWnd)); if ((GetStyle() & BS_OWNERDRAW) != BS_OWNERDRAW) { TRACE0("COXBitmapButton::SetTrackLook : Can only set track look for owner draw buttons, failing\n"); return FALSE; } if (bTrackLook != m_bTrackLook) { m_bTrackLook = bTrackLook; Invalidate(); } return TRUE; } void COXBitmapButton::SetHorizontalAlignment(DWORD nAlignment /* = BS_CENTER */) { ASSERT(::IsWindow(m_hWnd)); ASSERT((nAlignment == BS_LEFT) || (nAlignment == BS_CENTER) || (nAlignment == BS_RIGHT)); if (GetHorizontalAlignment() != nAlignment) { ModifyStyle((BS_LEFT | BS_CENTER | BS_RIGHT), nAlignment); // ... Alignment changed, redraw button Invalidate(); } } DWORD COXBitmapButton::GetHorizontalAlignment() const { ASSERT(::IsWindow(m_hWnd)); DWORD nReturn=GetStyle() & (BS_LEFT | BS_CENTER | BS_RIGHT); if(nReturn==0) return BS_CENTER; else return nReturn; } void COXBitmapButton::SetVerticalAlignment(DWORD nAlignment /* = BS_VCENTER */) { ASSERT(::IsWindow(m_hWnd)); ASSERT((nAlignment == BS_TOP) || (nAlignment == BS_VCENTER) || (nAlignment == BS_BOTTOM)); if (GetVerticalAlignment() != nAlignment) { ModifyStyle((BS_TOP | BS_VCENTER | BS_BOTTOM), nAlignment); // ... Alignment changed, redraw button Invalidate(); } } DWORD COXBitmapButton::GetVerticalAlignment() const { ASSERT(::IsWindow(m_hWnd)); DWORD nReturn=GetStyle() & (BS_TOP | BS_VCENTER | BS_BOTTOM); if(nReturn==0) return BS_VCENTER; else return nReturn; } COLORREF COXBitmapButton::GetTextColor() const { return m_textColor; } void COXBitmapButton::SetTextColor(COLORREF textColor) { if (textColor != m_textColor) { m_textColor = textColor; // ... Text color changed, redraw the button Invalidate(); } } CFont* COXBitmapButton::GetTextFont() { ASSERT(::IsWindow(m_hWnd)); // Get the font associated with this control CFont* pFont = GetFont(); if (pFont == NULL) { TRACE0("COXBitmapButton::GetTextFont : Failed to get the font\n"); // ... Return an empty font m_textFont.DeleteObject(); } else if (pFont->GetSafeHandle() == (HFONT)m_textFont.m_hObject) { // If the handles are the same, the wrapping object must be the same as well ASSERT(pFont == &m_textFont); } else { // The window font is different from the font we have // (This means the font has not been set with SetTextFont() but with WM_SETFONT) // Make a copy of the window font LOGFONT logFont; m_textFont.DeleteObject(); VERIFY(pFont->GetLogFont(&logFont)); VERIFY(m_textFont.CreateFontIndirect(&logFont)); } return &m_textFont; } void COXBitmapButton::SetTextFont(CFont* pTextFont) { ASSERT(pTextFont != NULL); ASSERT(::IsWindow(m_hWnd)); // Make a copy of the supplied font if necessary BOOL bChanged = FALSE; if (pTextFont->GetSafeHandle() != (HFONT)m_textFont.m_hObject) { LOGFONT logFont; m_textFont.DeleteObject(); VERIFY(pTextFont->GetLogFont(&logFont)); VERIFY(m_textFont.CreateFontIndirect(&logFont)); bChanged = TRUE; } // ... Set the window font SetFont(&m_textFont, bChanged); } CString COXBitmapButton::GetText() const { ASSERT(::IsWindow(m_hWnd)); CString sText; GetWindowText(sText); // Get everything in front of first EndOfLine char return GetSubString(sText, 1, _T('\n')); } void COXBitmapButton::SetText(LPCTSTR pszText) { ASSERT(::IsWindow(m_hWnd)); CString sWindowText(pszText); // ... Get the tooltip text so it can be used again CString sToolTipText = GetToolTipText(); if (!sToolTipText.IsEmpty()) sWindowText += _T("\n") + sToolTipText; SetWindowText(sWindowText); } CString COXBitmapButton::GetToolTipText() const { ASSERT(::IsWindow(m_hWnd)); CString sText; GetWindowText(sText); // Get everything after the first EndOfLine char and before te second return GetSubString(sText, 2, _T('\n')); } void COXBitmapButton::SetToolTipText(LPCTSTR pszToolTipText) { ASSERT(::IsWindow(m_hWnd)); CString sToolTipText(pszToolTipText); // ... Get the text so it can be used again CString sWindowText = GetText(); if (!sToolTipText.IsEmpty()) sWindowText += _T("\n") + sToolTipText; // A tool will be added to (or deleted from) the tool tip control // in the WM_SETTEXT handler SetWindowText(sWindowText); } BOOL COXBitmapButton::GetToolTip() const { ASSERT(::IsWindow(m_hWnd)); // Tooltip is enabled if the tool tip control is creted return (m_toolTip.m_hWnd != NULL); } BOOL COXBitmapButton::SetToolTip(BOOL bEnable /* = TRUE */) { ASSERT(::IsWindow(m_hWnd)); if (bEnable) { if (m_toolTip.m_hWnd != NULL) { if (::IsWindow(m_toolTip.m_hWnd)) { TRACE0("COXBitmapButton::SetToolTip : Tooltip window already exists, continuing\n"); return TRUE; } } if (!m_toolTip.Create(this)) { TRACE0("COXBitmapButton::SetToolTip : Failed to create tool tip control\n"); return FALSE; } m_toolTip.ModifyStyleEx(0,WS_EX_TOPMOST); // ... Set the initial tool tip text CString sToolTipText = GetToolTipText(); CRect rectWindow; GetClientRect(rectWindow); if (!sToolTipText.IsEmpty()) VERIFY(m_toolTip.AddTool(this, sToolTipText, rectWindow, OXBB_TOOLTIP_ID)); m_toolTip.Activate(TRUE); } else if (m_toolTip.m_hWnd != NULL) { m_toolTip.DelTool(this,OXBB_TOOLTIP_ID); m_toolTip.DestroyWindow(); } return TRUE; } CSize COXBitmapButton::GetFitButtonSize() { CSize imageAndGapSize; CSize textAndGapSize; CSize totalSize; // Get the needed sizes for image and text if(GetVerticalAlignment()==BS_VCENTER) { // if vertical alignment is BS_VCENTER then text and image are // located on the same line imageAndGapSize = GetImageSize(); textAndGapSize = GetTextSize(TRUE); if(imageAndGapSize.cx>0 && textAndGapSize.cx>0) { if ((imageAndGapSize.cx != 0) && (imageAndGapSize.cy != 0)) imageAndGapSize += CSize(2 * -m_ptImageOffset.x, 2 * -m_ptImageOffset.y); if ((textAndGapSize.cx != 0) && (textAndGapSize.cy != 0)) // ... Only add text gap if text is non-empty textAndGapSize += CSize(-m_ptTextOffset.x, 2 * -m_ptTextOffset.y); } else if(imageAndGapSize.cx==0) { textAndGapSize += CSize(2 * -m_ptTextOffset.x, 2 * -m_ptTextOffset.y); } else if(textAndGapSize.cx==0) { imageAndGapSize += CSize(2 * -m_ptImageOffset.x, 2 * -m_ptImageOffset.y); } // Use the sum for the widtht and the largest for the height if (imageAndGapSize.cy < textAndGapSize.cy) totalSize.cy = textAndGapSize.cy; else totalSize.cy = imageAndGapSize.cy; totalSize.cx = imageAndGapSize.cx + textAndGapSize.cx; } else { CSize imageSize=GetImageSize(); CSize textSize=GetTextSize(TRUE); imageAndGapSize = imageSize; textAndGapSize = textSize; if ((textSize.cx != 0) && (textSize.cy != 0)) { // ... Set text and gap to correct value if text exists // ... Subtract text and space size from limit size (only height) textAndGapSize += CSize(-m_ptTextOffset.x * 2, -m_ptTextOffset.y); } if ((imageSize.cx != 0) && (imageSize.cy != 0)) { imageAndGapSize += CSize(2 * -m_ptImageOffset.x, 2 * -m_ptImageOffset.y); } else { if ((textSize.cx != 0) && (textSize.cy != 0)) { // ... Set text and gap to correct value if text exists // ... Subtract text and space size from limit size (only height) textAndGapSize += CSize(0, -m_ptTextOffset.y); } } // Use the sum for the height and the largest for the width if (imageAndGapSize.cx < textAndGapSize.cx) totalSize.cx = textAndGapSize.cx; else totalSize.cx = imageAndGapSize.cx; totalSize.cy = imageAndGapSize.cy + textAndGapSize.cy; if(totalSize.cy==0) { totalSize.cy += 2 * -m_ptTextOffset.y; } if(totalSize.cx==0) { totalSize.cx += 2 * -m_ptTextOffset.x; } } totalSize+=GetReservedSpace(); return totalSize; } CSize COXBitmapButton::GetButtonSize() const { ASSERT(::IsWindow(m_hWnd)); CRect clientRect; GetClientRect(clientRect); return clientRect.Size(); } CSize COXBitmapButton::GetImageSize() const { ASSERT(::IsWindow(m_hWnd)); // new stuff if (::IsWindow(m_animateCtrl)) { CRect rect; m_animateCtrl.GetWindowRect(rect); rect.InflateRect(1,1); return rect.Size(); } else { if (m_imageList.m_hImageList == NULL) return CSize(0, 0); IMAGEINFO imageInfo; ::ZeroMemory(&imageInfo, sizeof(imageInfo)); VERIFY(m_imageList.GetImageInfo(m_nNormalImageIndex, &imageInfo)); return CRect(imageInfo.rcImage).Size(); } } CSize COXBitmapButton::GetTextSize(BOOL bCompact) { ASSERT(::IsWindow(m_hWnd)); CWindowDC dc(this); CRect textRect(0, 0, 0, 0); CString sText = GetText(); if (!sText.IsEmpty()) { // ... Use correct font CFont* pOldFont = dc.SelectObject(GetTextFont()); UINT nFormat=DT_SINGLELINE | DT_CALCRECT | DT_LEFT | DT_TOP; VERIFY(dc.DrawText(sText, textRect, nFormat) != 0); CSize sizeImage=GetImageSize(); if(bCompact) { if((GetStyle()&BS_MULTILINE)==BS_MULTILINE) { nFormat=DT_WORDBREAK | DT_CALCRECT | DT_LEFT | DT_TOP; textRect-=textRect.TopLeft(); CRect oldRect(0,0,0,0); while(TRUE) { VERIFY(dc.DrawText(sText, textRect, nFormat) != 0); float nXCoef=(float)textRect.Width()/(float)textRect.Height(); float nYCoef=(float)textRect.Height()/(float)textRect.Width(); if(oldRect==textRect || (nXCoef<(float)2.6 && nYCoef<(float)1)) { break; } oldRect=textRect; if(nXCoef>=2) { textRect.right-=textRect.Width()/3; } else { textRect.right+=textRect.Width()/4; } } } } else { CRect buttonRect; GetClientRect(buttonRect); buttonRect.right-=GetReservedSpace().cx; buttonRect.bottom-=GetReservedSpace().cy; buttonRect.InflateRect(m_ptTextOffset.x,m_ptTextOffset.y); if(sizeImage.cx!=0 && sizeImage.cy!=0) { switch(GetVerticalAlignment()) { case BS_TOP: case BS_BOTTOM: { buttonRect.bottom-=sizeImage.cy-2*m_ptImageOffset.y+ m_ptTextOffset.y; if(buttonRect.bottomScreenToClient(rect); pParent->RedrawWindow(rect, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE); return TRUE; } BOOL COXBitmapButton::RegrabBackground() { // ... Check whether we have the focus BOOL bFocus = (GetFocus() == this); // Hide and show the window (parent background will be repainted // and grabbed during OnEraseBkgnd() // ... Discard a possible previous copy m_bBackgroundGrabbed = FALSE; ShowWindow(SW_HIDE); ShowWindow(SW_SHOWNA); // ... Restore the focus if necessary if (bFocus) SetFocus(); return m_bBackgroundGrabbed; } BOOL COXBitmapButton::SetDefaultCursor(UINT nCursorID /* = 0 */) { // Check whether we are reseting the cursor if (nCursorID == 0) { m_nDefaultCursorID = 0; m_hDefaultCursor = NULL; return TRUE; } // The cursor is read from resource now // (Make sure OXBitmapButton.rc is included in your resource file) HCURSOR hNewCursor = AfxGetApp()->LoadCursor(nCursorID); if (hNewCursor != NULL) { m_nDefaultCursorID = nCursorID; m_hDefaultCursor = hNewCursor; return TRUE; } else { TRACE1("COXBitmapButton::SetDefaultCursor : Failed to load cursor with ID %i\n", nCursorID); return FALSE; } } UINT COXBitmapButton::GetDefaultCursor() const { return m_nDefaultCursorID; } BOOL COXBitmapButton::SetDisabledCursor(UINT nCursorID /* = 0 */) { // Check whether we are reseting the cursor if (nCursorID == 0) { m_nDisabledCursorID = 0; m_hDisabledCursor = NULL; return TRUE; } // The cursor is read from resource now // (Make sure OXBitmapButton.rc is included in your resource file) HCURSOR hNewCursor = AfxGetApp()->LoadCursor(nCursorID); if (hNewCursor != NULL) { m_nDisabledCursorID = nCursorID; m_hDisabledCursor = hNewCursor; return TRUE; } else { TRACE1("COXBitmapButton::SetDisabledCursor : Failed to load cursor with ID %i\n", nCursorID); return FALSE; } } UINT COXBitmapButton::GetDisabledCursor() const { return m_nDisabledCursorID; } BOOL COXBitmapButton::GetPseudoDisableMode() const { return m_bPseudoDisableMode; } BOOL COXBitmapButton::SetPseudoDisableMode(BOOL bPseudoDisableMode /* = TRUE */) { if (m_bPseudoDisableMode != bPseudoDisableMode) { if (bPseudoDisableMode) { // ... Changing from normal mode to pseudo-disable mode m_bEnabled = IsWindowEnabled(); m_bPseudoDisableMode = TRUE; m_bHasTabStop = ((GetStyle() & WS_TABSTOP) == WS_TABSTOP); // ... Always enable the window (in real Windows sense) // and remove tabstop if necessary CButton::EnableWindow(TRUE); EnableWindow(m_bEnabled); } else { // ... Changing from pseudo-disable mode to normal mode m_bPseudoDisableMode = FALSE; if (m_bHasTabStop) ModifyStyle(0, WS_TABSTOP); // ... Call the base class version CButton::EnableWindow(m_bEnabled); } } return TRUE; } BOOL COXBitmapButton::IsWindowEnabled() const { if (!m_bPseudoDisableMode) return CButton::IsWindowEnabled(); else // ... In pseudo-disable mode we return the stored state, // not the actual window state (WS_DISABLED) return m_bEnabled; } BOOL COXBitmapButton::EnableWindow(BOOL bEnable /* = TRUE */) { BOOL bOldEnabled = FALSE; if (!m_bPseudoDisableMode) { // ... Just pass it on to the base class bOldEnabled = CButton::EnableWindow(bEnable); } else { // Store the requested state, but reset the style bOldEnabled = m_bEnabled; m_bEnabled = bEnable; // ... If the window had the tab stop style : add or remove it if (m_bHasTabStop) ModifyStyle(!bEnable ? WS_TABSTOP : 0, bEnable ? WS_TABSTOP : 0); if (bOldEnabled != bEnable) //... Redraw the window after state change Invalidate(); } return bOldEnabled; } BOOL COXBitmapButton::AdjustBackground() { // Get the window rect // ... The button rect expressed in screen coordinates CRect buttonInScreenRect; GetWindowRect(buttonInScreenRect); // ... The button rect expressed in button client coordinates CRect buttonInClientRect; buttonInClientRect = buttonInScreenRect; ScreenToClient(buttonInClientRect); // ... Make sure the border is grabbed as well buttonInScreenRect.right++; buttonInScreenRect.bottom++; // Convert it to parent coordinates CWnd* pParent = GetParent(); ASSERT(pParent != NULL); // ... The parent rect expressed in screen coordinates CRect parentInScreenRect; pParent->GetWindowRect(parentInScreenRect); // ... The button rect expressed in parent coordinates CRect buttonInParentRect = buttonInClientRect; buttonInParentRect += buttonInScreenRect.TopLeft() - parentInScreenRect.TopLeft(); // ... Get the parent's window DC CWindowDC windowDC(pParent); // Copy the image from the screen DC to the memory DC CDC memDC; CBitmap bitmap; VERIFY(memDC.CreateCompatibleDC(&windowDC)); VERIFY(bitmap.CreateCompatibleBitmap(&windowDC, buttonInClientRect.Width(), buttonInClientRect.Height())); // ... Select new bitmap into memory DC CBitmap* pOldBitmap = memDC.SelectObject(&bitmap); // ... Copy the bitmap from the window DC to memory DC memDC.BitBlt(0, 0, buttonInClientRect.Width(), buttonInClientRect.Height(), &windowDC, buttonInParentRect.left, buttonInParentRect.top, SRCCOPY); // ... Restore the old bitmap of the memory DC memDC.SelectObject(pOldBitmap); // ... First delete an image that may be present from previous grabs m_backgroundImage.DeleteImageList(); VERIFY(m_backgroundImage.Create(buttonInClientRect.Width(), buttonInClientRect.Height(), ILC_COLORDDB, 0, 1)); VERIFY(m_backgroundImage.Add(&bitmap, CLR_NONE) == 0); return TRUE; } #ifdef _DEBUG void COXBitmapButton::AssertValid() const { CButton::AssertValid(); } void COXBitmapButton::Dump(CDumpContext& dc) const { CButton::Dump(dc); } #endif //_DEBUG COXBitmapButton::~COXBitmapButton() { } void COXBitmapButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { ASSERT(lpDrawItemStruct != NULL); CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); UINT nState = lpDrawItemStruct->itemState; CRect itemRect = lpDrawItemStruct->rcItem; CRect buttonRect; CRect imageRect; CRect textRect; CRect arrowRect=itemRect; CDC dcCompatible; if(!dcCompatible.CreateCompatibleDC(pDC)) { TRACE(_T("COXBitmapButton::DrawItem:Failed to create compatible DC")); return; } CBitmap bitmap; if(!bitmap.CreateCompatibleBitmap(pDC, itemRect.Width(), itemRect.Height())) { TRACE(_T("COXBitmapButton::DrawItem:Failed to create compatible bitmap")); return; } CBitmap* pOldBitmap=dcCompatible.SelectObject(&bitmap); CFont* pOldFont = dcCompatible.SelectObject(pDC->GetCurrentFont()); CRect rcOriginal=itemRect; itemRect-=itemRect.TopLeft(); // Adjust the state for a possible pseudo-disable mode if (IsWindowEnabled()) nState &= ~ODS_DISABLED; else nState |= ODS_DISABLED; // First distribute the available space (image etc) CRect rectFree=itemRect; rectFree.right-=GetReservedSpace().cx; rectFree.bottom-=GetReservedSpace().cy; arrowRect=rectFree; DistributeSpace(nState, rectFree, buttonRect, imageRect, textRect); // ... Select and realize palette CPalette* pOldCompatiblePalette=NULL; SelectPalette(&dcCompatible,nState,buttonRect,pOldCompatiblePalette); // ... Draw the button borders DrawButton(&dcCompatible, nState, itemRect); // ... Draw the image in its correct state DrawImage(&dcCompatible, nState, &m_imageList, imageRect); DrawText(&dcCompatible, nState, GetText(), textRect); if(HasDropDownArrow()) { arrowRect.left=arrowRect.right; arrowRect.right=arrowRect.left+(m_nDropDownArrowWidth-m_ptArrowOffset.x); arrowRect.InflateRect(0,m_ptArrowOffset.y,m_ptArrowOffset.x,m_ptArrowOffset.y); DrawDropDownArrow(&dcCompatible,nState,arrowRect); } if (nState & ODS_FOCUS) { // ... Draw a focus rectangle on top of the button and image DrawFocusRectangle(&dcCompatible, nState, itemRect, imageRect); } // adjust the position of animate control window if any AVI was loaded if (::IsWindow(m_animateCtrl)) { m_animateCtrl.SetWindowPos(NULL,imageRect.left+1,imageRect.top+1,0,0, SWP_NOSIZE|SWP_NOZORDER); m_animateCtrl.Invalidate(); } CPalette* pPalette=dcCompatible.GetCurrentPalette(); ASSERT(pPalette!=NULL); CPalette* pOldPalette=pDC->SelectPalette(pPalette,FALSE); pDC->RealizePalette(); pDC->BitBlt(rcOriginal.left,rcOriginal.top,rcOriginal.Width(), rcOriginal.Height(),&dcCompatible,0,0,SRCCOPY); // ... Deselect palette RestorePalette(&dcCompatible, nState, buttonRect, pOldCompatiblePalette); if(pOldBitmap) dcCompatible.SelectObject(pOldBitmap); if(pOldFont) dcCompatible.SelectObject(pOldFont); if(pOldPalette!=NULL) pDC->SelectPalette(pOldPalette,FALSE); } // protected: void COXBitmapButton::DistributeSpace(UINT nState, CRect itemRect, CRect& buttonRect, CRect& imageRect, CRect& textRect) { // Let the button use the entire space buttonRect = itemRect; // Take horizontal and vertical alignment into account // ... Get the alignment DWORD nHorizontalAlignment = GetHorizontalAlignment(); DWORD nVerticalAlignment = GetVerticalAlignment(); // ... Outer allowable position for image and text const CRect rectLimit = buttonRect; CPoint originLimit = rectLimit.TopLeft(); CSize sizeLimit = rectLimit.Size(); // Calculate the image size CSize imageSize = GetImageSize(); CSize imageAndGapSize = imageSize; // Calculate the text size CSize textSize = GetTextSize(); CSize textAndGapSize = textSize; // if vertical alignment is BS_VCENTER then text and image are // located on the same line BOOL bSpecialCase = (nVerticalAlignment==BS_VCENTER && imageSize.cx>0 && textSize.cx>0); CPoint textOrigin; CPoint imageOrigin; if(bSpecialCase) { if ((imageSize.cx != 0) && (imageSize.cy != 0)) { imageAndGapSize += CSize(2 * -m_ptImageOffset.x, 2 * -m_ptImageOffset.y); } if ((textSize.cx != 0) && (textSize.cy != 0)) { // ... Set text and gap to correct value if text exists // ... Subtract text and space size from limit size (only width) textAndGapSize += CSize(-m_ptTextOffset.x, 2 * -m_ptTextOffset.y); } // ... Adjust size if already to big if (sizeLimit.cx < textAndGapSize.cx) { textAndGapSize.cx = sizeLimit.cx; textSize.cx = textAndGapSize.cx - (-m_ptTextOffset.x); if (textSize.cx < 0) textSize.cx = 0; } if (sizeLimit.cy < textAndGapSize.cy) { textAndGapSize.cy = sizeLimit.cy; textSize.cy = textAndGapSize.cy - (2 * -m_ptTextOffset.y); if (textSize.cy < 0) textSize.cy = 0; } // ... Subtract text and space size from limit size (only width) sizeLimit.cx -= textAndGapSize.cx; // ... May not have used more size than available ASSERT((0 <= sizeLimit.cx) || (0 <= sizeLimit.cy)); // ... Adjust size if already to big if (sizeLimit.cx < imageAndGapSize.cx) { imageAndGapSize.cx = sizeLimit.cx; imageSize.cx = imageAndGapSize.cx - 2 * (-m_ptImageOffset.x); if (imageSize.cx < 0) imageSize.cx = 0; } if (sizeLimit.cy < imageAndGapSize.cy) { imageAndGapSize.cy = sizeLimit.cy; imageSize.cy = imageAndGapSize.cy - 2 * (-m_ptImageOffset.y); if (imageSize.cy < 0) imageSize.cy = 0; } // ... Subtract image and space size from limit size (only width) sizeLimit.cx -= imageAndGapSize.cx; if(imageSize.cx==0) { textSize.cx = textAndGapSize.cx - (2 * -m_ptTextOffset.x); if (textSize.cx < 0) textSize.cx = 0; } // ... May not have used more size than available // The width that is still left will be used between the image and the border // or the text and the border (not between the text and the image) ASSERT((0 <= sizeLimit.cx) || (0 <= sizeLimit.cy)); // Calculate the image and text position textOrigin.x = originLimit.x + -m_ptTextOffset.x; textOrigin.y = originLimit.y + -m_ptTextOffset.y; imageOrigin = originLimit + -m_ptImageOffset; if (nHorizontalAlignment == BS_RIGHT) { ASSERT(textOrigin.x == originLimit.x + -m_ptTextOffset.x); if(textAndGapSize.cx!=0) { imageOrigin.x = textAndGapSize.cx + -m_ptImageOffset.x; } } else // (nHorizontalAlignment == BS_LEFT) or other { ASSERT(imageOrigin.x == originLimit.x + -m_ptImageOffset.x); if(imageAndGapSize.cx!=0) { textOrigin.x = imageAndGapSize.cx; } } ASSERT(textOrigin.y == originLimit.y + -m_ptTextOffset.y); ASSERT(imageOrigin.y == originLimit.y + -m_ptImageOffset.y); textOrigin.y = (rectLimit.Height() - textAndGapSize.cy) / 2 + -m_ptTextOffset.y; imageOrigin.y = (rectLimit.Height() - imageAndGapSize.cy) / 2 + -m_ptImageOffset.y; } else { if ((textSize.cx != 0) && (textSize.cy != 0)) { // ... Set text and gap to correct value if text exists // ... Subtract text and space size from limit size (only height) textAndGapSize += CSize(-m_ptTextOffset.x * 2, -m_ptTextOffset.y); } if ((imageSize.cx != 0) && (imageSize.cy != 0)) { imageAndGapSize += CSize(2 * -m_ptImageOffset.x, 2 * -m_ptImageOffset.y); if (sizeLimit.cy < textAndGapSize.cy) { textAndGapSize.cy = sizeLimit.cy; textSize.cy = textAndGapSize.cy - (-m_ptTextOffset.y); if (textSize.cy < 0) textSize.cy = 0; } } else { if ((textSize.cx != 0) && (textSize.cy != 0)) { // ... Set text and gap to correct value if text exists // ... Subtract text and space size from limit size (only height) textAndGapSize += CSize(0, -m_ptTextOffset.y); } if (sizeLimit.cy < textAndGapSize.cy) { textAndGapSize.cy = sizeLimit.cy; textSize.cy = textAndGapSize.cy - 2 * (-m_ptTextOffset.y); if (textSize.cy < 0) textSize.cy = 0; } } // ... Adjust size if already to big if (sizeLimit.cx < textAndGapSize.cx) { textAndGapSize.cx = sizeLimit.cx; textSize.cx = textAndGapSize.cx - 2 * (-m_ptTextOffset.x); if (textSize.cx < 0) textSize.cx = 0; } // ... Subtract text and space size from limit size (only height) sizeLimit.cy -= textAndGapSize.cy; // ... May not have used more size than available ASSERT((0 <= sizeLimit.cx) || (0 <= sizeLimit.cy)); // ... Adjust size if already to big if (sizeLimit.cx < imageAndGapSize.cx) { imageAndGapSize.cx = sizeLimit.cx; imageSize.cx = imageAndGapSize.cx - 2 * (-m_ptImageOffset.x); if (imageSize.cx < 0) imageSize.cx = 0; } if (sizeLimit.cy < imageAndGapSize.cy) { imageAndGapSize.cy = sizeLimit.cy; imageSize.cy = imageAndGapSize.cy - 2 * (-m_ptImageOffset.y); if (imageSize.cy < 0) imageSize.cy = 0; } // ... Subtract image and space size from limit size (only height) sizeLimit.cy -= imageAndGapSize.cy; // ... May not have used more size than available // The width that is still left will be used between the image and the border // or the text and the border (not between the text and the image) ASSERT((0 <= sizeLimit.cx) || (0 <= sizeLimit.cy)); // Calculate the image and text position textOrigin = originLimit + -m_ptTextOffset; imageOrigin = originLimit + -m_ptImageOffset; if (nHorizontalAlignment == BS_LEFT) { ASSERT(textOrigin.x == originLimit.x + -m_ptTextOffset.x); ASSERT(imageOrigin.x == originLimit.x + -m_ptImageOffset.x); } else if (nHorizontalAlignment == BS_RIGHT) { textOrigin.x = sizeLimit.cx - textAndGapSize.cx + -m_ptTextOffset.x; imageOrigin.x = sizeLimit.cx - imageAndGapSize.cx + -m_ptImageOffset.x; } else // (nHorizontalAlignment == BS_CENTER) or other { textOrigin.x = (sizeLimit.cx - textAndGapSize.cx) / 2 + -m_ptTextOffset.x; imageOrigin.x = (sizeLimit.cx - imageAndGapSize.cx) / 2 + -m_ptImageOffset.x; } // Take vertical alignment into account if (nVerticalAlignment == BS_TOP) { // ... Image at top, text underneath ASSERT(imageOrigin.y == originLimit.y + -m_ptImageOffset.y); if(imageAndGapSize.cy>0) { textOrigin.y = originLimit.y + imageAndGapSize.cy; } } else if (nVerticalAlignment == BS_BOTTOM) { // ... Image at bottom, text above it imageOrigin.y = rectLimit.Height() - (imageSize.cy + -m_ptImageOffset.y); if(imageAndGapSize.cy==0) { textOrigin.y = rectLimit.Height() - (textSize.cy + -m_ptTextOffset.y); } } else // (nVerticalAlignment == BS_VCENTER) or other { ASSERT(textOrigin.y == originLimit.y + -m_ptTextOffset.y); ASSERT(imageOrigin.y == originLimit.y + -m_ptImageOffset.y); // ... Image and text vertically centered (text under image) int nImageAndAndTextAndAllGaps = imageAndGapSize.cy + textAndGapSize.cy; int nTopBottomSpace = (rectLimit.Height() - nImageAndAndTextAndAllGaps) / 2; imageOrigin.y = nTopBottomSpace + -m_ptImageOffset.y; if(imageAndGapSize.cy>0) { textOrigin.y = nTopBottomSpace + imageAndGapSize.cy; } else { textOrigin.y = nTopBottomSpace + -m_ptTextOffset.y; } } } // Assign image result if ((imageSize.cx != 0) && (imageSize.cy != 0)) imageRect = CRect(imageOrigin, imageSize); else // ... No image to show imageRect.SetRectEmpty(); // Assign text result if ((textSize.cx != 0) && (textSize.cy != 0)) textRect = CRect(textOrigin, textSize); else // ... No text to show textRect.SetRectEmpty(); // Add small adjustments if necessary if ((nState & ODS_SELECTED) == ODS_SELECTED) { // ... If button is pressed : move the image and text a little bit // to bottom right if(!imageRect.IsRectEmpty()) imageRect += m_ptDownOffset; if(!textRect.IsRectEmpty()) textRect += m_ptDownOffset; } else if (IsChecked()) { // ... If button is pressed : move the image and text a little bit // to bottom right if(!imageRect.IsRectEmpty()) imageRect += m_ptCheckedOffset; if(!textRect.IsRectEmpty()) textRect += m_ptCheckedOffset; } else if (((nState & ODS_DISABLED) != ODS_DISABLED) && m_bHyperLook && m_bMouseOverButton) { // ... Hyper look with mouse over button and not disabled : // move the image and text a little bit to top left if(!imageRect.IsRectEmpty()) imageRect += m_ptHyperOffset; if(!textRect.IsRectEmpty()) textRect += m_ptHyperOffset; } } void COXBitmapButton::SelectPalette(CDC* pDC, UINT /* nState */, CRect /* buttonRect */, CPalette*& pOldPalette) // --- In : pDC : Device context to draw on // nState : The state of the button // buttonRect : The place where to draw the button // --- Out : pOldPalette : The previous (old) palette // --- Returns : // --- Effect : This function may select a palette into the DC and realize it // It may also use the oldPalette parameter to store the old palette { pOldPalette = NULL; if ((HPALETTE)m_palette != NULL) { pOldPalette = pDC->SelectPalette(&m_palette, FALSE); pDC->RealizePalette(); } } void COXBitmapButton::DrawButton(CDC* pDC, UINT nState, CRect buttonRect) // --- In : pDC : Device context to draw on // nState : The state of the button // buttonRect : The place where to draw the button // --- Out : // --- Returns : // --- Effect : Draw the button look (with image etc.) in the correct state (pressed etc) { if (m_bHyperLook) { // Draw our own background while in hyper look mode // ... Background should have been grabbed already if(m_bBackgroundGrabbed) { VERIFY(m_backgroundImage.Draw(pDC, 0, buttonRect.TopLeft(), ILD_TRANSPARENT)); } return; } if (IsIndeterminate()) { // ... indeterminate pDC->DrawFrameControl(buttonRect, DFC_BUTTON , DFCS_BUTTONPUSH | DFCS_PUSHED); } else if (IsChecked() && ((m_bTrackLook && m_bMouseOverButton) || IsDropDownButton())) { // ... checked pDC->DrawFrameControl(buttonRect, DFC_BUTTON , DFCS_BUTTONPUSH | DFCS_PUSHED); } else if (IsChecked()) { // ... checked pDC->DrawFrameControl(buttonRect, DFC_BUTTON , DFCS_BUTTONPUSH | DFCS_CHECKED); } else if ((nState & ODS_SELECTED) == ODS_SELECTED) { // ... Pressed in pDC->DrawFrameControl(buttonRect, DFC_BUTTON , DFCS_BUTTONPUSH | DFCS_PUSHED); } else if (m_bTrackLook && (!m_bMouseOverButton || ((nState & ODS_DISABLED) == ODS_DISABLED))) { // ... Track look with mouse not over button or disabled state CBrush buttonBrush(m_defaultButtonColor); pDC->FillRect(buttonRect, &buttonBrush); } else if ((nState & ODS_DISABLED) == ODS_DISABLED) { // ... Disabled pDC->DrawFrameControl(buttonRect, DFC_BUTTON , DFCS_BUTTONPUSH | DFCS_INACTIVE); } else if ((nState & ODS_FOCUS) == ODS_FOCUS) { // ... Normal look or Track look with mouse over button (focused) CRect rect=buttonRect; if(!m_bTrackLook) rect.DeflateRect(1,1); pDC->DrawFrameControl(rect, DFC_BUTTON , DFCS_BUTTONPUSH); } else { // ... Normal look or Track look with mouse over button pDC->DrawFrameControl(buttonRect, DFC_BUTTON , DFCS_BUTTONPUSH); } } void COXBitmapButton::DrawImage(CDC* pDC, UINT nState, CImageList* pImageList, CRect imageRect) // --- In : pDC : Device context to draw on // nState : The state of the button // pImageList : The image list containing the images to draws // imageRect : The place where to draw the image // --- Out : // --- Returns : // --- Effect : Draw the correct image according to the state (disabled etc) { if ((pImageList == NULL) || (pImageList->m_hImageList == NULL) || imageRect.left==imageRect.right || imageRect.top==imageRect.bottom) // ... Must have the image list created before we can draw something return; // Select the image to draw // ... Other scenarios int nImageIndex = m_nNormalImageIndex; if (!m_bHyperLook && ((nState & ODS_DISABLED) == ODS_DISABLED)) // .. Disabled look (never used in hyper look) nImageIndex = m_nDisabledImageIndex; else if (m_bTrackLook && !m_bHyperLook && !m_bMouseOverButton && ((nState & ODS_SELECTED) != ODS_SELECTED)) // ... Track look with mouse not over button and button not pressed // and not in hyper look mode nImageIndex = m_nInactiveImageIndex; else if (IsIndeterminate()) // ... Track look with mouse not over button and button not pressed // and not in hyper look mode nImageIndex = m_nInactiveImageIndex; // Check whether image index is valid if (pImageList->GetImageCount() <= nImageIndex) { // ... Should have at least one image ASSERT(1 <= pImageList->GetImageCount()); // ... The needed image is not available, use the normal image (best we can do) nImageIndex = m_nNormalImageIndex; } // Draw the image VERIFY(pImageList->Draw(pDC, nImageIndex, imageRect.TopLeft(), ILD_TRANSPARENT)); } void COXBitmapButton::DrawText(CDC* pDC, UINT nState, CString sText, CRect textRect) // --- In : pDC : Device context to draw on // nState : The state of the button // sText : The text to draws // textRect : The place where to draw the text // --- Out : // --- Returns : // --- Effect : Draw the text in the correct state (disabled etc) { if (sText.IsEmpty() || textRect.left==textRect.right || textRect.top==textRect.bottom) // ... Nothing to do : return immediately return; DWORD nHorizontalAlignment = GetHorizontalAlignment(); UINT nFormat=((GetStyle()&BS_MULTILINE)==BS_MULTILINE ? DT_WORDBREAK : DT_SINGLELINE) | (nHorizontalAlignment==BS_LEFT ? DT_LEFT : (nHorizontalAlignment==BS_RIGHT ? DT_RIGHT : DT_CENTER))| DT_VCENTER; // ... Set the correct text color COLORREF oldColor = pDC->SetTextColor(GetTextColor()); int nOldBkMode=pDC->SetBkMode(TRANSPARENT); // Draw the text if (!m_bHyperLook && ((nState & ODS_DISABLED) == ODS_DISABLED)) { // ... Disabled look if (!::DrawState(pDC->m_hDC, NULL, (DRAWSTATEPROC)CallbackDrawState, (LPARAM)this, (WPARAM)nFormat, textRect.left, textRect.top, textRect.Width(), textRect.Height(),DSS_DISABLED)) { // DrawState may fail on some platforms (Win NT 3.51) // Than we draw a gray string (better than nothing) CBrush brush; brush.CreateStockObject(::GetSysColor(COLOR_GRAYTEXT)); VERIFY(pDC->GrayString(&brush, CallbackGrayString, (LPARAM)this, 0, textRect.left, textRect.top, textRect.Width(), textRect.Height())); } } else if(IsIndeterminate()) { // ... Indeterminate state pDC->SetTextColor(GetSysColor(COLOR_BTNSHADOW)); VERIFY(pDC->DrawText(sText, textRect, nFormat) != 0); } else if (!m_bHyperLook && m_bTrackLook && !m_bMouseOverButton) { // ... Track look with mouse not over button : Use black text color pDC->SetTextColor(oldColor); oldColor = pDC->SetTextColor(RGB(0, 0, 0)); VERIFY(pDC->DrawText(sText, textRect, nFormat) != 0); } else // ... Normal look VERIFY(pDC->DrawText(sText, textRect, nFormat) != 0); // ... Reset the color pDC->SetTextColor(oldColor); pDC->SetBkMode(nOldBkMode); } void COXBitmapButton::DrawFocusRectangle(CDC* pDC, UINT nState, CRect buttonRect, CRect imageRect) // --- In : pDC : Device context to draw on // buttonRect : The place where the button was drawn // imageRect : The place where the image was drawn // --- Out : // --- Returns : // --- Effect : Draw the focus rectangles { // Handle hyper look mode seperately if (m_bHyperLook) { // Center the focus mark in the image (not in the button) CRect innerFocusRect = imageRect; if(innerFocusRect.IsRectEmpty()) innerFocusRect=buttonRect; CSize deltaSize = innerFocusRect.Size() - m_hyperFocusSize; deltaSize.cx = -deltaSize.cx / 2; deltaSize.cy = -deltaSize.cy / 2; innerFocusRect.InflateRect(deltaSize); // Draw a focus circle CPen* pOldPen = (CPen*)pDC->SelectStockObject(BLACK_PEN); CBrush* pOldBrush = (CBrush*)pDC->SelectStockObject(HOLLOW_BRUSH); pDC->Ellipse(innerFocusRect); pDC->SelectObject(pOldPen); pDC->SelectObject(pOldBrush); return; } // Draw inner focus rect CRect innerFocusRect = buttonRect; innerFocusRect.InflateRect(m_ptInnerFocusOffset.x, m_ptInnerFocusOffset.y); pDC->DrawFocusRect(&innerFocusRect); // Draw outer focus rect (if not in track look mode) if (!m_bTrackLook && !IsChecked() && !IsIndeterminate()) { CBrush blackBrush; VERIFY(blackBrush.CreateStockObject(BLACK_BRUSH)); CRect outerFocusRect = buttonRect; outerFocusRect.InflateRect(m_ptOuterFocusOffset.x, m_ptOuterFocusOffset.y); pDC->FrameRect(outerFocusRect, &blackBrush); if ((nState & ODS_SELECTED) == ODS_SELECTED) { outerFocusRect.DeflateRect(1,1); CBrush greyBrush(::GetSysColor(COLOR_BTNSHADOW)); pDC->FrameRect(outerFocusRect, &greyBrush); } } } void COXBitmapButton::RestorePalette(CDC* pDC, UINT /* nState */, CRect /* buttonRect */, CPalette* pOldPalette) // --- In : pDC : Device context to draw on // nState : The state of the button // buttonRect : The place where to draw the button // pOldPalette : The previous (old) palette (may be NULL) // --- Out : // --- Returns : // --- Effect : This function may deselect a palette from the DC { if (pOldPalette != NULL) pDC->SelectPalette(pOldPalette, FALSE); } BOOL COXBitmapButton::BuildGrayBitmap(LPCTSTR lpszBitmapResource, COLORREF crMask, CBitmap* pGrayBitmap) // --- In : lpszBitmapResource : The bitmap resource to use // crMask : Mask color // pGrayBitmap : A (not yet attached) bitmap object // --- Out : pGrayBitmap : The attached bitmap object // --- Returns : Whether it was successful or not // --- Effect : Converts the supplied bitmap to a gray scale bitmap // The color crMask is not converted { // Bitmap object should exist but not yet be attached ASSERT(pGrayBitmap != NULL); ASSERT(pGrayBitmap->m_hObject == NULL); // Get the bitmap resource data HINSTANCE hInstance = NULL; HRSRC hResourceInfoBlock = NULL; HGLOBAL hBitmapGlobal = NULL; HGLOBAL hBitmapCopy = NULL; DWORD nResourceSize = 0; LPBITMAPINFOHEADER pBitmapInfoHeader = NULL; hInstance = AfxFindResourceHandle(lpszBitmapResource, RT_BITMAP); hResourceInfoBlock = ::FindResource(hInstance, lpszBitmapResource, RT_BITMAP); hBitmapGlobal = ::LoadResource(hInstance, hResourceInfoBlock); if (hBitmapGlobal == NULL) { TRACE0("COXBitmapButton::BuildGrayBitmap : Failed to load bitmap resource, failing\n"); return FALSE; } nResourceSize = ::SizeofResource(hInstance, hResourceInfoBlock); ASSERT(sizeof(BITMAPINFOHEADER) <= nResourceSize); // Memory may be read only, make a copy void* pOldBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hBitmapGlobal); hBitmapCopy = ::GlobalAlloc(GMEM_MOVEABLE, nResourceSize); pBitmapInfoHeader = (LPBITMAPINFOHEADER)::GlobalLock(hBitmapCopy); ASSERT(pBitmapInfoHeader != NULL); ::CopyMemory(pBitmapInfoHeader, pOldBitmapInfoHeader, nResourceSize); // Change the bitmap color table to gray scale BOOL bGraySuccess = MakeGray(pBitmapInfoHeader, crMask); // Build the bitmap HBITMAP hBitmap = NULL; if (bGraySuccess) { int nNumColors = (1 << pBitmapInfoHeader->biBitCount); CClientDC dc(NULL); hBitmap = ::CreateDIBitmap(dc.m_hDC, pBitmapInfoHeader, CBM_INIT, (LPBYTE)pBitmapInfoHeader + pBitmapInfoHeader->biSize + nNumColors * sizeof(RGBQUAD), (LPBITMAPINFO)pBitmapInfoHeader, DIB_RGB_COLORS); pGrayBitmap->Attach(hBitmap); } BOOL bBitmapSuccess = (hBitmap != NULL); #ifdef _DEBUG if (!bBitmapSuccess) TRACE0("COXBitmapButton::BuildGrayBitmap : Failed to create the bitmap\n"); #endif // _DEBUG // Clean up ::UnlockResource(hBitmapGlobal); if ((::GlobalUnlock(hBitmapCopy) == 0) && (::GetLastError() == NO_ERROR)) VERIFY(::GlobalFree(hBitmapCopy) == NULL); return bGraySuccess && bBitmapSuccess; } BOOL COXBitmapButton::BuildGrayIcon(LPCTSTR lpszIconResource, HICON* phGrayIcon) // --- In : lpszBitmapResource : The bitmap resource to use // crMask : Mask color // pGrayBitmap : A (not yet attached) bitmap object // --- Out : pGrayBitmap : The attached bitmap object // --- Returns : Whether it was successful or not // --- Effect : Converts the supplied icon to a gray scale icon { // Icon object should exist but not yet be attached ASSERT(phGrayIcon != NULL); ASSERT(AfxIsValidAddress(phGrayIcon, sizeof(HICON*))); ASSERT(*phGrayIcon == NULL); // Get the icon resource data HINSTANCE hInstance = NULL; HRSRC hResourceInfoBlock = NULL; HGLOBAL hGroupIconGlobal = NULL; HGLOBAL hIconGlobal = NULL; HGLOBAL hIconCopy = NULL; DWORD nResourceSize = 0; LPGRPICONDIR pGroupIconDir = NULL; LPICONIMAGE pIconImage = NULL; LPBITMAPINFOHEADER pBitmapInfoHeader = NULL; WORD nIconID = 0; // First get the icon directory hInstance = AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON); hResourceInfoBlock = ::FindResource(hInstance, lpszIconResource, RT_GROUP_ICON); hGroupIconGlobal = ::LoadResource(hInstance, hResourceInfoBlock); if (hGroupIconGlobal == NULL) { TRACE0("COXBitmapButton::BuildGrayIcon : Failed to load icon group resource, failing\n"); return FALSE; } nResourceSize = ::SizeofResource(hInstance, hResourceInfoBlock); ASSERT(sizeof(GRPICONDIR) <= nResourceSize); pGroupIconDir = (LPGRPICONDIR)::LockResource(hGroupIconGlobal); ASSERT(pGroupIconDir != NULL); ASSERT(pGroupIconDir->idType == 1); ASSERT(0 < pGroupIconDir->idCount); nIconID = pGroupIconDir->idEntries[0].nID; if ((::GlobalUnlock(hGroupIconGlobal) == 0) && (::GetLastError() == NO_ERROR)) VERIFY(::GlobalFree(hGroupIconGlobal) == NULL); // Then get the icon itself hResourceInfoBlock = ::FindResource(hInstance, MAKEINTRESOURCE(nIconID), RT_ICON); hIconGlobal = ::LoadResource(hInstance, hResourceInfoBlock); if (hIconGlobal == NULL) { TRACE0("COXBitmapButton::BuildGrayIcon : Failed to load icon resource, failing\n"); return FALSE; } nResourceSize = ::SizeofResource(hInstance, hResourceInfoBlock); ASSERT(sizeof(ICONIMAGE) <= nResourceSize); // Memory may be read only, make a copy void* pOldBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hIconGlobal); ASSERT(pOldBitmapInfoHeader != NULL); hIconCopy = ::GlobalAlloc(GMEM_MOVEABLE, nResourceSize); pBitmapInfoHeader = (LPBITMAPINFOHEADER)::GlobalLock(hIconCopy); ASSERT(pBitmapInfoHeader != NULL); ::CopyMemory(pBitmapInfoHeader, pOldBitmapInfoHeader, nResourceSize); pIconImage = (LPICONIMAGE)pBitmapInfoHeader; // Change the bitmap color table to gray scale BOOL bGraySuccess = MakeGray(pBitmapInfoHeader); // Build the icon if (bGraySuccess) *phGrayIcon = ::CreateIconFromResource((PBYTE)pIconImage, nResourceSize, TRUE, 0x00030000); BOOL bIconSuccess = (*phGrayIcon != NULL); #ifdef _DEBUG if (!bIconSuccess) TRACE0("COXBitmapButton::BuildGrayBitmap : Failed to create the icon\n"); #endif // _DEBUG // Clean up ::UnlockResource(hIconGlobal); if ((::GlobalUnlock(hIconCopy) == 0) && (::GetLastError() == NO_ERROR)) VERIFY(::GlobalFree(hIconCopy) == NULL); return bGraySuccess && bIconSuccess; } BOOL COXBitmapButton::MakeGray(LPBITMAPINFOHEADER pBitmapInfoHeader, COLORREF crMask /* = CLR_NONE */) // --- In : pBitmapInfoHeader : The bitmap data to convert // crMask : The color to skip during conversion // --- Out : // --- Returns : Whether the gray bitmap could be created // --- Effect : Chenges to color entries of the specified bitmap to gray scale { // See also 'Retrieving Palette Information from a Bitmap Resource' // MSDN PSS ID Number: Q124947 ASSERT(pBitmapInfoHeader != NULL); ASSERT(AfxIsValidAddress(pBitmapInfoHeader, sizeof(BITMAPINFOHEADER))); // Check palette existance int nNumColors = 0; if (pBitmapInfoHeader->biBitCount <= 8) nNumColors = (1 << pBitmapInfoHeader->biBitCount); else // ... No palette used for 24 BPP DIB nNumColors = 0; if (nNumColors == 0) { TRACE0("COXBitmapButton::MakeGray : Resource does not have a palette, failing\n"); return FALSE; } // Change the colors to gray scale int nColor; int nWhite; LPBITMAPINFO pBitmapInfo = (LPBITMAPINFO)pBitmapInfoHeader; ASSERT(AfxIsValidAddress(pBitmapInfo, sizeof(BITMAPINFO))); COLORREF orgColor; for (nColor = 0; nColor < nNumColors; nColor++) { // ... This formula will compute the 'white factor' of a color // It translates an RGB value to a gray scale value orgColor = RGB(pBitmapInfo->bmiColors[nColor].rgbRed, pBitmapInfo->bmiColors[nColor].rgbGreen, pBitmapInfo->bmiColors[nColor].rgbBlue); nWhite = (pBitmapInfo->bmiColors[nColor].rgbRed * 77 + pBitmapInfo->bmiColors[nColor].rgbGreen * 153 + pBitmapInfo->bmiColors[nColor].rgbBlue * 25 ) / 255; // Round the value to 0, 128, 192 or 255 (gray scale colors which are always available) if (nWhite < 64) nWhite = 0; // ... [0, 63] else if (nWhite < 160) nWhite = 128; // ... [64, 159] else if (nWhite < 224) nWhite = 192; // ... [160, 223] else nWhite = 255; // ... [224, 255] if (orgColor != crMask) { // Change the color entries in the bitmap pBitmapInfo->bmiColors[nColor].rgbRed = (BYTE)nWhite; pBitmapInfo->bmiColors[nColor].rgbGreen = (BYTE)nWhite; pBitmapInfo->bmiColors[nColor].rgbBlue = (BYTE)nWhite; } } return TRUE; } BOOL COXBitmapButton::BuildDisabledImage(HICON hSourceIcon, CSize imageSize, HICON& hDestIcon) // --- In : hSourceIcon : Icon conating the image // imageSize : Size of the image // --- Out : hDestIcon : An icon containing the disabled image // --- Returns : Whether the disabled icon could be created // --- Effect : Creates an icon that contains the disabled look of the supplied icon { CBitmap helperBitmap; CClientDC dc(NULL); // Create a mem DC with a new bitmap in it CDC memDC; VERIFY(memDC.CreateCompatibleDC(&dc)); VERIFY(helperBitmap.CreateCompatibleBitmap(&dc, imageSize.cx, imageSize.cy)); CBitmap* pOldBitmap = (CBitmap*)memDC.SelectObject(&helperBitmap); // ... Default background is the button color memDC.FillSolidRect(CRect(CPoint(0,0), imageSize), m_defaultButtonColor); // Draw the source icon on the mem DC in a disabled state BOOL bSuccess = memDC.DrawState(CPoint(0, 0), imageSize, hSourceIcon, DSS_DISABLED, (HBRUSH)NULL); memDC.SelectObject(pOldBitmap); // Convert the disabled bitmap to an icon by using the same mask as the source icon ICONINFO iconInfo; ::ZeroMemory(&iconInfo, sizeof(iconInfo)); // ... ::GetIconInfo craetes two new bitmaps (color and mask) VERIFY(::GetIconInfo(hSourceIcon, &iconInfo)); // ... Delete the color bitmap ::DeleteObject(iconInfo.hbmColor); iconInfo.hbmColor = NULL; iconInfo.hbmColor = helperBitmap; hDestIcon = ::CreateIconIndirect(&iconInfo); // ... Delete the mask bitmap ::DeleteObject(iconInfo.hbmMask); iconInfo.hbmMask = NULL; return bSuccess; } BOOL COXBitmapButton::GetBitmapPalette(LPCTSTR lpszBitmapResource, CPalette& palette) // --- In : lpszBitmapResource : The bitmap resource to use // --- Out : palette : The palette of the bitmap // --- Returns : Whether it was successful or not // --- Effect : Extracts the palette of a bitmap resource { // Get the bitmap resource data HINSTANCE hInstance = NULL; HRSRC hResourceInfoBlock = NULL; HGLOBAL hBitmapGlobal = NULL; HGLOBAL hBitmapCopy = NULL; DWORD nResourceSize = 0; LPBITMAPINFOHEADER pBitmapInfoHeader = NULL; hInstance = AfxFindResourceHandle(lpszBitmapResource, RT_BITMAP); hResourceInfoBlock = ::FindResource(hInstance, lpszBitmapResource, RT_BITMAP); hBitmapGlobal = ::LoadResource(hInstance, hResourceInfoBlock); if (hBitmapGlobal == NULL) { TRACE0("COXBitmapButton::GetBitmapPalette : Failed to load bitmap resource, failing\n"); return FALSE; } nResourceSize = ::SizeofResource(hInstance, hResourceInfoBlock); ASSERT(sizeof(BITMAPINFOHEADER) <= nResourceSize); // Memory may be read only, make a copy void* pOldBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hBitmapGlobal); hBitmapCopy = ::GlobalAlloc(GMEM_MOVEABLE, nResourceSize); pBitmapInfoHeader = (LPBITMAPINFOHEADER)::GlobalLock(hBitmapCopy); ASSERT(pBitmapInfoHeader != NULL); ::CopyMemory(pBitmapInfoHeader, pOldBitmapInfoHeader, nResourceSize); // Change the bitmap color table to gray scale BOOL bPaletteSuccess = GetImagePalette(pBitmapInfoHeader, palette); // Clean up ::UnlockResource(hBitmapGlobal); if ((::GlobalUnlock(hBitmapCopy) == 0) && (::GetLastError() == NO_ERROR)) VERIFY(::GlobalFree(hBitmapCopy) == NULL); return bPaletteSuccess; } BOOL COXBitmapButton::GetIconPalette(LPCTSTR lpszIconResource, CPalette& palette) // --- In : lpszIconResource : The icon resource to use // --- Out : palette : The palette of the icon // --- Returns : Whether it was successful or not // --- Effect : Extracts the palette of an icon resource { // Get the icon resource data HINSTANCE hInstance = NULL; HRSRC hResourceInfoBlock = NULL; HGLOBAL hGroupIconGlobal = NULL; HGLOBAL hIconGlobal = NULL; HGLOBAL hIconCopy = NULL; DWORD nResourceSize = 0; LPGRPICONDIR pGroupIconDir = NULL; LPICONIMAGE pIconImage = NULL; LPBITMAPINFOHEADER pBitmapInfoHeader = NULL; WORD nIconID = 0; // First get the icon directory hInstance = AfxFindResourceHandle(lpszIconResource, RT_GROUP_ICON); hResourceInfoBlock = ::FindResource(hInstance, lpszIconResource, RT_GROUP_ICON); hGroupIconGlobal = ::LoadResource(hInstance, hResourceInfoBlock); if (hGroupIconGlobal == NULL) { TRACE0("COXBitmapButton::GetIconPalette : Failed to load icon group resource, failing\n"); return FALSE; } nResourceSize = ::SizeofResource(hInstance, hResourceInfoBlock); ASSERT(sizeof(GRPICONDIR) <= nResourceSize); pGroupIconDir = (LPGRPICONDIR)::LockResource(hGroupIconGlobal); ASSERT(pGroupIconDir != NULL); ASSERT(pGroupIconDir->idType == 1); ASSERT(0 < pGroupIconDir->idCount); nIconID = pGroupIconDir->idEntries[0].nID; if ((::GlobalUnlock(hGroupIconGlobal) == 0) && (::GetLastError() == NO_ERROR)) VERIFY(::GlobalFree(hGroupIconGlobal) == NULL); // Then get the icon itself hResourceInfoBlock = ::FindResource(hInstance, MAKEINTRESOURCE(nIconID), RT_ICON); hIconGlobal = ::LoadResource(hInstance, hResourceInfoBlock); if (hIconGlobal == NULL) { TRACE0("COXBitmapButton::GetIconPalette : Failed to load icon resource, failing\n"); return FALSE; } nResourceSize = ::SizeofResource(hInstance, hResourceInfoBlock); ASSERT(sizeof(ICONIMAGE) <= nResourceSize); // Memory may be read only, make a copy void* pOldBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hIconGlobal); ASSERT(pOldBitmapInfoHeader != NULL); hIconCopy = ::GlobalAlloc(GMEM_MOVEABLE, nResourceSize); pBitmapInfoHeader = (LPBITMAPINFOHEADER)::GlobalLock(hIconCopy); ASSERT(pBitmapInfoHeader != NULL); ::CopyMemory(pBitmapInfoHeader, pOldBitmapInfoHeader, nResourceSize); pIconImage = (LPICONIMAGE)pBitmapInfoHeader; // Change the bitmap color table to gray scale BOOL bPaletteSuccess = GetImagePalette(pBitmapInfoHeader, palette); // Clean up ::UnlockResource(hIconGlobal); if ((::GlobalUnlock(hIconCopy) == 0) && (::GetLastError() == NO_ERROR)) VERIFY(::GlobalFree(hIconCopy) == NULL); return bPaletteSuccess; } BOOL COXBitmapButton::GetImagePalette(LPBITMAPINFOHEADER pBitmapInfoHeader, CPalette& palette) // --- In : pBitmapInfoHeader : The image data // --- Out : palette : The palette of the image // --- Returns : Whether it was successful or not // --- Effect : Extracts the palette of an image { // ... Initialize output parameter if (palette.m_hObject != NULL) palette.DeleteObject(); // Check the palette existance int nNumColors = 0; if (pBitmapInfoHeader->biBitCount <= 8) nNumColors = (1 << pBitmapInfoHeader->biBitCount); else // ... No palette used for 24 BPP DIB nNumColors = 0; if (nNumColors == 0) { TRACE0("COXBitmapButton::GetImagePalette : Resource does not have a palette, failing\n"); return FALSE; } // Create the logical palette BYTE* pBuffer = NULL; LPLOGPALETTE pLogPalette = NULL; const int nBufferSize = sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * nNumColors; pBuffer = new BYTE[nBufferSize]; ::ZeroMemory(pBuffer, nBufferSize); pLogPalette = (LPLOGPALETTE)pBuffer; // Initialize the palette pLogPalette->palVersion = 0x300; pLogPalette->palNumEntries = (unsigned short)nNumColors; // Get the palette colors LPBITMAPINFO pBitmapInfo = (LPBITMAPINFO)pBitmapInfoHeader; ASSERT(AfxIsValidAddress(pBitmapInfo, sizeof(BITMAPINFO))); int nColor; for (nColor = 0; nColor < nNumColors; nColor++) { pLogPalette->palPalEntry[nColor].peRed = pBitmapInfo->bmiColors[nColor].rgbRed; pLogPalette->palPalEntry[nColor].peGreen = pBitmapInfo->bmiColors[nColor].rgbGreen; pLogPalette->palPalEntry[nColor].peBlue = pBitmapInfo->bmiColors[nColor].rgbBlue; pLogPalette->palPalEntry[nColor].peFlags = 0; } // Build the palette from the logical palette BOOL bPaletteSuccess = palette.CreatePalette(pLogPalette); #ifdef _DEBUG if (!bPaletteSuccess) TRACE0("COXBitmapButton::GetImagePalette : Failed to create the palette\n"); #endif // _DEBUG // Clean up delete[] pBuffer; return TRUE; } void COXBitmapButton::CheckTrackLook(CPoint point) // --- In : point : The current position of the mouse in screen coordinates // --- Out : // --- Returns : // --- Effect : Sets the correct look if in track look mode { if (!m_bTrackLook && !::IsWindow(m_animateCtrl)) // Only do something when we are in track look mode return; // Check whether the mouse is over the button now CRect rcWindow; GetWindowRect(rcWindow); BOOL bNewMouseOverButton = rcWindow.PtInRect(point); if (bNewMouseOverButton && IsWindowEnabled()) { // Moved inside this control window // ... Do capture the mouse input while the mouse is still down // (may have triggered an action, e.g. show msgbox) if (!m_bMouseDown) SetCapture(); // ... When we changed state invalidate the window and get the focus // so that pressing a key will have the right effect if (m_bTrackLook && !m_bMouseOverButton) { BOOL bRedraw=!IsIndeterminate(); if(GetFocus()!=this) { // don't set focus - there is no that much sense in it (the only // thing we miss is kry board input - we'll get it when user click // on the button) // SetFocus(); bRedraw=TRUE; } if(bRedraw) Invalidate(); } m_bMouseOverButton = TRUE; // new stuff if(IsWindowEnabled() && ::IsWindow(m_animateCtrl) && !m_bPlaying) { m_animateCtrl.Play(0,(UINT)-1,1); m_bPlaying=TRUE; } } else { // ... We release the mouse capture (may have already lost it) if (!m_bMouseDown) ReleaseCapture(); // ... When we changed state invalidate the window if (m_bTrackLook && m_bMouseOverButton) { BOOL bRedraw=!IsIndeterminate(); if(bRedraw) Invalidate(); } m_bMouseOverButton = FALSE; // new stuff if(m_bPlaying && !m_bMouseOverButton) { m_bPlaying = FALSE; } } } void COXBitmapButton::PostCheckTrackLook() // --- In : // --- Out : // --- Returns : // --- Effect : Checks the current state of the track look // Should be called by function which might release the mouse capture { if ((m_bTrackLook || ::IsWindow(m_animateCtrl)) && m_bMouseOverButton && (GetCapture() != this)) { // We have lost the capture m_bMouseOverButton = FALSE; if (m_bTrackLook) { BOOL bRedraw=!IsIndeterminate(); if(bRedraw) Invalidate(); } // Recheck the mouse position as soon as possible PostMessage(WM_CHECK_TRACK_LOOK); } } CString COXBitmapButton::GetSubString(LPCTSTR pszFullString, int nSubIndex, TCHAR cDelimiter) // --- In : pszFullString : The full string // nSubIndex : The ONE-based index of the substring requested // cDelimiter : Delimiter character used between all substrings // --- Out : // --- Returns : The requested substring or an empty string otherwise // --- Effect : { ASSERT(0 < nSubIndex); CString sSubString; if (pszFullString == NULL) { // Nothing to search : nothing to find ASSERT(sSubString.IsEmpty()); return sSubString; } // Set pszStart to first charecter and pszEnd after last charecter LPCTSTR pszBegin = pszFullString; LPCTSTR pszEnd = pszFullString + _tcslen(pszFullString); LPCTSTR pszDelimiter = _tcschr(pszBegin, cDelimiter); if (pszDelimiter == NULL) pszDelimiter = pszEnd; ASSERT((*pszDelimiter == cDelimiter) || (*pszDelimiter == _T('\0'))); ASSERT(pszBegin <= pszDelimiter); while (--nSubIndex != 0) { if (*pszDelimiter == _T('\0')) { // Search to end of string and not found ASSERT(sSubString.IsEmpty()); return sSubString; } pszBegin = pszDelimiter + 1; pszDelimiter = _tcschr(pszBegin, cDelimiter); if (pszDelimiter == NULL) pszDelimiter = pszEnd; ASSERT((*pszDelimiter == cDelimiter) || (*pszDelimiter == _T('\0'))); ASSERT(pszBegin <= pszDelimiter); } int nLen = PtrToInt(pszDelimiter - pszBegin); ASSERT(0 <= nLen); LPTSTR pszSubString = sSubString.GetBufferSetLength(nLen); ::CopyMemory(pszSubString, pszBegin, nLen * sizeof(TCHAR)); return sSubString; } CString COXBitmapButton::RemoveAmpersand(LPCTSTR pszText) // --- In : pszText : The input string // --- Out : // --- Returns : The input string but with all single ampersands removed and // all double ampersands replaced by a single // --- Effect : { if (pszText == NULL) return _T(""); CString sResult; BOOL bPreviousWasAmpersand = FALSE; BOOL bThisIsAmpersand = FALSE; LPTSTR pszResult = sResult.GetBuffer(PtrToInt(_tcslen(pszText))); while (*pszText != _T('\0')) { bThisIsAmpersand = (*pszText == _T('&')); if (bPreviousWasAmpersand || !bThisIsAmpersand) { // ... Prev was ampersand or this one is not (or both), copy *(pszResult++) = *pszText; } bPreviousWasAmpersand = bThisIsAmpersand && !bPreviousWasAmpersand; pszText++; } *pszResult = _T('\0'); sResult.ReleaseBuffer(); return sResult; } BOOL COXBitmapButton::LoadBitmap(LPCTSTR lpszBitmapResource, CBitmap& bitmap, CPalette& palette) // --- In : lpszBitmapResource : The bitmap resource // --- Out : bitmap : The bitmap loaded (if successful) // palette : The bitmap loaded (if successful) // --- Returns : Whether the function could load the bitmap successsfully // --- Effect : Loads a bitmap and its palette from resource // This function also works for 256-color bitmaps // (which ::LoadBitmpa does not) // See also "How to Use a DIB Stored as a Windows Resource" // Article ID: Q67883 : // When the display is a 256-color 8514 unit, the same action [LoadBitmap()] // will map the 256 bitmap colors into the 20 reserved system colors, // and an 8 bits-per-pixel bitmap will be returned. { // ... Assume failure BOOL bPaletteSuccess = FALSE; BOOL bBitmapSuccess = FALSE; // ... First delete a possible previous bitmap contents bitmap.DeleteObject(); // ... Without deleting the palette, reloading the bitmap would fail // Thanks to Ali palette.DeleteObject(); // Load bitmap // ... Bitmap and palette object should not yet be attached ASSERT(bitmap.m_hObject == NULL); ASSERT(palette.m_hObject == NULL); // ... Get the bitmap data HINSTANCE hInstance = NULL; HRSRC hResourceInfoBlock = NULL; HGLOBAL hBitmapGlobal = NULL; HGLOBAL hBitmapCopy = NULL; DWORD nResourceSize = 0; LPBITMAPINFOHEADER pBitmapInfoHeader = NULL; LPBITMAPINFOHEADER pCopyBitmapInfoHeader = NULL; hInstance = AfxFindResourceHandle(lpszBitmapResource, RT_BITMAP); hResourceInfoBlock = ::FindResource(hInstance, lpszBitmapResource, RT_BITMAP); hBitmapGlobal = ::LoadResource(hInstance, hResourceInfoBlock); if (hBitmapGlobal == NULL) { TRACE0("COXBitmapButton::LoadBitmap : Failed to load bitmap resource, failing\n"); return FALSE; } nResourceSize = ::SizeofResource(hInstance, hResourceInfoBlock); ASSERT(sizeof(BITMAPINFOHEADER) <= nResourceSize); // Memory may be read only, make a copy pBitmapInfoHeader = (LPBITMAPINFOHEADER)::LockResource(hBitmapGlobal); hBitmapCopy = ::GlobalAlloc(GMEM_MOVEABLE, nResourceSize); pCopyBitmapInfoHeader = (LPBITMAPINFOHEADER)::GlobalLock(hBitmapCopy); ASSERT(pCopyBitmapInfoHeader != NULL); ::CopyMemory(pCopyBitmapInfoHeader, pBitmapInfoHeader, nResourceSize); // Get the bitmap color table bPaletteSuccess = GetImagePalette(pCopyBitmapInfoHeader, palette); if (!bPaletteSuccess) TRACE0("COXBitmapButton::LoadBitmap : Failed to load palette, continuing\n"); // Get the bitmap bits itself DWORD nPaletteByteSize = 0; LPVOID pBitmapBits = NULL; if (pCopyBitmapInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) { if (pCopyBitmapInfoHeader->biBitCount <= 8) nPaletteByteSize = (1 << pCopyBitmapInfoHeader->biBitCount) * sizeof(RGBQUAD); } else { if (((LPBITMAPCOREHEADER)pCopyBitmapInfoHeader)->bcBitCount <= 8) nPaletteByteSize = (1 << ((LPBITMAPCOREHEADER)pCopyBitmapInfoHeader)->bcBitCount) * sizeof(RGBTRIPLE); } pBitmapBits = (LPBYTE)pCopyBitmapInfoHeader + pCopyBitmapInfoHeader->biSize + nPaletteByteSize; { CWindowDC dc(NULL); CPalette* pOldPalette = NULL; if (bPaletteSuccess) { dc.SelectPalette(&palette, FALSE); dc.RealizePalette(); } HBITMAP hBitmap = ::CreateDIBitmap (dc.GetSafeHdc(), pCopyBitmapInfoHeader, CBM_INIT, pBitmapBits, (LPBITMAPINFO)pCopyBitmapInfoHeader, DIB_RGB_COLORS); bBitmapSuccess = (hBitmap != NULL); if (bBitmapSuccess) bitmap.Attach(hBitmap); else TRACE0("COXBitmapButton::LoadBitmap : Failed to load bitmap, failing\n"); if (pOldPalette != NULL) dc.SelectPalette (pOldPalette, FALSE); } ::UnlockResource(hBitmapGlobal); if ((::GlobalUnlock(hBitmapCopy) == 0) && (::GetLastError() == NO_ERROR)) VERIFY(::GlobalFree(hBitmapCopy) == NULL); // ... Return whether successful or not return bBitmapSuccess; } // private: // ========================================================================== void COXBitmapButton::OnSysColorChange() { CButton::OnSysColorChange(); // Get the new button color m_defaultButtonColor = ::GetSysColor(COLOR_BTNFACE); // Redraw the button Invalidate(); } void COXBitmapButton::OnMouseMove(UINT nFlags, CPoint point) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; // Check whether the mouse is over the button now ClientToScreen(&point); CheckTrackLook(point); CButton::OnMouseMove(nFlags, point); } void COXBitmapButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; if(nChar==VK_RETURN) { Toggle(); } CButton::OnKeyDown(nChar, nRepCnt, nFlags); } void COXBitmapButton::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; if(nChar==VK_SPACE) { Toggle(); } CButton::OnKeyUp(nChar, nRepCnt, nFlags); // The previous function may have released the mouse capture, // so recheck the track look PostCheckTrackLook(); } void COXBitmapButton::OnKillFocus(CWnd* pNewWnd) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; CButton::OnKillFocus(pNewWnd); // The previous function may have released the mouse capture, // so recheck the track look // PostCheckTrackLook(); } void COXBitmapButton::OnMButtonUp(UINT nFlags, CPoint point) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; CButton::OnMButtonUp(nFlags, point); // The previous function may have released the mouse capture, // so recheck the track look PostCheckTrackLook(); } void COXBitmapButton::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; CButton::OnSysKeyDown(nChar, nRepCnt, nFlags); } void COXBitmapButton::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; CButton::OnSysKeyUp(nChar, nRepCnt, nFlags); // The previous function may have released the mouse capture, // so recheck the track look PostCheckTrackLook(); } void COXBitmapButton::OnLButtonDown(UINT nFlags, CPoint point) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; m_bMouseDown = TRUE; CButton::OnLButtonDown(nFlags, point); // The previous function may have released the mouse capture, // so recheck the track look PostCheckTrackLook(); } void COXBitmapButton::OnLButtonUp(UINT nFlags, CPoint point) { if (!IsWindowEnabled()) // ... Do nothing when this window is disabled return; CRect rect; GetWindowRect(rect); ClientToScreen(&point); if(rect.PtInRect(point)) { if((GetState()&0x0004)>0) { Toggle(); } } CButton::OnLButtonUp(nFlags, point); m_bMouseDown = FALSE; // The previous function may have released the mouse capture, // so recheck the track look PostCheckTrackLook(); } LRESULT COXBitmapButton::OnClick(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); // ... Do nothing when this window is disabled if(IsWindowEnabled()) { // Toggle(); } return Default(); } LRESULT COXBitmapButton::OnSetText(WPARAM wParam, LPARAM lParam) { LRESULT result = DefWindowProc(WM_SETTEXT, wParam, lParam); // Adjust a possible tool tip control's text if (m_toolTip.m_hWnd != 0) { // Get everything after the first EndOfLine char and before te second CString sToolTipText = GetSubString((LPCTSTR)lParam, 2, _T('\n')); if (!sToolTipText.IsEmpty()) { m_toolTip.DelTool(this,OXBB_TOOLTIP_ID); CRect rectWindow; GetClientRect(rectWindow); VERIFY(m_toolTip.AddTool(this, sToolTipText, rectWindow, OXBB_TOOLTIP_ID)); } else { m_toolTip.DelTool(this,OXBB_TOOLTIP_ID); } } return result; } BOOL COXBitmapButton::PreTranslateMessage(MSG* pMsg) { // ... Pass the message on to the tool tip if (m_toolTip.m_hWnd != NULL) { m_toolTip.Activate(TRUE); m_toolTip.RelayEvent(pMsg); } return CButton::PreTranslateMessage(pMsg); } LRESULT COXBitmapButton::OnCheckTrackLook(WPARAM wParam, LPARAM lParam) { ASSERT(wParam == 0); ASSERT(lParam == 0); UNUSED(wParam); UNUSED(lParam); // Check whether the mouse is over the button now CPoint ptCursor; VERIFY(::GetCursorPos(&ptCursor)); CheckTrackLook(ptCursor); return TRUE; } BOOL COXBitmapButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { // Use our own cursor if we have one if (IsWindowEnabled()) { if (m_hDefaultCursor != NULL) { ::SetCursor(m_hDefaultCursor); // ... We handled the message return TRUE; } } else { if (m_hDisabledCursor != NULL) { ::SetCursor(m_hDisabledCursor); // ... We handled the message return TRUE; } } // ... Call base class implementation return CButton::OnSetCursor(pWnd, nHitTest, message); } BOOL COXBitmapButton::OnEraseBkgnd(CDC* pDC) { if(m_bHyperLook) { // Do not fill the background when in hyper look mode if(!m_bBackgroundGrabbed) { // Grab the backround on the first OnEraseBackground() call m_bBackgroundGrabbed=AdjustBackground(); } return TRUE; } else { return CButton::OnEraseBkgnd(pDC); } } void COXBitmapButton::OnSize(UINT nType, int cx, int cy) { CButton::OnSize(nType, cx, cy); // ... Because the button changed size, the background image should be grabbed again if (m_bHyperLook) RegrabBackground(); if(GetToolTip()) SetToolTip(TRUE); } void COXBitmapButton::OnMove(int x, int y) { CButton::OnMove(x, y); // ... Because the button moved, the background image should be grabbed again if (m_bHyperLook) RegrabBackground(); } void COXBitmapButton::OnEnable(BOOL bEnable) { CButton::OnEnable(bEnable); // The previous function may have changed the enable state of the button // so recheck the track look PostCheckTrackLook(); } int COXBitmapButton::GetDropDownArrowWidth() { HDC hDC = ::GetDC(NULL); ASSERT(hDC != NULL); HFONT hFont=NULL; HFONT oldFont=NULL; int nDropDownArrowWidth=0; if((hFont=CreateFont(GetSystemMetrics(SM_CYMENUCHECK), 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett")))!=NULL) { oldFont = (HFONT)SelectObject(hDC, hFont); } VERIFY(GetCharWidth(hDC, '6', '6', &nDropDownArrowWidth)); if(oldFont!=NULL) { SelectObject(hDC, oldFont); } if(hFont!=NULL) { DeleteObject(hFont); } ::ReleaseDC(NULL, hDC); return nDropDownArrowWidth; } void COXBitmapButton::DrawDropDownArrow(CDC* pDC, UINT nState, CRect arrowRect) { ASSERT(IsDropDownButton()); if(!HasDropDownArrow()) { arrowRect.SetRectEmpty(); } COLORREF clrHilight=::GetSysColor(COLOR_BTNHILIGHT); COLORREF clrShadow=::GetSysColor(COLOR_BTNSHADOW); if(IsDrawingDropdownSeparator() && !(m_bTrackLook && !m_bMouseOverButton)) { CRect dividerRect = arrowRect; dividerRect.left=dividerRect.left-3; dividerRect.right=dividerRect.left+2; pDC->Draw3dRect(dividerRect,clrShadow,clrHilight); } arrowRect.left-=1; if ((nState&ODS_SELECTED)==ODS_SELECTED) { arrowRect+=m_ptDownOffset; } else if (IsChecked()) { arrowRect+=m_ptCheckedOffset; } CFont font; CFont* pOldFont=NULL; if ((font.CreateFont(GetSystemMetrics(SM_CYMENUCHECK), 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett"))) != NULL) { pOldFont=(CFont*)pDC->SelectObject(&font); } int nOldBkMode=pDC->SetBkMode(TRANSPARENT); COLORREF oldTextColor=CLR_NONE; if((nState&ODS_DISABLED)==ODS_DISABLED) { oldTextColor=pDC->SetTextColor(clrShadow); } if((GetStyleEx()&OXBB_EX_DROPDOWNRIGHT)==OXBB_EX_DROPDOWNRIGHT) { pDC->DrawText(_T("8"),arrowRect,DT_CENTER|DT_VCENTER|DT_SINGLELINE); } else { pDC->DrawText(_T("6"),arrowRect,DT_CENTER|DT_VCENTER|DT_SINGLELINE); } if((nState&ODS_DISABLED)==ODS_DISABLED) { pDC->SetTextColor(oldTextColor); } pDC->SetBkMode(nOldBkMode); if(pOldFont != NULL) { pDC->SelectObject(pOldFont); } } void COXBitmapButton::SetStyleEx(DWORD dwStyleEx, BOOL bRedraw/*=TRUE*/) { ASSERT(dwStyleEx&OXBB_EX_DROPDOWN || dwStyleEx&OXBB_EX_DROPDOWNRIGHT || dwStyleEx&OXBB_EX_DROPDOWNNOARROW || dwStyleEx&OXBB_EX_TOGGLE || dwStyleEx&OXBB_EX_TOGGLE3STATE || dwStyleEx==0); ASSERT(!((dwStyleEx&OXBB_EX_TOGGLE || dwStyleEx&OXBB_EX_TOGGLE3STATE) && (dwStyleEx&OXBB_EX_DROPDOWN || dwStyleEx&OXBB_EX_DROPDOWNRIGHT || dwStyleEx&OXBB_EX_DROPDOWNNOARROW))); if(m_dwStyleEx!=dwStyleEx) { m_dwStyleEx=dwStyleEx; if(bRedraw && ::IsWindow(GetSafeHwnd())) RedrawWindow(); } } DWORD COXBitmapButton::GetStyleEx() { return m_dwStyleEx; } BOOL COXBitmapButton::IsDropDownButton() { return (GetStyleEx()&OXBB_EX_DROPDOWN)==OXBB_EX_DROPDOWN; } BOOL COXBitmapButton::HasDropDownArrow() { return (IsDropDownButton() && (GetStyleEx()&OXBB_EX_DROPDOWNNOARROW)!=OXBB_EX_DROPDOWNNOARROW); } BOOL COXBitmapButton::IsToggleButton() { return (GetStyleEx()&OXBB_EX_TOGGLE)==OXBB_EX_TOGGLE; } BOOL COXBitmapButton::OnClicked() { ASSERT(::IsWindow(m_hWnd)); if(IsDropDownButton()) { CWnd* pParentWnd=GetParent(); ASSERT(pParentWnd); NMHDR hdr; hdr.hwndFrom=GetSafeHwnd(); hdr.idFrom=GetDlgCtrlID(); hdr.code=OXBBN_DROPDOWN; if(!pParentWnd->SendMessage(WM_NOTIFY,hdr.idFrom,(LPARAM)&hdr)) { OnDropDown(); } Toggle(); RedrawWindow(); } if(IsToggleButton()) { CWnd* pParentWnd=GetParent(); ASSERT(pParentWnd); NMHDR hdr; hdr.hwndFrom=GetSafeHwnd(); hdr.idFrom=GetDlgCtrlID(); hdr.code=OXBBN_TOGGLE; if(!pParentWnd->SendMessage(WM_NOTIFY,hdr.idFrom,(LPARAM)&hdr)) { OnToggle(); } } return FALSE; } void COXBitmapButton::OnDropDown() { ASSERT(::IsWindow(m_hWnd)); ASSERT(IsDropDownButton()); } void COXBitmapButton::OnToggle() { ASSERT(::IsWindow(m_hWnd)); ASSERT(IsToggleButton()); } BOOL COXBitmapButton::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Add your specialized code here and/or call the base class cs.style |= BS_OWNERDRAW; return CButton::PreCreateWindow(cs); } void COXBitmapButton::SetStateEx(DWORD dwStateEx, BOOL bRedraw/*=TRUE*/) { ASSERT(dwStateEx&OXBB_STATE_CHECKED || dwStateEx&OXBB_STATE_INDETERMINATE || dwStateEx==0); if(m_dwStateEx!=dwStateEx) { m_dwStateEx=dwStateEx; if(bRedraw && ::IsWindow(GetSafeHwnd())) RedrawWindow(); } } DWORD COXBitmapButton::GetStateEx() { return m_dwStateEx; } BOOL COXBitmapButton::Toggle() { BOOL bChecked=FALSE; if(IsToggleButton() || IsDropDownButton()) { bChecked=IsChecked(); if(!bChecked) { SetStateEx(GetStateEx()|OXBB_STATE_CHECKED); } else { if(IsToggleButton() && (GetStyleEx()&OXBB_EX_TOGGLE3STATE)==OXBB_EX_TOGGLE3STATE) { if((GetStateEx()&OXBB_STATE_INDETERMINATE)==OXBB_STATE_INDETERMINATE) { SetStateEx(GetStateEx()&~(OXBB_STATE_INDETERMINATE|OXBB_STATE_CHECKED)); } else { SetStateEx(GetStateEx()|OXBB_STATE_INDETERMINATE); } } else { SetStateEx(GetStateEx()&~OXBB_STATE_CHECKED); } } bChecked=IsChecked(); } return bChecked; } BOOL COXBitmapButton::IsChecked() { return ((GetStateEx()&OXBB_STATE_CHECKED)==OXBB_STATE_CHECKED); } BOOL COXBitmapButton::IsIndeterminate() { return ((GetStateEx()&OXBB_STATE_INDETERMINATE)==OXBB_STATE_INDETERMINATE); } BOOL CALLBACK CallbackDrawState(HDC hdc, LPARAM lData, WPARAM wData, int cx, int cy) { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif COXBitmapButton* pButton=(COXBitmapButton*)lData; CDC* pDC=CDC::FromHandle(hdc); CRect textRect(0,0,cx,cy); return pDC->DrawText(pButton->GetText(), textRect, PtrToUint(wData)); } BOOL CALLBACK CallbackGrayString(HDC hdc, LPARAM lData, int nCount) { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif UNREFERENCED_PARAMETER(nCount); COXBitmapButton* pButton=(COXBitmapButton*)lData; UINT nFormat=((pButton->GetStyle()&BS_MULTILINE)==BS_MULTILINE ? DT_WORDBREAK : DT_SINGLELINE) | DT_LEFT | DT_TOP; CDC* pDC=CDC::FromHandle(hdc); CRect textRect; pDC->GetClipBox(textRect); return pDC->DrawText(pButton->GetText(), textRect, nFormat); } CSize COXBitmapButton::GetReservedSpace() { CSize sizeReserved(0,0); if(HasDropDownArrow()) { sizeReserved.cx+=m_nDropDownArrowWidth-m_ptArrowOffset.x; } return sizeReserved; } void COXBitmapButton::OnLButtonDblClk(UINT nFlags, CPoint point) { ::SendMessage((HWND)(m_hWnd),WM_LBUTTONDOWN,0,MAKELPARAM(point.x,point.y)); CButton::OnLButtonDblClk(nFlags, point); }