2025-11-27 16:46:48 +09:00

3433 lines
95 KiB
C++

// ==========================================================================
// 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.bottom<buttonRect.top)
buttonRect.SetRectEmpty();
break;
}
case BS_VCENTER:
{
buttonRect.right-=sizeImage.cx-2*m_ptImageOffset.x+
m_ptTextOffset.x;
if(buttonRect.right<buttonRect.left)
buttonRect.SetRectEmpty();
break;
}
default:
ASSERT(FALSE);
}
}
if((GetStyle()&BS_MULTILINE)==BS_MULTILINE)
{
textRect=buttonRect;
nFormat=DT_WORDBREAK | DT_CALCRECT | DT_LEFT | DT_TOP;
VERIFY(dc.DrawText(sText, textRect, nFormat) != 0);
}
if(sizeImage.cx!=0 && sizeImage.cy!=0)
{
switch(GetVerticalAlignment())
{
case BS_TOP:
case BS_BOTTOM:
{
textRect.right=__min(textRect.Width(),buttonRect.Width());
textRect.bottom=buttonRect.Height();
break;
}
case BS_VCENTER:
{
textRect.right=buttonRect.Width();
textRect.bottom=__min(textRect.Height(),buttonRect.Height());
break;
}
default:
ASSERT(FALSE);
}
textRect.left=0;
textRect.top=0;
}
}
dc.SelectObject(pOldFont);
}
return textRect.Size();
}
BOOL COXBitmapButton::GetHyperLook() const
{
return m_bHyperLook;
}
BOOL COXBitmapButton::SetHyperLook(BOOL bHyperLook /* = TRUE */)
{
if(m_bHyperLook == bHyperLook)
return TRUE;
// ... Set the mode state
m_bHyperLook = bHyperLook;
// ... Change the cursors by default
SetDefaultCursor(bHyperLook ? IDC_OX_HAND_CURSOR : 0);
SetDisabledCursor(bHyperLook ? IDC_OX_NO_HAND_CURSOR : 0);
if (bHyperLook)
{
// ... Use the pseuco-disable mode (necessary for cursors)
SetPseudoDisableMode(TRUE);
// ... Regrab the background
m_bBackgroundGrabbed = FALSE;
}
CRect rect;
GetWindowRect(rect);
CWnd* pParent = GetParent();
ASSERT(pParent != NULL);
pParent->ScreenToClient(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);
}