3433 lines
95 KiB
C++
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);
|
|
} |