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