You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3448 lines
101 KiB
3448 lines
101 KiB
#include "ctlspriv.h"
|
|
#pragma hdrstop
|
|
#include "usrctl32.h"
|
|
#include "button.h"
|
|
|
|
//
|
|
// ButtonCalcRect codes
|
|
//
|
|
#define CBR_CLIENTRECT 0
|
|
#define CBR_CHECKBOX 1
|
|
#define CBR_CHECKTEXT 2
|
|
#define CBR_GROUPTEXT 3
|
|
#define CBR_GROUPFRAME 4
|
|
#define CBR_PUSHBUTTON 5
|
|
#define CBR_RADIOBUTTON 6
|
|
|
|
|
|
#define Button_IsThemed(pbutn) ((pbutn)->hTheme && (pbutn)->hImage == NULL)
|
|
|
|
//---------------------------------------------------------------------------//
|
|
CONST BYTE mpStyleCbr[] =
|
|
{
|
|
CBR_PUSHBUTTON, // BS_PUSHBUTTON
|
|
CBR_PUSHBUTTON, // BS_DEFPUSHBUTTON
|
|
CBR_CHECKTEXT, // BS_CHECKBOX
|
|
CBR_CHECKTEXT, // BS_AUTOCHECKBOX
|
|
CBR_CHECKTEXT, // BS_RADIOBUTTON
|
|
CBR_CHECKTEXT, // BS_3STATE
|
|
CBR_CHECKTEXT, // BS_AUTO3STATE
|
|
CBR_GROUPTEXT, // BS_GROUPBOX
|
|
CBR_CLIENTRECT, // BS_USERBUTTON
|
|
CBR_CHECKTEXT, // BS_AUTORADIOBUTTON
|
|
CBR_CLIENTRECT, // BS_PUSHBOX
|
|
CBR_CLIENTRECT, // BS_OWNERDRAW
|
|
};
|
|
|
|
#define IMAGE_BMMAX IMAGE_CURSOR+1
|
|
static CONST BYTE rgbType[IMAGE_BMMAX] =
|
|
{
|
|
BS_BITMAP, // IMAGE_BITMAP
|
|
BS_ICON, // IMAGE_CURSOR
|
|
BS_ICON // IMAGE_ICON
|
|
};
|
|
|
|
#define IsValidImage(imageType, realType, max) \
|
|
((imageType < max) && (rgbType[imageType] == realType))
|
|
|
|
typedef struct tagBTNDATA
|
|
{
|
|
LPTSTR pszText; // Text string
|
|
INT cchText; // char count of string
|
|
PBUTN pbutn; // Button data
|
|
WORD wFlags; // Alignment flags
|
|
} BTNDATA, *LPBTNDATA;
|
|
|
|
//---- to support multiple themes in a single process, move these into PBUTN ----
|
|
static SIZE sizeCheckBox = {0};
|
|
static SIZE sizeRadioBox = {0};
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Forwards
|
|
//
|
|
VOID Button_DrawPush(PBUTN pbutn, HDC hdc, UINT pbfPush);
|
|
VOID GetCheckBoxSize(HDC hdc, PBUTN pbutn, BOOL fCheckBox, LPSIZE psize);
|
|
WORD GetAlignment(PBUTN pbutn);
|
|
VOID Button_CalcRect(PBUTN pbutn, HDC hdc, LPRECT lprc, int iCode, UINT uFlags);
|
|
VOID Button_MultiExtent(WORD wFlags, HDC hdc, LPRECT lprcMax, LPTSTR lpsz, INT cch, PINT pcx, PINT pcy);
|
|
|
|
__inline UINT IsPushButton(PBUTN pbutn);
|
|
__inline ULONG GetButtonType(ULONG ulWinStyle);
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// InitButtonClass() - Registers the control's window class
|
|
//
|
|
BOOL InitButtonClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
wc.lpfnWndProc = Button_WndProc;
|
|
wc.lpszClassName = WC_BUTTON;
|
|
wc.style = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = sizeof(PBUTN);
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
|
|
return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS));
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_GetThemeIds() - Gets the associated iPartId and iStateId needed for
|
|
// the theme manager APIs for the button control passed
|
|
// in pbutn.
|
|
//
|
|
HRESULT Button_GetThemeIds(PBUTN pbutn, LPINT piPartId, LPINT piStateId)
|
|
{
|
|
if ( piPartId )
|
|
{
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
|
|
if (IsPushButton(pbutn))
|
|
{
|
|
*piPartId = BP_PUSHBUTTON;
|
|
}
|
|
else
|
|
{
|
|
|
|
switch (GetButtonType(ulStyle))
|
|
{
|
|
case BS_CHECKBOX:
|
|
case BS_AUTOCHECKBOX:
|
|
case BS_3STATE:
|
|
case BS_AUTO3STATE:
|
|
*piPartId = BP_CHECKBOX;
|
|
break;
|
|
|
|
case BS_RADIOBUTTON:
|
|
case BS_AUTORADIOBUTTON:
|
|
*piPartId = BP_RADIOBUTTON;
|
|
break;
|
|
|
|
case BS_GROUPBOX:
|
|
*piPartId = BP_GROUPBOX;
|
|
break;
|
|
|
|
case BS_OWNERDRAW:
|
|
//
|
|
// don't do anything with owerdrawn buttons
|
|
//
|
|
return E_FAIL;
|
|
|
|
default:
|
|
TraceMsg(TF_STANDARD, "What kind of buttonType is this, %#.2x", GetButtonType(ulStyle));
|
|
*piPartId = BP_PUSHBUTTON;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (piStateId)
|
|
{
|
|
switch (*piPartId)
|
|
{
|
|
case BP_PUSHBUTTON:
|
|
if ((pbutn->buttonState & BST_PUSHED) ||
|
|
((pbutn->buttonState & (BST_CHECKED|BST_HOT)) == BST_CHECKED))
|
|
{
|
|
*piStateId = PBS_PRESSED;
|
|
}
|
|
else if (!IsWindowEnabled(pbutn->ci.hwnd))
|
|
{
|
|
*piStateId = PBS_DISABLED;
|
|
}
|
|
else if (pbutn->buttonState & BST_HOT)
|
|
{
|
|
*piStateId = PBS_HOT;
|
|
}
|
|
else if (ulStyle & BS_DEFPUSHBUTTON)
|
|
{
|
|
*piStateId = PBS_DEFAULTED;
|
|
}
|
|
else
|
|
{
|
|
*piStateId = PBS_NORMAL;
|
|
}
|
|
break;
|
|
|
|
case BP_CHECKBOX:
|
|
case BP_RADIOBUTTON:
|
|
//
|
|
// NOTE (phellyar): We're relying on the order of the RADIOBUTTONSTATES and
|
|
// CHECKBOXSTATES enums in tmdefs.h to calculate the correct
|
|
// StateId. If the ordering of those enums changes, revisit
|
|
// the logic here.
|
|
// Note also that CHECKBOXSTATES is a super set of
|
|
// RADIOBUTTONSTATES which is why we're using CBS_* here.
|
|
//
|
|
if ( pbutn->buttonState & BST_CHECKED )
|
|
{
|
|
//
|
|
// button is checked
|
|
//
|
|
*piStateId = CBS_CHECKEDNORMAL;
|
|
}
|
|
else if ( pbutn->buttonState & BST_INDETERMINATE )
|
|
{
|
|
//
|
|
// button is intedeterminate
|
|
//
|
|
*piStateId = CBS_MIXEDNORMAL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// button is unchecked
|
|
//
|
|
*piStateId = CBS_UNCHECKEDNORMAL;
|
|
}
|
|
|
|
if ( pbutn->buttonState & BST_PUSHED )
|
|
{
|
|
//
|
|
// being pressed
|
|
//
|
|
*piStateId += 2;
|
|
}
|
|
else if (!IsWindowEnabled(pbutn->ci.hwnd))
|
|
{
|
|
//
|
|
// disabled
|
|
//
|
|
*piStateId += 3;
|
|
}
|
|
else if (pbutn->buttonState & BST_HOT )
|
|
{
|
|
//
|
|
// mouse over
|
|
//
|
|
*piStateId += 1;
|
|
}
|
|
|
|
break;
|
|
|
|
case BP_GROUPBOX:
|
|
if (!IsWindowEnabled(pbutn->ci.hwnd))
|
|
{
|
|
*piStateId = GBS_DISABLED;
|
|
}
|
|
else
|
|
{
|
|
*piStateId = GBS_NORMAL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_GetTextFlags() - Returns the DrawTextEx flags that should be used
|
|
// when rendering text for this control, needed by
|
|
// DrawThemeText.
|
|
//
|
|
DWORD Button_GetTextFlags(PBUTN pbutn)
|
|
{
|
|
DWORD dwTextFlags = 0;
|
|
WORD wAlign = GetAlignment(pbutn);
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
|
|
//
|
|
// Set up text flags
|
|
//
|
|
|
|
//
|
|
// horizontal text alignment
|
|
//
|
|
switch (wAlign & HIBYTE(BS_HORZMASK))
|
|
{
|
|
case HIBYTE(BS_LEFT):
|
|
dwTextFlags |= DT_LEFT;
|
|
break;
|
|
|
|
case HIBYTE(BS_RIGHT):
|
|
dwTextFlags |= DT_RIGHT;
|
|
break;
|
|
|
|
case HIBYTE(BS_CENTER):
|
|
dwTextFlags |= DT_CENTER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// vertical text alignment
|
|
//
|
|
switch (wAlign & HIBYTE(BS_VERTMASK))
|
|
{
|
|
case HIBYTE(BS_TOP):
|
|
dwTextFlags |= DT_TOP;
|
|
break;
|
|
|
|
case HIBYTE(BS_BOTTOM):
|
|
dwTextFlags |= DT_BOTTOM;
|
|
break;
|
|
|
|
case HIBYTE(BS_VCENTER):
|
|
dwTextFlags |= DT_VCENTER;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// line break
|
|
//
|
|
if (ulStyle & BS_MULTILINE)
|
|
{
|
|
dwTextFlags |= (DT_WORDBREAK | DT_EDITCONTROL);
|
|
}
|
|
else
|
|
{
|
|
dwTextFlags |= DT_SINGLELINE;
|
|
}
|
|
|
|
|
|
if (ulStyle & SS_NOPREFIX)
|
|
{
|
|
dwTextFlags |= DT_NOPREFIX;
|
|
}
|
|
|
|
//
|
|
// Draw the underscore for accelorators?
|
|
//
|
|
if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
|
|
{
|
|
dwTextFlags |= DT_HIDEPREFIX;
|
|
}
|
|
|
|
return dwTextFlags;
|
|
}
|
|
|
|
DWORD ButtonStateToCustomDrawState(PBUTN pbutn)
|
|
{
|
|
DWORD itemState = 0;
|
|
if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN))
|
|
{
|
|
itemState |= CDIS_SHOWKEYBOARDCUES;
|
|
}
|
|
|
|
if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
|
|
{
|
|
itemState |= CDIS_SHOWKEYBOARDCUES;
|
|
}
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_FOCUS)
|
|
{
|
|
itemState |= CDIS_FOCUS;
|
|
}
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_PUSHED)
|
|
{
|
|
itemState |= CDIS_SELECTED;
|
|
}
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_HOT)
|
|
{
|
|
itemState |= CDIS_HOT;
|
|
}
|
|
|
|
if (!IsWindowEnabled(pbutn->ci.hwnd))
|
|
{
|
|
itemState |= CDIS_DISABLED;
|
|
}
|
|
|
|
return itemState;
|
|
}
|
|
|
|
|
|
void Button_GetImagePosition(PBUTN pbutn, RECT* prc, int* px, int* py)
|
|
{
|
|
int cx = 0;
|
|
int cy = 0;
|
|
CCGetIconSize(&pbutn->ci, pbutn->himl, &cx, &cy);
|
|
|
|
cx += pbutn->rcIcon.left + pbutn->rcIcon.right;
|
|
cy += pbutn->rcIcon.top + pbutn->rcIcon.bottom;
|
|
switch (pbutn->uAlign)
|
|
{
|
|
case BUTTON_IMAGELIST_ALIGN_RIGHT:
|
|
*px = prc->right - cx;
|
|
*py = prc->top + (RECTHEIGHT(*prc) - cy) / 2 + pbutn->rcIcon.top;
|
|
prc->right -= cx;
|
|
break;
|
|
|
|
case BUTTON_IMAGELIST_ALIGN_CENTER: // This means no text
|
|
*px = prc->left + (RECTWIDTH(*prc) - cx) / 2 + pbutn->rcIcon.left;
|
|
*py = prc->top + (RECTHEIGHT(*prc) - cy) / 2 + pbutn->rcIcon.top;
|
|
break;
|
|
|
|
case BUTTON_IMAGELIST_ALIGN_TOP:
|
|
*px = prc->left + (RECTWIDTH(*prc) - cx) / 2 + pbutn->rcIcon.left;
|
|
*py = pbutn->rcIcon.top;
|
|
prc->top += cy;
|
|
break;
|
|
|
|
case BUTTON_IMAGELIST_ALIGN_BOTTOM:
|
|
*px = (RECTWIDTH(*prc) - cx) / 2 + pbutn->rcIcon.left;
|
|
*py = prc->bottom - cy;
|
|
prc->bottom -= cy;
|
|
break;
|
|
|
|
case BUTTON_IMAGELIST_ALIGN_LEFT:
|
|
// Fall
|
|
default:
|
|
*px = prc->left + pbutn->rcIcon.left;
|
|
*py = prc->top + (RECTHEIGHT(*prc) - cy) / 2 + pbutn->rcIcon.top;
|
|
prc->left += cx;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_DrawThemed() - Renders button control according to the current
|
|
// theme.
|
|
// pbutn - the button control to render
|
|
// hdc - the hdc to draw on
|
|
// iPartId - the button part
|
|
// iStateId - the button state
|
|
//
|
|
HRESULT Button_DrawThemed(PBUTN pbutn, HDC hdc, int iPartId, int iStateId)
|
|
{
|
|
HRESULT hr;
|
|
RECT rcClient;
|
|
RECT rcContent;
|
|
RECT rcFocus;
|
|
RECT rcCheck;
|
|
DWORD dwTextFlags;
|
|
LPWSTR pszText;
|
|
INT cchText;
|
|
NMCUSTOMDRAW nmcd = {0};
|
|
|
|
BOOL fRadioOrCheck = (iPartId == BP_RADIOBUTTON || iPartId == BP_CHECKBOX );
|
|
|
|
//
|
|
// Render the button background
|
|
//
|
|
GetClientRect(pbutn->ci.hwnd, &rcClient);
|
|
rcCheck = rcContent = rcClient;
|
|
if ( fRadioOrCheck )
|
|
{
|
|
SIZE sizeChar;
|
|
SIZE sizeCheck;
|
|
int iCode;
|
|
|
|
//
|
|
// Compat....
|
|
//
|
|
|
|
GetTextExtentPoint32(hdc, TEXT("0"), 1, &sizeChar);
|
|
|
|
GetCheckBoxSize(hdc, pbutn, (iPartId == BP_CHECKBOX), &sizeCheck);
|
|
|
|
if (iPartId == BP_CHECKBOX)
|
|
iCode = CBR_CHECKBOX;
|
|
else
|
|
iCode = CBR_RADIOBUTTON;
|
|
|
|
Button_CalcRect(pbutn, hdc, &rcCheck, iCode, 0);
|
|
|
|
rcCheck.bottom = rcCheck.top + sizeCheck.cx;
|
|
|
|
if ((GET_STYLE(pbutn) & BS_RIGHTBUTTON) != 0)
|
|
{
|
|
rcCheck.left = rcContent.right - sizeCheck.cx;
|
|
rcContent.right = rcCheck.left - (sizeChar.cx/2);
|
|
}
|
|
else
|
|
{
|
|
rcCheck.right = rcContent.left + sizeCheck.cx;
|
|
rcContent.left = rcCheck.right + (sizeChar.cx/2);
|
|
}
|
|
|
|
//---- shrink radiobutton/checkbox button to fix client rect ----
|
|
if (RECTWIDTH(rcClient) < RECTWIDTH(rcCheck))
|
|
{
|
|
rcCheck.right = rcCheck.left + RECTWIDTH(rcClient);
|
|
}
|
|
|
|
if (RECTHEIGHT(rcClient) < RECTHEIGHT(rcCheck))
|
|
{
|
|
rcCheck.bottom = rcCheck.top + RECTHEIGHT(rcClient);
|
|
}
|
|
}
|
|
|
|
nmcd.hdc = hdc;
|
|
nmcd.rc = rcClient;
|
|
nmcd.dwItemSpec = GetWindowID(pbutn->ci.hwnd);
|
|
nmcd.uItemState = ButtonStateToCustomDrawState(pbutn);
|
|
|
|
|
|
pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREERASE, &nmcd);
|
|
|
|
if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
|
|
{
|
|
hr = DrawThemeBackground(pbutn->hTheme, hdc, iPartId, iStateId, &rcCheck, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceMsg(TF_STANDARD, "Failed to render theme background");
|
|
return hr;
|
|
}
|
|
|
|
if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
|
|
CICustomDrawNotify(&pbutn->ci, CDDS_POSTERASE, &nmcd);
|
|
|
|
pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREPAINT, &nmcd);
|
|
|
|
if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
|
|
{
|
|
//
|
|
// Render the button text
|
|
//
|
|
GetThemeBackgroundContentRect(pbutn->hTheme, hdc, iPartId, iStateId, &rcContent, &rcContent);
|
|
|
|
rcFocus = rcContent;
|
|
|
|
if (pbutn->himl)
|
|
{
|
|
int x, y;
|
|
int iImage = 0;
|
|
if (ImageList_GetImageCount(pbutn->himl) > 1)
|
|
{
|
|
iImage = (iStateId - PBS_NORMAL);
|
|
}
|
|
|
|
Button_GetImagePosition(pbutn, &rcContent, &x, &y);
|
|
|
|
ImageList_Draw(pbutn->himl, iImage, hdc, x, y, ILD_TRANSPARENT | (CCDPIScale(pbutn->ci)?ILD_DPISCALE:0));
|
|
}
|
|
|
|
//
|
|
// Get the button text
|
|
//
|
|
cchText = GetWindowTextLength(pbutn->ci.hwnd);
|
|
if (cchText <= 0)
|
|
{
|
|
//
|
|
// Nothing to draw
|
|
//
|
|
return hr;
|
|
}
|
|
|
|
pszText = UserLocalAlloc(0, (cchText+1)*SIZEOF(WCHAR));
|
|
if (pszText == NULL)
|
|
{
|
|
TraceMsg(TF_STANDARD, "Can't allocate buffer");
|
|
return E_FAIL;
|
|
}
|
|
|
|
GetWindowTextW(pbutn->ci.hwnd, pszText, cchText+1);
|
|
|
|
dwTextFlags = Button_GetTextFlags(pbutn);
|
|
|
|
if ( TESTFLAG(GET_STYLE(pbutn), BS_MULTILINE) || fRadioOrCheck )
|
|
{
|
|
int cxWidth, cyHeight;
|
|
TEXTMETRIC tm;
|
|
|
|
if ( TESTFLAG(GET_STYLE(pbutn), BS_MULTILINE) )
|
|
{
|
|
RECT rcTextExtent = rcContent;
|
|
|
|
cyHeight = DrawTextEx(hdc, pszText, cchText, &rcTextExtent, dwTextFlags|DT_CALCRECT, NULL);
|
|
cxWidth = RECTWIDTH(rcTextExtent);
|
|
}
|
|
else
|
|
{
|
|
SIZE size;
|
|
LPWSTR pszStrip = UserLocalAlloc(0, (cchText+1)*SIZEOF(WCHAR));
|
|
|
|
if (pszStrip)
|
|
{
|
|
INT cchStrip = StripAccelerators(pszText, pszStrip, TRUE);
|
|
GetTextExtentPoint32(hdc, pszStrip, cchStrip, &size);
|
|
UserLocalFree(pszStrip);
|
|
}
|
|
else
|
|
{
|
|
GetTextExtentPoint32(hdc, pszText, cchText, &size);
|
|
}
|
|
|
|
cyHeight = size.cy;
|
|
cxWidth = size.cx;
|
|
}
|
|
|
|
if (fRadioOrCheck && (cyHeight < RECTHEIGHT(rcCheck)))
|
|
{
|
|
// optimization for single line check/radios, align them with the top
|
|
// of the check no matter when the vertical alignment
|
|
rcContent.top = rcCheck.top;
|
|
}
|
|
else
|
|
{
|
|
if (dwTextFlags & DT_VCENTER)
|
|
{
|
|
rcContent.top += (RECTHEIGHT(rcContent) - cyHeight) / 2;
|
|
}
|
|
else if (dwTextFlags & DT_BOTTOM)
|
|
{
|
|
rcContent.top = rcContent.bottom - cyHeight;
|
|
}
|
|
}
|
|
|
|
if ( GetTextMetrics( hdc, &tm ) && (tm.tmInternalLeading == 0) )
|
|
{
|
|
// Far East fonts that have no leading. Leave space to prevent
|
|
// focus rect from obscuring text.
|
|
rcContent.top += g_cyBorder;
|
|
}
|
|
rcContent.bottom = rcContent.top + cyHeight;
|
|
|
|
if (dwTextFlags & DT_CENTER)
|
|
{
|
|
rcContent.left += (RECTWIDTH(rcContent) - cxWidth) / 2;
|
|
}
|
|
else if (dwTextFlags & DT_RIGHT)
|
|
{
|
|
rcContent.left = rcContent.right - cxWidth;
|
|
}
|
|
rcContent.right= rcContent.left + cxWidth;
|
|
|
|
|
|
if ( fRadioOrCheck )
|
|
{
|
|
//
|
|
// Inflate the bounding rect a litte, but contrained to
|
|
// within the client area.
|
|
//
|
|
rcFocus.top = max(rcClient.top, rcContent.top-1);
|
|
rcFocus.bottom = min(rcClient.bottom, rcContent.bottom+1);
|
|
|
|
rcFocus.left = max(rcClient.left, rcContent.left-1);
|
|
rcFocus.right = min(rcClient.right, rcContent.right+1);
|
|
}
|
|
}
|
|
|
|
hr = DrawThemeText(pbutn->hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, 0, &rcContent);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceMsg(TF_STANDARD, "Failed to render button text");
|
|
}
|
|
|
|
if (!TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN) && (BUTTONSTATE(pbutn) & BST_FOCUS))
|
|
{
|
|
DrawFocusRect(hdc, &rcFocus);
|
|
}
|
|
|
|
|
|
UserLocalFree(pszText);
|
|
if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
|
|
{
|
|
CICustomDrawNotify(&pbutn->ci, CDDS_POSTPAINT, &nmcd);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_GetTheme() - Get a handle to the theme for this button control
|
|
//
|
|
HTHEME Button_GetTheme(PBUTN pbutn)
|
|
{
|
|
//
|
|
// Button's with predefined IDs can be
|
|
// themed differently
|
|
//
|
|
static LPWSTR szButtonClasses[] =
|
|
{
|
|
L"Button", // =0
|
|
L"Button-OK;Button", // IDOK=1
|
|
L"Button-CANCEL;Button", // IDCANCEL=2
|
|
L"Button-ABORT;Button", // IDABORT=3
|
|
L"Button-RETRY;Button", // IDRETRY=4
|
|
L"Button-IGNORE;Button", // IDIGNORE=5
|
|
L"Button-YES;Button", // IDYES=6
|
|
L"Button-NO;Button", // IDNO=7
|
|
L"Button-CLOSE;Button", // IDCLOSE=8
|
|
L"Button-HELP;Button", // IDHELP=9
|
|
L"Button-TRYAGAIN;Button", // IDTRYAGAIN=10
|
|
L"Button-CONTINUE;Button", // IDCONTINUE=11
|
|
L"Button-APPLY;Button", // IDAPPLY=12 (not yet std)
|
|
};
|
|
int iButtonId = GetWindowID(pbutn->ci.hwnd);
|
|
|
|
if (iButtonId < 0 || iButtonId >= ARRAYSIZE(szButtonClasses)) // outside range
|
|
{
|
|
iButtonId = 0;
|
|
}
|
|
|
|
EnableThemeDialogTexture(GetParent(pbutn->ci.hwnd), ETDT_ENABLE);
|
|
|
|
return OpenThemeData(pbutn->ci.hwnd, szButtonClasses[iButtonId]);
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID GetCheckBoxSize(HDC hdc, PBUTN pbutn, BOOL fCheckBox, LPSIZE psize)
|
|
{
|
|
SIZE *psz;
|
|
|
|
if (fCheckBox)
|
|
psz = &sizeCheckBox;
|
|
else
|
|
psz = &sizeRadioBox;
|
|
|
|
if ((! psz->cx) && (! psz->cy)) // not yet calculated
|
|
{
|
|
BOOL fGotSize = FALSE;
|
|
|
|
if (pbutn->hTheme) // get themed size
|
|
{
|
|
int iPartId;
|
|
HRESULT hr;
|
|
|
|
if (fCheckBox)
|
|
iPartId = BP_CHECKBOX;
|
|
else
|
|
iPartId = BP_RADIOBUTTON;
|
|
|
|
hr = GetThemePartSize(pbutn->hTheme, hdc, iPartId, 1, NULL, TS_DRAW, psz);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceMsg(TF_STANDARD, "Failed to get theme part size for checkbox/radiobutton");
|
|
}
|
|
else
|
|
{
|
|
fGotSize = TRUE;
|
|
}
|
|
}
|
|
|
|
if (! fGotSize) // get classic size (use checkbox for both)
|
|
{
|
|
HBITMAP hbmp = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECKBOXES));
|
|
|
|
if (hbmp != NULL)
|
|
{
|
|
BITMAP bmp;
|
|
|
|
GetObject(hbmp, sizeof(BITMAP), &bmp);
|
|
|
|
//
|
|
// Checkbox bitmap is arranged 4 over and three down. Only need to get
|
|
// the size of a single checkbox, so do the math here.
|
|
//
|
|
psz->cx = bmp.bmWidth / 4;
|
|
psz->cy = bmp.bmHeight / 3;
|
|
|
|
DeleteObject(hbmp);
|
|
}
|
|
else
|
|
{
|
|
AssertMsg(hbmp != NULL, TEXT("Unable to load checkbox bitmap"));
|
|
}
|
|
}
|
|
}
|
|
|
|
*psize = *psz;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
__inline BYTE GetButtonStyle(ULONG ulWinStyle)
|
|
{
|
|
return (BYTE) LOBYTE(ulWinStyle & BS_TYPEMASK);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
__inline ULONG GetButtonType(ULONG ulWinStyle)
|
|
{
|
|
return ulWinStyle & BS_TYPEMASK;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// IsPushButton()
|
|
//
|
|
// Returns non-zero if the window is a push button. Returns flags that
|
|
// are interesting if it is. These flags are
|
|
//
|
|
UINT IsPushButton(PBUTN pbutn)
|
|
{
|
|
BYTE bStyle;
|
|
UINT flags;
|
|
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
|
|
bStyle = GetButtonStyle(ulStyle);
|
|
flags = 0;
|
|
|
|
switch (bStyle)
|
|
{
|
|
case LOBYTE(BS_PUSHBUTTON):
|
|
flags |= PBF_PUSHABLE;
|
|
break;
|
|
|
|
case LOBYTE(BS_DEFPUSHBUTTON):
|
|
flags |= PBF_PUSHABLE | PBF_DEFAULT;
|
|
break;
|
|
|
|
default:
|
|
if (ulStyle & BS_PUSHLIKE)
|
|
{
|
|
flags |= PBF_PUSHABLE;
|
|
}
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// GetAlignment()
|
|
//
|
|
// Gets default alignment of button. If BS_HORZMASK and/or BS_VERTMASK
|
|
// is specified, uses those. Otherwise, uses default for button.
|
|
//
|
|
// It's probably a fine time to describe what alignment flags mean for
|
|
// each type of button. Note that the presence of a bitmap/icon affects
|
|
// the meaning of alignments.
|
|
//
|
|
// (1) Push like buttons
|
|
// With one of {bitmap, icon, text}:
|
|
// Just like you'd expect
|
|
// With one of {bitmap, icon} AND text:
|
|
// Image & text are centered as a unit; alignment means where
|
|
// the image shows up. E.G., left-aligned means the image
|
|
// on the left, text on the right.
|
|
// (2) Radio/check like buttons
|
|
// Left aligned means check/radio box is on left, then bitmap/icon
|
|
// and text follows, left justified.
|
|
// Right aligned means checkk/radio box is on right, preceded by
|
|
// text and bitmap/icon, right justified.
|
|
// Centered has no meaning.
|
|
// With one of {bitmap, icon} AND text:
|
|
// Top aligned means bitmap/icon above, text below
|
|
// Bottom aligned means text above, bitmap/icon below
|
|
// With one of {bitmap, icon, text}
|
|
// Alignments mean what you'd expect.
|
|
// (3) Group boxes
|
|
// Left aligned means text is left justified on left side
|
|
// Right aligned means text is right justified on right side
|
|
// Center aligned means text is in middle
|
|
//
|
|
WORD GetAlignment(PBUTN pbutn)
|
|
{
|
|
BYTE bHorz;
|
|
BYTE bVert;
|
|
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
|
|
bHorz = HIBYTE(ulStyle & BS_HORZMASK);
|
|
bVert = HIBYTE(ulStyle & BS_VERTMASK);
|
|
|
|
if (!bHorz || !bVert)
|
|
{
|
|
if (IsPushButton(pbutn))
|
|
{
|
|
if (!bHorz)
|
|
{
|
|
bHorz = HIBYTE(BS_CENTER);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bHorz)
|
|
{
|
|
bHorz = HIBYTE(BS_LEFT);
|
|
}
|
|
}
|
|
|
|
if (GetButtonStyle(ulStyle) == BS_GROUPBOX)
|
|
{
|
|
if (!bVert)
|
|
{
|
|
bVert = HIBYTE(BS_TOP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!bVert)
|
|
{
|
|
bVert = HIBYTE(BS_VCENTER);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bHorz | bVert;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_SetFont()
|
|
//
|
|
// Changes button font, and decides if we can use real bold font for default
|
|
// push buttons or if we have to simulate it.
|
|
//
|
|
VOID Button_SetFont(PBUTN pbutn, HFONT hFont, BOOL fRedraw)
|
|
{
|
|
pbutn->hFont = hFont;
|
|
|
|
if (fRedraw && IsWindowVisible(pbutn->ci.hwnd))
|
|
{
|
|
InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
HBRUSH Button_InitDC(PBUTN pbutn, HDC hdc)
|
|
{
|
|
UINT uMsg;
|
|
BYTE bStyle;
|
|
HBRUSH hBrush;
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
ULONG ulStyleEx = GET_EXSTYLE(pbutn);
|
|
|
|
//
|
|
// Set BkMode before getting brush so that the app can change it to
|
|
// transparent if it wants.
|
|
//
|
|
SetBkMode(hdc, OPAQUE);
|
|
|
|
bStyle = GetButtonStyle(ulStyle);
|
|
|
|
switch (bStyle)
|
|
{
|
|
default:
|
|
if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT) && ((ulStyle & BS_PUSHLIKE) == 0))
|
|
{
|
|
uMsg = WM_CTLCOLORSTATIC;
|
|
break;
|
|
}
|
|
|
|
case LOBYTE(BS_PUSHBUTTON):
|
|
case LOBYTE(BS_DEFPUSHBUTTON):
|
|
case LOBYTE(BS_OWNERDRAW):
|
|
case LOBYTE(BS_USERBUTTON):
|
|
uMsg = WM_CTLCOLORBTN;
|
|
break;
|
|
}
|
|
|
|
hBrush = (HBRUSH)SendMessage(GetParent(pbutn->ci.hwnd), uMsg, (WPARAM)hdc, (LPARAM)pbutn->ci.hwnd);
|
|
|
|
//
|
|
// Select in the user's font if set, and save the old font so that we can
|
|
// restore it when we release the dc.
|
|
//
|
|
if (pbutn->hFont)
|
|
{
|
|
SelectObject(hdc, pbutn->hFont);
|
|
}
|
|
|
|
//
|
|
// Clip output to the window rect if needed.
|
|
//
|
|
if (bStyle != LOBYTE(BS_GROUPBOX))
|
|
{
|
|
RECT rcClient;
|
|
|
|
GetClientRect(pbutn->ci.hwnd, &rcClient);
|
|
IntersectClipRect(hdc, 0, 0,
|
|
rcClient.right,
|
|
rcClient.bottom);
|
|
}
|
|
|
|
if ((ulStyleEx & WS_EX_RTLREADING) != 0)
|
|
{
|
|
SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
|
|
}
|
|
|
|
return hBrush;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
HDC Button_GetDC(PBUTN pbutn, HBRUSH *phBrush)
|
|
{
|
|
HDC hdc = NULL;
|
|
|
|
if (IsWindowVisible(pbutn->ci.hwnd))
|
|
{
|
|
HBRUSH hBrush;
|
|
|
|
hdc = GetDC(pbutn->ci.hwnd);
|
|
hBrush = Button_InitDC(pbutn, hdc);
|
|
|
|
if ((phBrush != NULL) && hBrush)
|
|
{
|
|
*phBrush = hBrush;
|
|
}
|
|
}
|
|
|
|
return hdc;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_ReleaseDC(PBUTN pbutn, HDC hdc, HBRUSH *phBrush)
|
|
{
|
|
ULONG ulStyleEx = GET_EXSTYLE(pbutn);
|
|
|
|
if ((ulStyleEx & WS_EX_RTLREADING) != 0)
|
|
{
|
|
SetTextAlign(hdc, GetTextAlign(hdc) & ~TA_RTLREADING);
|
|
}
|
|
|
|
if (pbutn->hFont)
|
|
{
|
|
SelectObject(hdc, GetStockObject(SYSTEM_FONT));
|
|
}
|
|
|
|
ReleaseDC(pbutn->ci.hwnd, hdc);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_OwnerDraw(PBUTN pbutn, HDC hdc, UINT itemAction)
|
|
{
|
|
DRAWITEMSTRUCT drawItemStruct;
|
|
UINT itemState = 0;
|
|
int iButtonId = GetWindowID(pbutn->ci.hwnd);
|
|
|
|
if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN))
|
|
{
|
|
itemState |= ODS_NOFOCUSRECT;
|
|
}
|
|
|
|
if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
|
|
{
|
|
itemState |= ODS_NOACCEL;
|
|
}
|
|
|
|
if (TESTFLAG(BUTTONSTATE(pbutn), BST_FOCUS))
|
|
{
|
|
itemState |= ODS_FOCUS;
|
|
}
|
|
|
|
if (TESTFLAG(BUTTONSTATE(pbutn), BST_PUSHED))
|
|
{
|
|
itemState |= ODS_SELECTED;
|
|
}
|
|
|
|
if (!IsWindowEnabled(pbutn->ci.hwnd))
|
|
{
|
|
itemState |= ODS_DISABLED;
|
|
}
|
|
|
|
//
|
|
// Populate the draw item struct
|
|
//
|
|
drawItemStruct.CtlType = ODT_BUTTON;
|
|
drawItemStruct.CtlID = iButtonId;
|
|
drawItemStruct.itemAction = itemAction;
|
|
drawItemStruct.itemState = itemState;
|
|
drawItemStruct.hwndItem = pbutn->ci.hwnd;
|
|
drawItemStruct.hDC = hdc;
|
|
GetClientRect(pbutn->ci.hwnd, &drawItemStruct.rcItem);
|
|
drawItemStruct.itemData = 0L;
|
|
|
|
//
|
|
// Send a WM_DRAWITEM message to our parent
|
|
//
|
|
SendMessage(GetParent(pbutn->ci.hwnd),
|
|
WM_DRAWITEM,
|
|
(WPARAM)iButtonId,
|
|
(LPARAM)&drawItemStruct);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_CalcRect(PBUTN pbutn, HDC hdc, LPRECT lprc, int iCode, UINT uFlags)
|
|
{
|
|
CONST TCHAR szOneChar[] = TEXT("0");
|
|
|
|
SIZE sizeExtent;
|
|
int dy;
|
|
WORD wAlign;
|
|
int cxEdge, cyEdge;
|
|
int cxBorder, cyBorder;
|
|
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
ULONG ulStyleEx = GET_EXSTYLE(pbutn);
|
|
|
|
cxEdge = GetSystemMetrics(SM_CXEDGE);
|
|
cyEdge = GetSystemMetrics(SM_CYEDGE);
|
|
cxBorder = GetSystemMetrics(SM_CXBORDER);
|
|
cyBorder = GetSystemMetrics(SM_CYBORDER);
|
|
|
|
GetClientRect(pbutn->ci.hwnd, lprc);
|
|
|
|
wAlign = GetAlignment(pbutn);
|
|
|
|
switch (iCode)
|
|
{
|
|
case CBR_PUSHBUTTON:
|
|
//
|
|
// Subtract out raised edge all around
|
|
//
|
|
InflateRect(lprc, -cxEdge, -cyEdge);
|
|
|
|
if (uFlags & PBF_DEFAULT)
|
|
{
|
|
InflateRect(lprc, -cxBorder, -cyBorder);
|
|
}
|
|
break;
|
|
|
|
case CBR_CHECKBOX:
|
|
case CBR_RADIOBUTTON:
|
|
{
|
|
SIZE sizeChk = {0};
|
|
|
|
GetCheckBoxSize(hdc, pbutn, (iCode == CBR_CHECKBOX), &sizeChk);
|
|
|
|
switch (wAlign & HIBYTE(BS_VERTMASK))
|
|
{
|
|
case HIBYTE(BS_VCENTER):
|
|
lprc->top = (lprc->top + lprc->bottom - sizeChk.cy) / 2;
|
|
break;
|
|
|
|
case HIBYTE(BS_TOP):
|
|
case HIBYTE(BS_BOTTOM):
|
|
GetTextExtentPoint32(hdc, (LPTSTR)szOneChar, 1, &sizeExtent);
|
|
dy = sizeExtent.cy + sizeExtent.cy/4;
|
|
|
|
//
|
|
// Save vertical extent
|
|
//
|
|
sizeExtent.cx = dy;
|
|
|
|
//
|
|
// Get centered amount
|
|
//
|
|
dy = (dy - sizeChk.cy) / 2;
|
|
if ((wAlign & HIBYTE(BS_VERTMASK)) == HIBYTE(BS_TOP))
|
|
{
|
|
lprc->top += dy;
|
|
}
|
|
else
|
|
{
|
|
lprc->top = lprc->bottom - sizeExtent.cx + dy;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ((ulStyle & BS_RIGHTBUTTON) != 0)
|
|
{
|
|
lprc->left = lprc->right - sizeChk.cx;
|
|
}
|
|
else
|
|
{
|
|
lprc->right = lprc->left + sizeChk.cx;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CBR_CHECKTEXT:
|
|
{
|
|
SIZE sizeChk = {0};
|
|
|
|
GetCheckBoxSize(hdc, pbutn, TRUE, &sizeChk);
|
|
|
|
if ((ulStyle & BS_RIGHTBUTTON) != 0)
|
|
{
|
|
lprc->right -= sizeChk.cx;
|
|
|
|
//
|
|
// More spacing for 4.0 dudes
|
|
//
|
|
if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT))
|
|
{
|
|
GetTextExtentPoint32(hdc, szOneChar, 1, &sizeExtent);
|
|
lprc->right -= sizeExtent.cx / 2;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
lprc->left += sizeChk.cx;
|
|
|
|
//
|
|
// More spacing for 4.0 dudes
|
|
//
|
|
if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT))
|
|
{
|
|
GetTextExtentPoint32(hdc, szOneChar, 1, &sizeExtent);
|
|
lprc->left += sizeExtent.cx / 2;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CBR_GROUPTEXT:
|
|
{
|
|
LPWSTR pszText = NULL;
|
|
INT cchText = GetWindowTextLength(pbutn->ci.hwnd);
|
|
BOOL fSucceeded = FALSE;
|
|
|
|
if (cchText > 0)
|
|
{
|
|
pszText = UserLocalAlloc(0, (cchText+1)*SIZEOF(WCHAR));
|
|
|
|
if (pszText)
|
|
{
|
|
if (GetWindowText(pbutn->ci.hwnd, pszText, cchText+1) > 0)
|
|
{
|
|
//
|
|
// if not themed
|
|
//
|
|
if (!Button_IsThemed(pbutn))
|
|
{
|
|
GetTextExtentPoint32(hdc, pszText, cchText, &sizeExtent);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwTextFlags = Button_GetTextFlags(pbutn);
|
|
RECT rcExtent;
|
|
GetThemeTextExtent(pbutn->hTheme,
|
|
hdc,
|
|
BP_GROUPBOX,
|
|
0,
|
|
pszText,
|
|
cchText,
|
|
dwTextFlags,
|
|
lprc,
|
|
&rcExtent);
|
|
|
|
sizeExtent.cx = RECTWIDTH(rcExtent);
|
|
sizeExtent.cy = RECTHEIGHT(rcExtent);
|
|
|
|
}
|
|
|
|
sizeExtent.cx += GetSystemMetrics(SM_CXEDGE) * 2;
|
|
|
|
switch (wAlign & HIBYTE(BS_HORZMASK))
|
|
{
|
|
//
|
|
// BFLEFT, nothing
|
|
//
|
|
case HIBYTE(BS_LEFT):
|
|
lprc->left += (SYSFONT_CXCHAR - GetSystemMetrics(SM_CXBORDER));
|
|
lprc->right = lprc->left + (int)(sizeExtent.cx);
|
|
break;
|
|
|
|
case HIBYTE(BS_RIGHT):
|
|
lprc->right -= (SYSFONT_CXCHAR - GetSystemMetrics(SM_CXBORDER));
|
|
lprc->left = lprc->right - (int)(sizeExtent.cx);
|
|
break;
|
|
|
|
case HIBYTE(BS_CENTER):
|
|
lprc->left = (lprc->left + lprc->right - (int)(sizeExtent.cx)) / 2;
|
|
lprc->right = lprc->left + (int)(sizeExtent.cx);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Center aligned.
|
|
//
|
|
lprc->bottom = lprc->top + sizeExtent.cy + GetSystemMetrics(SM_CYEDGE);
|
|
fSucceeded = TRUE;
|
|
}
|
|
|
|
UserLocalFree(pszText);
|
|
}
|
|
}
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
SetRectEmpty(lprc);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case CBR_GROUPFRAME:
|
|
GetTextExtentPoint32(hdc, (LPTSTR)szOneChar, 1, &sizeExtent);
|
|
lprc->top += sizeExtent.cy / 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_MultiExtent()
|
|
//
|
|
// Calculates button text extent, given alignment flags.
|
|
//
|
|
VOID Button_MultiExtent(WORD wFlags, HDC hdc, LPRECT lprcMax, LPTSTR pszBuffer, INT cchBuffer, PINT pcx, PINT pcy)
|
|
{
|
|
RECT rc;
|
|
UINT dtFlags = DT_CALCRECT | DT_WORDBREAK | DT_EDITCONTROL;
|
|
|
|
CopyRect(&rc, lprcMax);
|
|
|
|
//
|
|
// Note that since we're just calculating the maximum dimensions,
|
|
// left-justification and top-justification are not important.
|
|
// Also, remember to leave margins horz and vert that follow our rules
|
|
// in DrawBtnText().
|
|
//
|
|
|
|
InflateRect(&rc, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYBORDER));
|
|
|
|
if ((wFlags & LOWORD(BS_HORZMASK)) == LOWORD(BS_CENTER))
|
|
{
|
|
dtFlags |= DT_CENTER;
|
|
}
|
|
|
|
if ((wFlags & LOWORD(BS_VERTMASK)) == LOWORD(BS_VCENTER))
|
|
{
|
|
dtFlags |= DT_VCENTER;
|
|
}
|
|
|
|
DrawTextEx(hdc, pszBuffer, cchBuffer, &rc, dtFlags, NULL);
|
|
|
|
if (pcx)
|
|
{
|
|
*pcx = rc.right-rc.left;
|
|
}
|
|
|
|
if (pcy)
|
|
{
|
|
*pcy = rc.bottom-rc.top;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_MultiDraw()
|
|
//
|
|
// Draws multiline button text
|
|
//
|
|
BOOL Button_MultiDraw(HDC hdc, LPARAM lParam, WPARAM wParam, INT cx, INT cy)
|
|
{
|
|
BTNDATA *pBtnData = (BTNDATA *)lParam;
|
|
|
|
if (pBtnData)
|
|
{
|
|
RECT rc;
|
|
UINT dtFlags = DT_WORDBREAK | DT_EDITCONTROL;
|
|
PBUTN pbutn = pBtnData->pbutn;
|
|
|
|
SetRect(&rc, 0, 0, cx, cy);
|
|
|
|
if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
|
|
{
|
|
dtFlags |= DT_HIDEPREFIX;
|
|
}
|
|
else if (pbutn->fPaintKbdCuesOnly)
|
|
{
|
|
dtFlags |= DT_PREFIXONLY;
|
|
}
|
|
|
|
//
|
|
// Horizontal alignment
|
|
//
|
|
switch (pBtnData->wFlags & LOWORD(BS_HORZMASK))
|
|
{
|
|
case LOWORD(BS_CENTER):
|
|
dtFlags |= DT_CENTER;
|
|
break;
|
|
|
|
case LOWORD(BS_RIGHT):
|
|
dtFlags |= DT_RIGHT;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Vertical alignment
|
|
//
|
|
switch (pBtnData->wFlags & LOWORD(BS_VERTMASK))
|
|
{
|
|
case LOWORD(BS_VCENTER):
|
|
dtFlags |= DT_VCENTER;
|
|
break;
|
|
|
|
case LOWORD(BS_BOTTOM):
|
|
dtFlags |= DT_BOTTOM;
|
|
break;
|
|
}
|
|
|
|
DrawTextEx(hdc, pBtnData->pszText, pBtnData->cchText, &rc, dtFlags, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
BOOL Button_SetCapture(PBUTN pbutn, UINT uCodeMouse)
|
|
{
|
|
BUTTONSTATE(pbutn) |= uCodeMouse;
|
|
|
|
if (!(BUTTONSTATE(pbutn) & BST_CAPTURED))
|
|
{
|
|
SetCapture(pbutn->ci.hwnd);
|
|
BUTTONSTATE(pbutn) |= BST_CAPTURED;
|
|
|
|
//
|
|
// To prevent redundant CLICK messages, we set the INCLICK bit so
|
|
// the WM_SETFOCUS code will not do a Button_NotifyParent(BN_CLICKED).
|
|
//
|
|
BUTTONSTATE(pbutn) |= BST_INCLICK;
|
|
|
|
SetFocus(pbutn->ci.hwnd);
|
|
|
|
BUTTONSTATE(pbutn) &= ~BST_INCLICK;
|
|
}
|
|
|
|
return BUTTONSTATE(pbutn) & BST_CAPTURED;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_NotifyParent(PBUTN pbutn, UINT uCode)
|
|
{
|
|
HWND hwndParent = GetParent(pbutn->ci.hwnd);
|
|
int iButtonId = GetWindowID(pbutn->ci.hwnd);
|
|
|
|
if ( !hwndParent )
|
|
{
|
|
hwndParent = pbutn->ci.hwnd;
|
|
}
|
|
|
|
SendMessage(hwndParent,
|
|
WM_COMMAND,
|
|
MAKELONG(iButtonId, uCode),
|
|
(LPARAM)pbutn->ci.hwnd);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_ReleaseCapture(PBUTN pbutn, BOOL fCheck)
|
|
{
|
|
UINT uCheck;
|
|
BOOL fNotifyParent = FALSE;
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_PUSHED)
|
|
{
|
|
|
|
SendMessage(pbutn->ci.hwnd, BM_SETSTATE, FALSE, 0);
|
|
|
|
if (fCheck)
|
|
{
|
|
switch (GetButtonType(ulStyle))
|
|
{
|
|
case BS_AUTOCHECKBOX:
|
|
case BS_AUTO3STATE:
|
|
|
|
uCheck = (UINT)((BUTTONSTATE(pbutn) & BST_CHECKMASK) + 1);
|
|
|
|
if (uCheck > (UINT)(GetButtonType(ulStyle) == BS_AUTO3STATE ? BST_INDETERMINATE : BST_CHECKED))
|
|
{
|
|
uCheck = BST_UNCHECKED;
|
|
}
|
|
|
|
SendMessage(pbutn->ci.hwnd, BM_SETCHECK, uCheck, 0);
|
|
|
|
break;
|
|
|
|
case BS_AUTORADIOBUTTON:
|
|
{
|
|
//
|
|
// Walk the radio buttons in the same group as us. Check ourself
|
|
// and uncheck everyone else.
|
|
//
|
|
HWND hwndNext = pbutn->ci.hwnd;
|
|
HWND hwndParent = GetParent(pbutn->ci.hwnd);
|
|
|
|
do
|
|
{
|
|
if ((UINT)SendMessage(hwndNext, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
|
|
{
|
|
SendMessage(hwndNext, BM_SETCHECK, hwndNext == pbutn->ci.hwnd, 0L);
|
|
}
|
|
|
|
hwndNext = GetNextDlgGroupItem(hwndParent, hwndNext, FALSE);
|
|
}
|
|
//
|
|
// Loop until we see ourself again
|
|
//
|
|
while (hwndNext != pbutn->ci.hwnd);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
fNotifyParent = TRUE;
|
|
}
|
|
}
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_CAPTURED)
|
|
{
|
|
BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE);
|
|
ReleaseCapture();
|
|
}
|
|
|
|
if (fNotifyParent)
|
|
{
|
|
//
|
|
// We have to do the notification after setting the buttonstate bits.
|
|
//
|
|
Button_NotifyParent(pbutn, BN_CLICKED);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_DrawText()
|
|
//
|
|
// Draws text of button.
|
|
//
|
|
VOID Button_DrawText(PBUTN pbutn, HDC hdc, DWORD dwFlags, BOOL fDepress)
|
|
{
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
BYTE bStyle = GetButtonStyle(ulStyle);
|
|
|
|
if ((bStyle != LOBYTE(BS_GROUPBOX)) || TESTFLAG(dwFlags, DBT_TEXT))
|
|
{
|
|
LPTSTR pszText = NULL;
|
|
INT cchText = GetWindowTextLength(pbutn->ci.hwnd);
|
|
|
|
if (cchText >= 0)
|
|
{
|
|
pszText = UserLocalAlloc(0, (cchText+1)*SIZEOF(WCHAR));
|
|
|
|
if (pszText)
|
|
{
|
|
UINT pbfPush = IsPushButton(pbutn);
|
|
RECT rc;
|
|
int x = 0, y = 0;
|
|
int cx = 0, cy = 0;
|
|
|
|
GetWindowText(pbutn->ci.hwnd, pszText, cchText+1);
|
|
|
|
if (!pbfPush && (bStyle == LOBYTE(BS_OWNERDRAW)))
|
|
{
|
|
//
|
|
// Skip stuff for ownerdraw buttons, since we aren't going to
|
|
// draw text/image.
|
|
//
|
|
Button_CalcRect(pbutn, hdc, &rc, mpStyleCbr[bStyle], pbfPush);
|
|
}
|
|
else if (!Button_IsThemed(pbutn))
|
|
{
|
|
HBRUSH hbr;
|
|
UINT dsFlags;
|
|
BTNDATA btnData;
|
|
|
|
|
|
LPARAM lData = 0;
|
|
WPARAM wData;
|
|
WORD wFlags = GetAlignment(pbutn);
|
|
|
|
if (pbfPush)
|
|
{
|
|
Button_CalcRect(pbutn, hdc, &rc, CBR_PUSHBUTTON, pbfPush);
|
|
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
//
|
|
// This is because we didn't have WM_CTLCOLOR,
|
|
// CTLCOLOR_BTN actually set up the button colors. For
|
|
// old apps, CTLCOLOR_BTN needs to work like CTLCOLOR_STATIC.
|
|
//
|
|
SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
|
|
SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
|
|
hbr = GetSysColorBrush(COLOR_BTNTEXT);
|
|
|
|
}
|
|
else
|
|
{
|
|
Button_CalcRect(pbutn, hdc, &rc, mpStyleCbr[bStyle], pbfPush);
|
|
hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
|
|
}
|
|
|
|
if (pbutn->himl)
|
|
{
|
|
int x, y;
|
|
Button_GetImagePosition(pbutn, &rc, &x, &y);
|
|
|
|
if (fDepress)
|
|
{
|
|
x += GetSystemMetrics(SM_CXBORDER);
|
|
y += GetSystemMetrics(SM_CYBORDER);
|
|
}
|
|
|
|
ImageList_Draw(pbutn->himl,
|
|
0,
|
|
hdc,
|
|
x,
|
|
y,
|
|
ILD_TRANSPARENT | (CCDPIScale(pbutn->ci)?ILD_DPISCALE:0));
|
|
}
|
|
|
|
|
|
// Initialize data for DrawState
|
|
if ((ulStyle & BS_BITMAP) != 0)
|
|
{
|
|
// bitmap
|
|
// lData is an hbitmap
|
|
// wData is 0
|
|
|
|
BITMAP bmp;
|
|
|
|
GetObject(pbutn->hImage, sizeof(BITMAP), &bmp);
|
|
cx = bmp.bmWidth;
|
|
cy = bmp.bmHeight;
|
|
|
|
dsFlags = DST_BITMAP;
|
|
lData = (LPARAM)pbutn->hImage;
|
|
wData = 0;
|
|
}
|
|
else if ((ulStyle & BS_ICON) != 0)
|
|
{
|
|
// icon
|
|
// lData is an hicon
|
|
// wData is 0
|
|
|
|
SIZE sizeIcon;
|
|
|
|
GetIconSize(pbutn->hImage, &sizeIcon);
|
|
cx = sizeIcon.cx;
|
|
cy = sizeIcon.cy;
|
|
|
|
dsFlags = DST_ICON;
|
|
lData = (LPARAM)pbutn->hImage;
|
|
wData = 0;
|
|
}
|
|
else
|
|
{
|
|
if ((ulStyle & BS_MULTILINE) != 0)
|
|
{
|
|
// multiline
|
|
// package the button data in bdt. DrawState wull call Button_MultiDraw
|
|
// lData is PBTNDATA
|
|
// wData is 0
|
|
|
|
Button_MultiExtent(wFlags, hdc, &rc, pszText, cchText, &cx, &cy);
|
|
|
|
btnData.pszText = pszText;
|
|
btnData.cchText = cchText;
|
|
btnData.pbutn = pbutn;
|
|
btnData.wFlags = wFlags;
|
|
|
|
dsFlags = DST_COMPLEX;
|
|
lData = (LPARAM)&btnData;
|
|
wData = 0;
|
|
}
|
|
else
|
|
{
|
|
// simple text button
|
|
// lData is pszText
|
|
// wData is cchText
|
|
|
|
// Try to get the text extent with mnemonics stripped.
|
|
SIZE size;
|
|
LPWSTR pszStrip = UserLocalAlloc(0, (cchText+1)*SIZEOF(WCHAR));
|
|
|
|
if (pszStrip != NULL)
|
|
{
|
|
INT cchStrip = StripAccelerators(pszText, pszStrip, TRUE);
|
|
GetTextExtentPoint32(hdc, pszStrip, cchStrip, &size);
|
|
UserLocalFree(pszStrip);
|
|
}
|
|
else
|
|
{
|
|
GetTextExtentPoint32(hdc, pszText, cchText, &size);
|
|
}
|
|
|
|
cx = size.cx;
|
|
cy = size.cy;
|
|
|
|
//
|
|
// If the control doesn't need underlines, set DST_HIDEPREFIX and
|
|
// also do not show the focus indicator
|
|
//
|
|
dsFlags = DST_PREFIXTEXT;
|
|
if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
|
|
{
|
|
dsFlags |= DSS_HIDEPREFIX;
|
|
}
|
|
else if (pbutn->fPaintKbdCuesOnly)
|
|
{
|
|
dsFlags |= DSS_PREFIXONLY;
|
|
}
|
|
|
|
lData = (LPARAM)pszText;
|
|
wData = cchText;
|
|
}
|
|
|
|
|
|
//
|
|
// Add on a pixel or two of vertical space to make centering
|
|
// happier. That way underline won't abut focus rect unless
|
|
// spacing is really tight.
|
|
//
|
|
cy++;
|
|
}
|
|
|
|
//
|
|
// ALIGNMENT
|
|
//
|
|
|
|
//
|
|
// Horizontal
|
|
//
|
|
switch (wFlags & HIBYTE(BS_HORZMASK))
|
|
{
|
|
//
|
|
// For left & right justified, we leave a margin of CXEDGE on either
|
|
// side for eye-pleasing space.
|
|
//
|
|
case HIBYTE(BS_LEFT):
|
|
x = rc.left + GetSystemMetrics(SM_CXEDGE);
|
|
break;
|
|
|
|
case HIBYTE(BS_RIGHT):
|
|
x = rc.right - cx - GetSystemMetrics(SM_CXEDGE);
|
|
break;
|
|
|
|
default:
|
|
x = (rc.left + rc.right - cx) / 2;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Vertical
|
|
//
|
|
switch (wFlags & HIBYTE(BS_VERTMASK))
|
|
{
|
|
//
|
|
// For top & bottom justified, we leave a margin of CYBORDER on
|
|
// either side for more eye-pleasing space.
|
|
//
|
|
case HIBYTE(BS_TOP):
|
|
y = rc.top + GetSystemMetrics(SM_CYBORDER);
|
|
break;
|
|
|
|
case HIBYTE(BS_BOTTOM):
|
|
y = rc.bottom - cy - GetSystemMetrics(SM_CYBORDER);
|
|
break;
|
|
|
|
default:
|
|
y = (rc.top + rc.bottom - cy) / 2;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Draw the text
|
|
//
|
|
if (lData && TESTFLAG(dwFlags, DBT_TEXT))
|
|
{
|
|
//
|
|
// This isn't called for USER buttons.
|
|
//
|
|
UserAssert(bStyle != LOBYTE(BS_USERBUTTON));
|
|
|
|
if (fDepress)
|
|
{
|
|
x += GetSystemMetrics(SM_CXBORDER);
|
|
y += GetSystemMetrics(SM_CYBORDER);
|
|
}
|
|
|
|
if (!IsWindowEnabled(pbutn->ci.hwnd))
|
|
{
|
|
UserAssert(HIBYTE(BS_ICON) == HIBYTE(BS_BITMAP));
|
|
if (GetSystemMetrics(SM_SLOWMACHINE) &&
|
|
((ulStyle & (BS_ICON | BS_BITMAP)) != 0) &&
|
|
(GetBkColor(hdc) != GetSysColor(COLOR_GRAYTEXT)))
|
|
{
|
|
//
|
|
// Perf && consistency with menus, statics
|
|
//
|
|
SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
|
|
}
|
|
else
|
|
{
|
|
dsFlags |= DSS_DISABLED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use transparent mode for checked push buttons since we're going to
|
|
// fill background with dither.
|
|
//
|
|
if (pbfPush)
|
|
{
|
|
switch (BUTTONSTATE(pbutn) & BST_CHECKMASK)
|
|
{
|
|
case BST_INDETERMINATE:
|
|
hbr = GetSysColorBrush(COLOR_GRAYTEXT);
|
|
dsFlags |= DSS_MONO;
|
|
//
|
|
// FALL THRU
|
|
//
|
|
|
|
case BST_CHECKED:
|
|
//
|
|
// Drawing on dithered background...
|
|
//
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use brush and colors currently selected into hdc when we grabbed
|
|
// color
|
|
//
|
|
DrawState(hdc,
|
|
hbr,
|
|
(DRAWSTATEPROC)Button_MultiDraw,
|
|
lData,
|
|
wData,
|
|
x,
|
|
y,
|
|
cx,
|
|
cy,
|
|
dsFlags);
|
|
}
|
|
|
|
}
|
|
|
|
// Draw focus rect.
|
|
//
|
|
// This can get called for OWNERDRAW and USERDRAW buttons. However, only
|
|
// OWNERDRAW buttons let the owner change the drawing of the focus button.
|
|
if (TESTFLAG(dwFlags, DBT_FOCUS))
|
|
{
|
|
if (bStyle == LOBYTE(BS_OWNERDRAW))
|
|
{
|
|
//
|
|
// For ownerdraw buttons, this is only called in response to a
|
|
// WM_SETFOCUS or WM_KILL FOCUS message. So, we can check the
|
|
// new state of the focus by looking at the BUTTONSTATE bits
|
|
// which are set before this procedure is called.
|
|
//
|
|
Button_OwnerDraw(pbutn, hdc, ODA_FOCUS);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't draw the focus if underlines are not turned on
|
|
//
|
|
if (!TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN))
|
|
{
|
|
//
|
|
// Let focus rect always hug edge of push buttons. We already
|
|
// have the client area setup for push buttons, so we don't have
|
|
// to do anything.
|
|
//
|
|
if (!pbfPush)
|
|
{
|
|
RECT rcClient;
|
|
|
|
GetClientRect(pbutn->ci.hwnd, &rcClient);
|
|
|
|
if (bStyle == LOBYTE(BS_USERBUTTON))
|
|
{
|
|
CopyRect(&rc, &rcClient);
|
|
}
|
|
else if (Button_IsThemed(pbutn))
|
|
{
|
|
//
|
|
// if themed
|
|
//
|
|
int iPartId = 0;
|
|
int iStateId = 0;
|
|
|
|
Button_GetThemeIds(pbutn, &iPartId, &iStateId);
|
|
GetThemeBackgroundContentRect(pbutn->hTheme,
|
|
hdc,
|
|
iPartId,
|
|
iStateId,
|
|
&rcClient,
|
|
&rc);
|
|
|
|
GetThemeTextExtent(pbutn->hTheme,
|
|
hdc,
|
|
iPartId,
|
|
iStateId,
|
|
pszText,
|
|
-1,
|
|
Button_GetTextFlags(pbutn),
|
|
&rc,
|
|
&rc);
|
|
|
|
//
|
|
// Inflate the bounding rect a litte, but contrained to
|
|
// within the client area.
|
|
//
|
|
rc.top = max(rcClient.top, rc.top-1);
|
|
rc.bottom = min(rcClient.bottom, rc.bottom+1);
|
|
|
|
rc.left = max(rcClient.left, rc.left-1);
|
|
rc.right = min(rcClient.right, rc.right+1);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Try to leave a border all around text. That causes
|
|
// focus to hug text.
|
|
//
|
|
rc.top = max(rcClient.top, y-GetSystemMetrics(SM_CYBORDER));
|
|
rc.bottom = min(rcClient.bottom, rc.top + GetSystemMetrics(SM_CYEDGE) + cy);
|
|
|
|
rc.left = max(rcClient.left, x-GetSystemMetrics(SM_CXBORDER));
|
|
rc.right = min(rcClient.right, rc.left + GetSystemMetrics(SM_CXEDGE) + cx);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InflateRect(&rc, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYBORDER));
|
|
}
|
|
|
|
//
|
|
// Are back & fore colors set properly?
|
|
//
|
|
DrawFocusRect(hdc, &rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
UserLocalFree(pszText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// DrawCheck()
|
|
//
|
|
VOID Button_DrawCheck(PBUTN pbutn, HDC hdc, HBRUSH hBrush)
|
|
{
|
|
//
|
|
// if not themed
|
|
//
|
|
if (!Button_IsThemed(pbutn)) // Images don't have a mask so look ugly. Need to use old painting
|
|
{
|
|
RECT rc;
|
|
UINT uFlags;
|
|
BOOL fDoubleBlt = FALSE;
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
SIZE sizeChk = {0};
|
|
|
|
Button_CalcRect(pbutn, hdc, &rc, CBR_CHECKBOX, 0);
|
|
|
|
uFlags = 0;
|
|
|
|
if ( BUTTONSTATE(pbutn) & BST_CHECKMASK )
|
|
{
|
|
uFlags |= DFCS_CHECKED;
|
|
}
|
|
|
|
if ( BUTTONSTATE(pbutn) & BST_PUSHED )
|
|
{
|
|
uFlags |= DFCS_PUSHED;
|
|
}
|
|
|
|
if ( !IsWindowEnabled(pbutn->ci.hwnd) )
|
|
{
|
|
uFlags |= DFCS_INACTIVE;
|
|
}
|
|
|
|
switch (GetButtonType(ulStyle))
|
|
{
|
|
case BS_AUTORADIOBUTTON:
|
|
case BS_RADIOBUTTON:
|
|
fDoubleBlt = TRUE;
|
|
uFlags |= DFCS_BUTTONRADIO;
|
|
break;
|
|
|
|
case BS_3STATE:
|
|
case BS_AUTO3STATE:
|
|
if ((BUTTONSTATE(pbutn) & BST_CHECKMASK) == BST_INDETERMINATE)
|
|
{
|
|
uFlags |= DFCS_BUTTON3STATE;
|
|
break;
|
|
}
|
|
//
|
|
// FALL THRU
|
|
//
|
|
|
|
default:
|
|
uFlags |= DFCS_BUTTONCHECK;
|
|
break;
|
|
}
|
|
|
|
if ((ulStyle & BS_FLAT) != 0)
|
|
{
|
|
uFlags |= DFCS_FLAT | DFCS_MONO;
|
|
}
|
|
|
|
GetCheckBoxSize(hdc, pbutn, TRUE, &sizeChk);
|
|
|
|
rc.right = rc.left + sizeChk.cx;
|
|
rc.bottom = rc.top + sizeChk.cy;
|
|
|
|
FillRect(hdc, &rc, hBrush);
|
|
|
|
DrawFrameControl(hdc, &rc, DFC_BUTTON, uFlags);
|
|
}
|
|
else
|
|
{
|
|
int iStateId = 0;
|
|
int iPartId = 0;
|
|
|
|
Button_GetThemeIds(pbutn, &iPartId, &iStateId);
|
|
|
|
if ((iPartId != BP_RADIOBUTTON) && (iPartId != BP_CHECKBOX))
|
|
{
|
|
TraceMsg(TF_STANDARD, "Button_DrawCheck: Not a radio or check, iPartId = %d", iPartId);
|
|
return;
|
|
}
|
|
|
|
|
|
Button_DrawThemed(pbutn, hdc, iPartId, iStateId);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_DrawNewState(PBUTN pbutn, HDC hdc, HBRUSH hbr, UINT sOld)
|
|
{
|
|
if (sOld != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED))
|
|
{
|
|
UINT pbfPush;
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
|
|
pbfPush = IsPushButton(pbutn);
|
|
|
|
switch (GetButtonType(ulStyle))
|
|
{
|
|
case BS_GROUPBOX:
|
|
case BS_OWNERDRAW:
|
|
break;
|
|
|
|
default:
|
|
if (!pbfPush)
|
|
{
|
|
Button_DrawCheck(pbutn, hdc, hbr);
|
|
break;
|
|
}
|
|
|
|
case BS_PUSHBUTTON:
|
|
case BS_DEFPUSHBUTTON:
|
|
case BS_PUSHBOX:
|
|
Button_DrawPush(pbutn, hdc, pbfPush);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_DrawPush()
|
|
//
|
|
// Draws push-like button with text
|
|
//
|
|
VOID Button_DrawPush(PBUTN pbutn, HDC hdc, UINT pbfPush)
|
|
{
|
|
|
|
//
|
|
// if not themed
|
|
//
|
|
if (!Button_IsThemed(pbutn))
|
|
{
|
|
|
|
RECT rc;
|
|
UINT uFlags = 0;
|
|
UINT uState = 0;
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
NMCUSTOMDRAW nmcd = {0};
|
|
|
|
//
|
|
// Always a push button if calling this function
|
|
//
|
|
uState = DFCS_BUTTONPUSH;
|
|
|
|
GetClientRect(pbutn->ci.hwnd, &rc);
|
|
nmcd.hdc = hdc;
|
|
nmcd.rc = rc;
|
|
nmcd.dwItemSpec = GetWindowID(pbutn->ci.hwnd);
|
|
nmcd.uItemState = ButtonStateToCustomDrawState(pbutn);
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_PUSHED)
|
|
{
|
|
uState |= DFCS_PUSHED;
|
|
}
|
|
|
|
pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREERASE, &nmcd);
|
|
|
|
if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
|
|
{
|
|
if (!pbutn->fPaintKbdCuesOnly)
|
|
{
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_CHECKMASK)
|
|
{
|
|
uState |= DFCS_CHECKED;
|
|
}
|
|
|
|
if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT))
|
|
{
|
|
uFlags = BF_SOFT;
|
|
}
|
|
|
|
if ((ulStyle & BS_FLAT) != 0)
|
|
{
|
|
uFlags |= DFCS_FLAT | DFCS_MONO;
|
|
}
|
|
|
|
if (pbfPush & PBF_DEFAULT)
|
|
{
|
|
int cxBorder = GetSystemMetrics(SM_CXBORDER);
|
|
int cyBorder = GetSystemMetrics(SM_CYBORDER);
|
|
|
|
int clFrame = 1;
|
|
|
|
int x = rc.left;
|
|
int y = rc.top;
|
|
|
|
int cxWidth = cxBorder * clFrame;
|
|
int cyWidth = cyBorder * clFrame;
|
|
|
|
int cx = rc.right - x - cxWidth;
|
|
int cy = rc.bottom - y - cyWidth;
|
|
|
|
HBRUSH hbrFill = GetSysColorBrush(COLOR_WINDOWFRAME);
|
|
HBRUSH hbrSave = SelectObject(hdc, hbrFill);
|
|
|
|
PatBlt(hdc, x, y, cxWidth, cy, PATCOPY);
|
|
PatBlt(hdc, x + cxWidth, y, cx, cyWidth, PATCOPY);
|
|
PatBlt(hdc, x, y + cy, cx, cyWidth, PATCOPY);
|
|
PatBlt(hdc, x + cx, y + cyWidth, cxWidth, cy, PATCOPY);
|
|
|
|
SelectObject(hdc, hbrSave);
|
|
|
|
InflateRect(&rc, -cxBorder, -cyBorder);
|
|
|
|
if (uState & DFCS_PUSHED)
|
|
{
|
|
uFlags |= DFCS_FLAT;
|
|
}
|
|
}
|
|
|
|
DrawFrameControl(hdc, &rc, DFC_BUTTON, uState | uFlags);
|
|
}
|
|
if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
|
|
CICustomDrawNotify(&pbutn->ci, CDDS_POSTERASE, &nmcd);
|
|
|
|
pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREPAINT, &nmcd);
|
|
|
|
if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
|
|
{
|
|
Button_DrawText(pbutn, hdc, DBT_TEXT | (BUTTONSTATE(pbutn) &
|
|
BST_FOCUS ? DBT_FOCUS : 0), (uState & DFCS_PUSHED));
|
|
|
|
if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
|
|
CICustomDrawNotify(&pbutn->ci, CDDS_POSTPAINT, &nmcd);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int iStateId = 0;
|
|
int iPartId = 0;
|
|
|
|
Button_GetThemeIds(pbutn, &iPartId, &iStateId);
|
|
if (iPartId != BP_PUSHBUTTON)
|
|
{
|
|
TraceMsg(TF_STANDARD, "Not a Pushbutton");
|
|
return;
|
|
}
|
|
Button_DrawThemed(pbutn, hdc, iPartId, iStateId);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
BOOL Button_OnSetImageList(PBUTN pbutn, BUTTON_IMAGELIST* biml)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
if (biml)
|
|
{
|
|
if (biml->himl)
|
|
{
|
|
pbutn->rcIcon = biml->margin;
|
|
pbutn->himl = biml->himl;
|
|
pbutn->uAlign = biml->uAlign;
|
|
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
void ApplyMarginsToRect(RECT* prcm, RECT* prc)
|
|
{
|
|
prc->left -= prcm->left;
|
|
prc->top -= prcm->top;
|
|
prc->right += prcm->right;
|
|
prc->bottom += prcm->bottom;
|
|
}
|
|
|
|
BOOL Button_OnGetIdealSize(PBUTN pbutn, PSIZE psize)
|
|
{
|
|
UINT bsWnd;
|
|
RECT rc = {0};
|
|
HBRUSH hBrush;
|
|
HDC hdc;
|
|
|
|
if (psize == NULL)
|
|
return FALSE;
|
|
|
|
GetWindowRect(pbutn->ci.hwnd, &rc);
|
|
|
|
hdc = GetDC (pbutn->ci.hwnd);
|
|
if (hdc)
|
|
{
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
|
|
bsWnd = GetButtonType(ulStyle);
|
|
hBrush = Button_InitDC(pbutn, hdc);
|
|
|
|
switch (bsWnd)
|
|
{
|
|
case BS_PUSHBUTTON:
|
|
case BS_DEFPUSHBUTTON:
|
|
{
|
|
LPWSTR pszText = NULL;
|
|
INT cchText = GetWindowTextLength(pbutn->ci.hwnd);
|
|
|
|
if (cchText > 0)
|
|
{
|
|
pszText = UserLocalAlloc(0, (cchText+1)*SIZEOF(WCHAR));
|
|
|
|
if (pszText)
|
|
{
|
|
RECT rcText = {0};
|
|
RECT rcIcon = {0};
|
|
int cx = 0, cy = 0;
|
|
int iStateId = 0;
|
|
int iPartId = 0;
|
|
|
|
GetWindowText(pbutn->ci.hwnd, pszText, cchText+1);
|
|
|
|
if (Button_IsThemed(pbutn))
|
|
{
|
|
Button_GetThemeIds(pbutn, &iPartId, &iStateId);
|
|
|
|
// First: Get the text rect
|
|
GetThemeTextExtent(pbutn->hTheme, hdc, iPartId, iStateId, pszText, cchText, 0, &rcText, &rcText);
|
|
ApplyMarginsToRect(&pbutn->rcText, &rcText);
|
|
|
|
rc = rcText;
|
|
|
|
// We should now have The button with text.
|
|
}
|
|
else
|
|
{
|
|
int cxWidth = 2 * GetSystemMetrics(SM_CXEDGE);
|
|
int cyWidth = 3 * GetSystemMetrics(SM_CYEDGE);
|
|
if (IsPushButton(pbutn) & PBF_DEFAULT)
|
|
{
|
|
cxWidth += 2 * GetSystemMetrics(SM_CXBORDER);
|
|
cyWidth += 2 * GetSystemMetrics(SM_CXBORDER);
|
|
}
|
|
|
|
DrawText(hdc, pszText, cchText, &rcText, DT_CALCRECT);
|
|
ApplyMarginsToRect(&pbutn->rcText, &rcText);
|
|
|
|
rcText.bottom += cyWidth + 1; // +1 because draw text adds a single pixel to the first char, but not the last...
|
|
rcText.right += cxWidth + 1;
|
|
}
|
|
|
|
if (pbutn->himl)
|
|
{
|
|
rc.top = rc.left = 0; // We turn this into a width not a position
|
|
|
|
CCGetIconSize(&pbutn->ci, pbutn->himl, &cx, &cy);
|
|
|
|
rcIcon.bottom = cy;
|
|
rcIcon.right = cx;
|
|
|
|
ApplyMarginsToRect(&pbutn->rcIcon, &rcIcon);
|
|
|
|
switch (pbutn->uAlign)
|
|
{
|
|
case BUTTON_IMAGELIST_ALIGN_TOP:
|
|
case BUTTON_IMAGELIST_ALIGN_BOTTOM:
|
|
rc.bottom = RECTHEIGHT(rcIcon) + RECTHEIGHT(rcText);
|
|
rc.right = max(RECTWIDTH(rcIcon), RECTWIDTH(rcText));
|
|
break;
|
|
|
|
case BUTTON_IMAGELIST_ALIGN_CENTER:
|
|
// This means no text
|
|
rc.bottom = RECTHEIGHT(rcIcon);
|
|
rc.right = RECTWIDTH(rcIcon);
|
|
break;
|
|
|
|
case BUTTON_IMAGELIST_ALIGN_RIGHT:
|
|
case BUTTON_IMAGELIST_ALIGN_LEFT:
|
|
// Fall
|
|
default:
|
|
rc.right = RECTWIDTH(rcIcon) + RECTWIDTH(rcText);
|
|
rc.bottom = max(RECTHEIGHT(rcIcon), RECTHEIGHT(rcText));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = rcText;
|
|
}
|
|
|
|
if (Button_IsThemed(pbutn))
|
|
{
|
|
GetThemeBackgroundExtent(pbutn->hTheme, hdc, iPartId, iStateId, &rc, &rc);
|
|
}
|
|
|
|
UserLocalFree(pszText);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the font which may have been loaded by ButtonInitDC.
|
|
//
|
|
if (pbutn->hFont)
|
|
{
|
|
SelectObject(hdc, GetStockObject(SYSTEM_FONT));
|
|
}
|
|
ReleaseDC(pbutn->ci.hwnd, hdc);
|
|
}
|
|
|
|
psize->cx = RECTWIDTH(rc);
|
|
psize->cy = RECTHEIGHT(rc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_Paint(PBUTN pbutn, HDC hdc)
|
|
{
|
|
RECT rc;
|
|
RECT rcText;
|
|
HBRUSH hBrush;
|
|
HBRUSH hBrushSave = NULL;
|
|
BOOL fDrawBackground = TRUE;
|
|
ULONG ulStyle = GET_STYLE(pbutn);
|
|
CCDBUFFER db = {0};
|
|
UINT bsWnd = GetButtonType(ulStyle);
|
|
UINT pbfPush = IsPushButton(pbutn);
|
|
BOOL fTransparent = FALSE;
|
|
int iPartId = 0;
|
|
int iStateId = 0;
|
|
|
|
GetClientRect(pbutn->ci.hwnd, &rc);
|
|
|
|
if (Button_IsThemed(pbutn) &&
|
|
(bsWnd != LOBYTE(BS_GROUPBOX)) &&
|
|
(bsWnd != LOBYTE(BS_OWNERDRAW)) &&
|
|
!pbutn->fPaintKbdCuesOnly)
|
|
{
|
|
hdc = CCBeginDoubleBuffer(hdc, &rc, &db);
|
|
|
|
Button_GetThemeIds(pbutn, &iPartId, &iStateId);
|
|
fTransparent = CCShouldAskForBits(&pbutn->ci, pbutn->hTheme, iPartId, iStateId);
|
|
if (fTransparent)
|
|
{
|
|
fDrawBackground = (TRUE != CCSendPrint(&pbutn->ci, hdc));
|
|
}
|
|
}
|
|
|
|
hBrush = Button_InitDC(pbutn, hdc);
|
|
|
|
|
|
if ((!pbfPush || fTransparent) && !pbutn->fPaintKbdCuesOnly &&
|
|
fDrawBackground)
|
|
{
|
|
if ((bsWnd != LOBYTE(BS_OWNERDRAW)) &&
|
|
(bsWnd != LOBYTE(BS_GROUPBOX)))
|
|
{
|
|
//
|
|
// Fill the client area with the background brush
|
|
// before we begin painting.
|
|
//
|
|
FillRect(hdc, &rc, hBrush);
|
|
}
|
|
|
|
hBrushSave = SelectObject(hdc, hBrush);
|
|
}
|
|
|
|
switch (bsWnd)
|
|
{
|
|
case BS_CHECKBOX:
|
|
case BS_RADIOBUTTON:
|
|
case BS_AUTORADIOBUTTON:
|
|
case BS_3STATE:
|
|
case BS_AUTOCHECKBOX:
|
|
case BS_AUTO3STATE:
|
|
if (!pbfPush)
|
|
{
|
|
if (!Button_IsThemed(pbutn))
|
|
{
|
|
Button_DrawText(pbutn, hdc,
|
|
DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
|
|
}
|
|
|
|
if (!pbutn->fPaintKbdCuesOnly || Button_IsThemed(pbutn))
|
|
{
|
|
Button_DrawCheck(pbutn, hdc, hBrush);
|
|
}
|
|
break;
|
|
}
|
|
//
|
|
// Fall through for PUSHLIKE buttons
|
|
//
|
|
|
|
case BS_PUSHBUTTON:
|
|
case BS_DEFPUSHBUTTON:
|
|
Button_DrawPush(pbutn, hdc, pbfPush);
|
|
break;
|
|
|
|
case BS_PUSHBOX:
|
|
Button_DrawText(pbutn, hdc,
|
|
DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
|
|
|
|
Button_DrawNewState(pbutn, hdc, hBrush, 0);
|
|
break;
|
|
|
|
case BS_USERBUTTON:
|
|
// Don't support USERBUTTON in v6. This has been superceded by OWNERDRAW in win32.
|
|
break;
|
|
|
|
case BS_OWNERDRAW:
|
|
Button_OwnerDraw(pbutn, hdc, ODA_DRAWENTIRE);
|
|
break;
|
|
|
|
case BS_GROUPBOX:
|
|
Button_CalcRect(pbutn, hdc, &rcText, CBR_GROUPTEXT, 0);
|
|
|
|
//----- get theme part, state for groupbox ----
|
|
if (Button_IsThemed(pbutn))
|
|
{
|
|
Button_GetThemeIds(pbutn, &iPartId, &iStateId);
|
|
}
|
|
|
|
if (!pbutn->fPaintKbdCuesOnly)
|
|
{
|
|
UINT uFlags;
|
|
BOOL fFillMyself = TRUE;
|
|
|
|
Button_CalcRect(pbutn, hdc, &rc, CBR_GROUPFRAME, 0);
|
|
|
|
uFlags = ((ulStyle & BS_FLAT) != 0) ? BF_FLAT | BF_MONO : 0;
|
|
if (!Button_IsThemed(pbutn))
|
|
{
|
|
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT | uFlags);
|
|
}
|
|
else
|
|
{
|
|
DrawThemeBackground(pbutn->hTheme, hdc, iPartId, iStateId, &rc, 0);
|
|
fFillMyself = (FALSE == CCSendPrintRect(&pbutn->ci, hdc, &rcText));
|
|
}
|
|
|
|
if (fFillMyself)
|
|
{
|
|
FillRect(hdc, &rcText, hBrush);
|
|
}
|
|
}
|
|
|
|
// FillRect(hdc, &rc, hBrush);
|
|
if (!Button_IsThemed(pbutn))
|
|
{
|
|
Button_DrawText(pbutn, hdc, DBT_TEXT, FALSE);
|
|
}
|
|
else
|
|
{
|
|
LPWSTR pszText = NULL;
|
|
INT cchText = GetWindowTextLength(pbutn->ci.hwnd);
|
|
|
|
if (cchText > 0)
|
|
{
|
|
pszText = UserLocalAlloc(0, (cchText+1)*SIZEOF(WCHAR));
|
|
if (pszText)
|
|
{
|
|
DWORD dwTextFlags = Button_GetTextFlags(pbutn);
|
|
|
|
GetWindowTextW(pbutn->ci.hwnd, pszText, cchText+1);
|
|
|
|
//
|
|
// Button_CalcRect padded by a CXEDGE so the groupbox frame wouldn't
|
|
// be flush with the Group text
|
|
//
|
|
rcText.left += GetSystemMetrics(SM_CXEDGE);
|
|
|
|
if (FAILED(DrawThemeText(pbutn->hTheme,
|
|
hdc,
|
|
iPartId,
|
|
iStateId,
|
|
pszText,
|
|
cchText,
|
|
dwTextFlags,
|
|
0,
|
|
&rcText)))
|
|
{
|
|
TraceMsg(TF_STANDARD, "Button_Paint failed to render groupbox text");
|
|
}
|
|
|
|
UserLocalFree(pszText);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!pbfPush && hBrushSave)
|
|
{
|
|
SelectObject(hdc, hBrushSave);
|
|
}
|
|
|
|
//
|
|
// Release the font which may have been loaded by ButtonInitDC.
|
|
//
|
|
if (pbutn->hFont)
|
|
{
|
|
SelectObject(hdc, GetStockObject(SYSTEM_FONT));
|
|
}
|
|
|
|
CCEndDoubleBuffer(&db);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
VOID Button_Repaint(PBUTN pbutn)
|
|
{
|
|
HDC hdc = Button_GetDC(pbutn, NULL);
|
|
|
|
if (hdc != NULL)
|
|
{
|
|
Button_Paint(pbutn, hdc);
|
|
Button_ReleaseDC(pbutn, hdc, NULL);
|
|
}
|
|
}
|
|
|
|
VOID Button_SetHot(PBUTN pbutn, BOOL fHot, DWORD dwReason)
|
|
{
|
|
NMBCHOTITEM nmhot = {0};
|
|
|
|
// Send a notification about the hot item change
|
|
if (fHot)
|
|
{
|
|
nmhot.dwFlags = HICF_ENTERING;
|
|
pbutn->buttonState |= BST_HOT;
|
|
}
|
|
else
|
|
{
|
|
nmhot.dwFlags = HICF_LEAVING;
|
|
pbutn->buttonState &= ~BST_HOT;
|
|
}
|
|
|
|
nmhot.dwFlags |= dwReason;
|
|
|
|
CCSendNotify(&pbutn->ci, BCN_HOTITEMCHANGE, &nmhot.hdr);
|
|
}
|
|
|
|
void Button_EraseOwnerDraw(PBUTN pbutn, HDC hdc)
|
|
{
|
|
if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_OWNERDRAW))
|
|
{
|
|
RECT rc;
|
|
HBRUSH hbr;
|
|
//
|
|
// Handle erase background for owner draw buttons.
|
|
//
|
|
GetClientRect(pbutn->ci.hwnd, &rc);
|
|
hbr = (HBRUSH)SendMessage(GetParent(pbutn->ci.hwnd), WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)pbutn->ci.hwnd);
|
|
FillRect(hdc, &rc, hbr);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------//
|
|
//
|
|
// Button_WndProc
|
|
//
|
|
// WndProc for buttons, check boxes, etc.
|
|
//
|
|
LRESULT APIENTRY Button_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
UINT wOldState;
|
|
RECT rc;
|
|
HDC hdc;
|
|
HBRUSH hbr;
|
|
PAINTSTRUCT ps;
|
|
PBUTN pbutn;
|
|
LRESULT lResult = FALSE;
|
|
|
|
//
|
|
// Get the instance data for this button control
|
|
//
|
|
pbutn = Button_GetPtr(hwnd);
|
|
if (!pbutn && uMsg != WM_NCCREATE)
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCHITTEST:
|
|
if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_GROUPBOX))
|
|
{
|
|
lResult = (LONG)HTTRANSPARENT;
|
|
}
|
|
else
|
|
{
|
|
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
|
|
if ( lResult == HTCLIENT && Button_IsThemed(pbutn))
|
|
{
|
|
HRESULT hr;
|
|
int iPartId = 0;
|
|
int iStateId = 0;
|
|
POINT pt;
|
|
WORD wHitTestCode;
|
|
|
|
hr = Button_GetThemeIds(pbutn, &iPartId, &iStateId);
|
|
if ( SUCCEEDED(hr) )
|
|
GetWindowRect(pbutn->ci.hwnd, &rc);
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
hr = HitTestThemeBackground(pbutn->hTheme,
|
|
NULL,
|
|
iPartId,
|
|
iStateId,
|
|
0,
|
|
&rc,
|
|
NULL,
|
|
pt,
|
|
&wHitTestCode);
|
|
if ( SUCCEEDED(hr) && wHitTestCode == HTTRANSPARENT)
|
|
{
|
|
lResult = (LRESULT)HTTRANSPARENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
Button_EraseOwnerDraw(pbutn, (HDC)wParam);
|
|
//
|
|
// Do nothing for other buttons, but don't let DefWndProc() do it
|
|
// either. It will be erased in Button_Paint().
|
|
//
|
|
lResult = (LONG)TRUE;
|
|
break;
|
|
|
|
case WM_PRINTCLIENT:
|
|
Button_EraseOwnerDraw(pbutn, (HDC)wParam);
|
|
Button_Paint(pbutn, (HDC)wParam);
|
|
break;
|
|
|
|
case WM_CREATE:
|
|
pbutn->hTheme = Button_GetTheme(pbutn);
|
|
CIInitialize(&pbutn->ci, hwnd, (LPCREATESTRUCT)lParam);
|
|
|
|
SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
//
|
|
// If wParam != NULL, then this is a subclassed paint.
|
|
//
|
|
if (wParam)
|
|
{
|
|
hdc = (HDC)wParam;
|
|
}
|
|
else
|
|
{
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
}
|
|
|
|
if (IsWindowVisible(pbutn->ci.hwnd))
|
|
{
|
|
Button_Paint(pbutn, hdc);
|
|
}
|
|
|
|
if (!wParam)
|
|
{
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
|
|
BUTTONSTATE(pbutn) |= BST_FOCUS;
|
|
if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_OWNERDRAW))
|
|
{
|
|
HDC hdc = Button_GetDC(pbutn, NULL);
|
|
if (hdc)
|
|
{
|
|
Button_DrawText(pbutn, hdc, DBT_FOCUS, FALSE);
|
|
Button_ReleaseDC(pbutn, hdc, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InvalidateRect(pbutn->ci.hwnd, NULL, FALSE);
|
|
}
|
|
|
|
if ((GET_STYLE(pbutn)& BS_NOTIFY) != 0)
|
|
{
|
|
Button_NotifyParent(pbutn, BN_SETFOCUS);
|
|
}
|
|
|
|
if (!(BUTTONSTATE(pbutn) & BST_INCLICK))
|
|
{
|
|
switch (GetButtonType(GET_STYLE(pbutn)))
|
|
{
|
|
case LOBYTE(BS_RADIOBUTTON):
|
|
case LOBYTE(BS_AUTORADIOBUTTON):
|
|
|
|
if (!(BUTTONSTATE(pbutn) & BST_DONTCLICK))
|
|
{
|
|
if (!(BUTTONSTATE(pbutn) & BST_CHECKMASK))
|
|
{
|
|
Button_NotifyParent(pbutn, BN_CLICKED);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_GETDLGCODE:
|
|
|
|
lResult = DLGC_BUTTON;
|
|
|
|
switch (GetButtonType(GET_STYLE(pbutn)))
|
|
{
|
|
case LOBYTE(BS_DEFPUSHBUTTON):
|
|
lResult |= DLGC_DEFPUSHBUTTON;
|
|
break;
|
|
|
|
case LOBYTE(BS_PUSHBUTTON):
|
|
case LOBYTE(BS_PUSHBOX):
|
|
lResult |= DLGC_UNDEFPUSHBUTTON;
|
|
break;
|
|
|
|
case LOBYTE(BS_AUTORADIOBUTTON):
|
|
case LOBYTE(BS_RADIOBUTTON):
|
|
lResult |= DLGC_RADIOBUTTON;
|
|
break;
|
|
|
|
case LOBYTE(BS_GROUPBOX):
|
|
//
|
|
// remove DLGC_BUTTON
|
|
//
|
|
lResult = DLGC_STATIC;
|
|
break;
|
|
|
|
case LOBYTE(BS_CHECKBOX):
|
|
case LOBYTE(BS_AUTOCHECKBOX):
|
|
|
|
//
|
|
// If this is a char that is a '=/+', or '-', we want it
|
|
//
|
|
if (lParam && ((LPMSG)lParam)->message == WM_CHAR)
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case TEXT('='):
|
|
case TEXT('+'):
|
|
case TEXT('-'):
|
|
lResult |= DLGC_WANTCHARS;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_CAPTURECHANGED:
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_CAPTURED)
|
|
{
|
|
//
|
|
// Unwittingly, we've been kicked out of capture,
|
|
// so undepress etc.
|
|
//
|
|
if (BUTTONSTATE(pbutn) & BST_MOUSE)
|
|
{
|
|
SendMessage(pbutn->ci.hwnd, BM_SETSTATE, FALSE, 0);
|
|
}
|
|
BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE);
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
|
|
//
|
|
// If we are losing the focus and we are in "capture mode", click
|
|
// the button. This allows tab and space keys to overlap for
|
|
// fast toggle of a series of buttons.
|
|
//
|
|
if (BUTTONSTATE(pbutn) & BST_MOUSE)
|
|
{
|
|
//
|
|
// If for some reason we are killing the focus, and we have the
|
|
// mouse captured, don't notify the parent we got clicked. This
|
|
// breaks Omnis Quartz otherwise.
|
|
//
|
|
SendMessage(pbutn->ci.hwnd, BM_SETSTATE, FALSE, 0);
|
|
}
|
|
|
|
Button_ReleaseCapture(pbutn, TRUE);
|
|
|
|
BUTTONSTATE(pbutn) &= ~BST_FOCUS;
|
|
|
|
if ((GET_STYLE(pbutn) & BS_NOTIFY) != 0)
|
|
{
|
|
Button_NotifyParent(pbutn, BN_KILLFOCUS);
|
|
}
|
|
|
|
//
|
|
// Since the bold border around the defpushbutton is done by
|
|
// someone else, we need to invalidate the rect so that the
|
|
// focus rect is repainted properly.
|
|
//
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
|
|
//
|
|
// Double click messages are recognized for BS_RADIOBUTTON,
|
|
// BS_USERBUTTON, and BS_OWNERDRAW styles. For all other buttons,
|
|
// double click is handled like a normal button down.
|
|
//
|
|
switch (GetButtonType(GET_STYLE(pbutn)))
|
|
{
|
|
default:
|
|
if ((GET_STYLE(pbutn) & BS_NOTIFY) == 0)
|
|
goto btnclick;
|
|
|
|
case LOBYTE(BS_USERBUTTON):
|
|
case LOBYTE(BS_RADIOBUTTON):
|
|
case LOBYTE(BS_OWNERDRAW):
|
|
Button_NotifyParent(pbutn, BN_DOUBLECLICKED);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if (BUTTONSTATE(pbutn) & BST_MOUSE)
|
|
{
|
|
Button_ReleaseCapture(pbutn, TRUE);
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSELEAVE:
|
|
{
|
|
//
|
|
// We should only be requesting mouseleave messages
|
|
// if we are themed but check anyway
|
|
//
|
|
if (pbutn->buttonState & BST_HOT)
|
|
{
|
|
Button_SetHot(pbutn, FALSE, HICF_MOUSE);
|
|
InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
//
|
|
// If the hot bit is not already set
|
|
//
|
|
// 300925: Can't hottrack ownerdraw buttons for app compat reasons
|
|
//
|
|
if (!TESTFLAG(pbutn->buttonState, BST_HOT) &&
|
|
GetButtonType(GET_STYLE(pbutn)) != LOBYTE(BS_OWNERDRAW))
|
|
{
|
|
TRACKMOUSEEVENT tme;
|
|
|
|
//
|
|
// Set the hot bit and request that
|
|
// we be notified when the mouse leaves
|
|
//
|
|
Button_SetHot(pbutn, TRUE, HICF_MOUSE);
|
|
|
|
tme.cbSize = sizeof(tme);
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = pbutn->ci.hwnd;
|
|
tme.dwHoverTime = 0;
|
|
|
|
TrackMouseEvent(&tme);
|
|
InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
|
|
}
|
|
|
|
if (!(BUTTONSTATE(pbutn) & BST_MOUSE))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// FALL THRU
|
|
//
|
|
|
|
case WM_LBUTTONDOWN:
|
|
btnclick:
|
|
if (Button_SetCapture(pbutn, BST_MOUSE))
|
|
{
|
|
POINT pt;
|
|
GetClientRect(pbutn->ci.hwnd, &rc);
|
|
POINTSTOPOINT(pt, lParam);
|
|
SendMessage(pbutn->ci.hwnd, BM_SETSTATE, PtInRect(&rc, pt), 0);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
if (BUTTONSTATE(pbutn) & BST_MOUSE)
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
if (GetButtonType(GET_STYLE(pbutn)) != LOBYTE(BS_CHECKBOX) &&
|
|
GetButtonType(GET_STYLE(pbutn)) != LOBYTE(BS_AUTOCHECKBOX))
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
switch (wParam)
|
|
{
|
|
case TEXT('+'):
|
|
case TEXT('='):
|
|
//
|
|
// we must Set the check mark on.
|
|
//
|
|
wParam = 1;
|
|
|
|
goto SetCheck;
|
|
|
|
case TEXT('-'):
|
|
//
|
|
// Set the check mark off.
|
|
//
|
|
wParam = 0;
|
|
SetCheck:
|
|
//
|
|
// Must notify only if the check status changes
|
|
//
|
|
if ((WORD)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (WORD)wParam)
|
|
{
|
|
//
|
|
// We must check/uncheck only if it is AUTO
|
|
//
|
|
if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_AUTOCHECKBOX))
|
|
{
|
|
if (Button_SetCapture(pbutn, 0))
|
|
{
|
|
SendMessage(pbutn->ci.hwnd, BM_SETCHECK, wParam, 0);
|
|
Button_ReleaseCapture(pbutn, TRUE);
|
|
}
|
|
}
|
|
|
|
Button_NotifyParent(pbutn, BN_CLICKED);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
goto CallDWP;
|
|
}
|
|
|
|
break;
|
|
|
|
case BCM_GETIDEALSIZE:
|
|
return Button_OnGetIdealSize(pbutn, (PSIZE)lParam);
|
|
|
|
case BCM_SETIMAGELIST:
|
|
return Button_OnSetImageList(pbutn, (BUTTON_IMAGELIST*)lParam);
|
|
|
|
case BCM_GETIMAGELIST:
|
|
{
|
|
BUTTON_IMAGELIST* biml = (BUTTON_IMAGELIST*)lParam;
|
|
if (biml)
|
|
{
|
|
biml->himl = pbutn->himl;
|
|
biml->uAlign = pbutn->uAlign;
|
|
biml->margin = pbutn->rcIcon;
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BCM_SETTEXTMARGIN:
|
|
{
|
|
RECT* prc = (RECT*)lParam;
|
|
if (prc)
|
|
{
|
|
pbutn->rcText = *prc;
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BCM_GETTEXTMARGIN:
|
|
{
|
|
RECT* prc = (RECT*)lParam;
|
|
if (prc)
|
|
{
|
|
*prc = pbutn->rcText;
|
|
return TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BM_CLICK:
|
|
|
|
//
|
|
// Don't recurse into this code!
|
|
//
|
|
if (BUTTONSTATE(pbutn) & BST_INBMCLICK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
BUTTONSTATE(pbutn) |= BST_INBMCLICK;
|
|
SendMessage(pbutn->ci.hwnd, WM_LBUTTONDOWN, 0, 0);
|
|
SendMessage(pbutn->ci.hwnd, WM_LBUTTONUP, 0, 0);
|
|
BUTTONSTATE(pbutn) &= ~BST_INBMCLICK;
|
|
|
|
//
|
|
// FALL THRU
|
|
//
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_MOUSE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (wParam == VK_SPACE)
|
|
{
|
|
if (Button_SetCapture(pbutn, 0))
|
|
{
|
|
SendMessage(pbutn->ci.hwnd, BM_SETSTATE, TRUE, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Button_ReleaseCapture(pbutn, FALSE);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_KEYUP:
|
|
case WM_SYSKEYUP:
|
|
|
|
if (BUTTONSTATE(pbutn) & BST_MOUSE)
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
//
|
|
// Don't cancel the capture mode on the up of the tab in case the
|
|
// guy is overlapping tab and space keys.
|
|
//
|
|
if (wParam == VK_TAB)
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
//
|
|
// WARNING: pbutn->ci.hwnd is history after this call!
|
|
//
|
|
Button_ReleaseCapture(pbutn, (wParam == VK_SPACE));
|
|
|
|
if (uMsg == WM_SYSKEYUP)
|
|
{
|
|
goto CallDWP;
|
|
}
|
|
|
|
break;
|
|
|
|
case BM_GETSTATE:
|
|
|
|
lResult = (LONG)BUTTONSTATE(pbutn);
|
|
break;
|
|
|
|
case BM_SETSTATE:
|
|
|
|
wOldState = (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED);
|
|
|
|
if (wParam)
|
|
{
|
|
BUTTONSTATE(pbutn) |= BST_PUSHED;
|
|
}
|
|
else
|
|
{
|
|
BUTTONSTATE(pbutn) &= ~BST_PUSHED;
|
|
}
|
|
|
|
if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_USERBUTTON))
|
|
{
|
|
Button_NotifyParent(pbutn, (UINT)(wParam ? BN_PUSHED : BN_UNPUSHED));
|
|
}
|
|
|
|
if (wOldState != (BOOL)(BUTTONSTATE(pbutn) & BST_PUSHED))
|
|
{
|
|
// Only invalidate if the state changed.
|
|
InvalidateRect(pbutn->ci.hwnd, NULL, FALSE);
|
|
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
|
|
}
|
|
|
|
break;
|
|
|
|
case BM_GETCHECK:
|
|
|
|
lResult = (LONG)(BUTTONSTATE(pbutn) & BST_CHECKMASK);
|
|
break;
|
|
|
|
case BM_SETCHECK:
|
|
|
|
switch (GetButtonType(GET_STYLE(pbutn)))
|
|
{
|
|
case LOBYTE(BS_RADIOBUTTON):
|
|
case LOBYTE(BS_AUTORADIOBUTTON):
|
|
|
|
if (wParam)
|
|
{
|
|
SetWindowState(pbutn->ci.hwnd, WS_TABSTOP);
|
|
}
|
|
else
|
|
{
|
|
ClearWindowState(pbutn->ci.hwnd, WS_TABSTOP);
|
|
}
|
|
|
|
//
|
|
// FALL THRU
|
|
//
|
|
|
|
case LOBYTE(BS_CHECKBOX):
|
|
case LOBYTE(BS_AUTOCHECKBOX):
|
|
|
|
if (wParam)
|
|
{
|
|
wParam = 1;
|
|
}
|
|
goto CheckIt;
|
|
|
|
case LOBYTE(BS_3STATE):
|
|
case LOBYTE(BS_AUTO3STATE):
|
|
|
|
if (wParam > BST_INDETERMINATE)
|
|
{
|
|
wParam = BST_INDETERMINATE;
|
|
}
|
|
|
|
CheckIt:
|
|
if ((UINT)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (UINT)wParam)
|
|
{
|
|
BUTTONSTATE(pbutn) &= ~BST_CHECKMASK;
|
|
BUTTONSTATE(pbutn) |= (UINT)wParam;
|
|
|
|
if (!IsWindowVisible(pbutn->ci.hwnd))
|
|
{
|
|
break;
|
|
}
|
|
|
|
InvalidateRect(pbutn->ci.hwnd, NULL, FALSE);
|
|
|
|
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case BM_SETSTYLE:
|
|
|
|
AlterWindowStyle(hwnd, BS_TYPEMASK, (DWORD)wParam);
|
|
|
|
if (lParam)
|
|
{
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
|
|
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
|
|
break;
|
|
|
|
case WM_SETTEXT:
|
|
|
|
//
|
|
// In case the new group name is longer than the old name,
|
|
// this paints over the old name before repainting the group
|
|
// box with the new name.
|
|
//
|
|
if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_GROUPBOX))
|
|
{
|
|
hdc = Button_GetDC(pbutn, &hbr);
|
|
if (hdc != NULL)
|
|
{
|
|
Button_CalcRect(pbutn, hdc, &rc, CBR_GROUPTEXT, 0);
|
|
InvalidateRect(hwnd, &rc, TRUE);
|
|
FillRect(hdc, &rc, hbr);
|
|
Button_ReleaseDC(pbutn, hdc, &hbr);
|
|
}
|
|
}
|
|
|
|
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
|
|
NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_WINDOW, INDEXID_CONTAINER);
|
|
goto DoEnable;
|
|
|
|
case WM_ENABLE:
|
|
lResult = 0L;
|
|
|
|
DoEnable:
|
|
Button_Repaint(pbutn);
|
|
break;
|
|
|
|
case WM_SETFONT:
|
|
|
|
//
|
|
// wParam - handle to the font
|
|
// lParam - if true, redraw else don't
|
|
//
|
|
Button_SetFont(pbutn, (HFONT)wParam, (BOOL)(lParam != 0));
|
|
break;
|
|
|
|
case WM_GETFONT:
|
|
lResult = (LRESULT)pbutn->hFont;
|
|
break;
|
|
|
|
case BM_GETIMAGE:
|
|
case BM_SETIMAGE:
|
|
|
|
if (!IsValidImage(wParam, (GET_STYLE(pbutn) & BS_IMAGEMASK) != 0, IMAGE_BMMAX))
|
|
{
|
|
TraceMsg(TF_STANDARD, "UxButton: Invalid button image type");
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
else
|
|
{
|
|
HANDLE hOld = pbutn->hImage;
|
|
|
|
if (uMsg == BM_SETIMAGE)
|
|
{
|
|
pbutn->hImage = (HANDLE)lParam;
|
|
if (IsWindowVisible(pbutn->ci.hwnd))
|
|
{
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
lResult = (LRESULT)hOld;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
|
|
if (pbutn->hTheme)
|
|
{
|
|
CloseThemeData(pbutn->hTheme);
|
|
}
|
|
UserLocalFree(pbutn);
|
|
|
|
TraceMsg(TF_STANDARD, "BUTTON: Clearing button instance pointer.");
|
|
Button_SetPtr(hwnd, NULL);
|
|
|
|
break;
|
|
|
|
case WM_NCCREATE:
|
|
|
|
pbutn = (PBUTN)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(BUTN));
|
|
if (pbutn)
|
|
{
|
|
//
|
|
// Success... store the instance pointer.
|
|
//
|
|
TraceMsg(TF_STANDARD, "BUTTON: Setting button instance pointer.");
|
|
Button_SetPtr(hwnd, pbutn);
|
|
pbutn->ci.hwnd = hwnd;
|
|
pbutn->pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);
|
|
|
|
SetRect(&pbutn->rcText, GetSystemMetrics(SM_CXEDGE) / 2, GetSystemMetrics(SM_CYEDGE) / 2,
|
|
GetSystemMetrics(SM_CXEDGE) / 2, GetSystemMetrics(SM_CYEDGE) / 2);
|
|
|
|
//
|
|
// Borland's OBEX has a button with style 0x98; We didn't strip
|
|
// these bits in win3.1 because we checked for 0x08.
|
|
// Stripping these bits cause a GP Fault in OBEX.
|
|
// For win3.1 guys, I use the old code to strip the style bits.
|
|
//
|
|
if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN31COMPAT))
|
|
{
|
|
if ((!TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT) &&
|
|
(((LOBYTE(GET_STYLE(pbutn))) & (LOBYTE(~BS_LEFTTEXT))) == LOBYTE(BS_USERBUTTON))) ||
|
|
(TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT) &&
|
|
(GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_USERBUTTON))))
|
|
{
|
|
//
|
|
// BS_USERBUTTON is no longer allowed for 3.1 and beyond.
|
|
// Just turn to normal push button.
|
|
//
|
|
AlterWindowStyle(hwnd, BS_TYPEMASK, 0);
|
|
TraceMsg(TF_STANDARD, "UxButton: BS_USERBUTTON no longer supported");
|
|
}
|
|
}
|
|
|
|
if ((GET_EXSTYLE(pbutn) & WS_EX_RIGHT) != 0)
|
|
{
|
|
AlterWindowStyle(hwnd, BS_RIGHT | BS_RIGHTBUTTON, BS_RIGHT | BS_RIGHTBUTTON);
|
|
}
|
|
|
|
goto CallDWP;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Failed... return FALSE.
|
|
//
|
|
// From a WM_NCCREATE msg, this will cause the
|
|
// CreateWindow call to fail.
|
|
//
|
|
TraceMsg(TF_STANDARD, "BUTTON: Unable to allocate button instance structure.");
|
|
lResult = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_UPDATEUISTATE:
|
|
|
|
DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
if (ISBSTEXTOROD(GET_STYLE(pbutn)))
|
|
{
|
|
pbutn->fPaintKbdCuesOnly = !IsUsingCleartype();
|
|
Button_Repaint(pbutn);
|
|
pbutn->fPaintKbdCuesOnly = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_GETOBJECT:
|
|
|
|
if(lParam == OBJID_QUERYCLASSNAMEIDX)
|
|
{
|
|
lResult = MSAA_CLASSNAMEIDX_BUTTON;
|
|
}
|
|
else
|
|
{
|
|
lResult = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_THEMECHANGED:
|
|
if ( pbutn->hTheme )
|
|
{
|
|
CloseThemeData(pbutn->hTheme);
|
|
}
|
|
|
|
//---- reset cached sizes that may change with a theme change ----
|
|
sizeCheckBox.cx = 0;
|
|
sizeCheckBox.cy = 0;
|
|
sizeRadioBox.cx = 0;
|
|
sizeRadioBox.cy = 0;
|
|
|
|
pbutn->hTheme = Button_GetTheme(pbutn);
|
|
|
|
InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
|
|
|
|
CCSendNotify(&pbutn->ci, NM_THEMECHANGED, NULL);
|
|
|
|
lResult = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (CCWndProc(&pbutn->ci, uMsg, wParam, lParam, &lResult))
|
|
return lResult;
|
|
|
|
CallDWP:
|
|
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
|
|
}
|
|
|
|
return lResult;
|
|
}
|