Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1955 lines
55 KiB

/**************************** Module Header ********************************\
* Module Name: btnctl.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Radio Button and Check Box Handling Routines
*
* History:
* ??-???-???? ?????? Ported from Win 3.0 sources
* 01-Feb-1991 mikeke Added Revalidation code
* 03-Jan-1992 ianja Neutralized (ANSI/wide-character)
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/* 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
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 {
LPWSTR lpsz; // Text string
PBUTN pbutn; // Button data
WORD wFlags; // Alignment flags
} BTNDATA, FAR * LPBTNDATA;
void xxxDrawButton(PBUTN pbutn, HDC hdc, UINT pbfPush);
LOOKASIDE ButtonLookaside;
/***************************************************************************\
*
* IsPushButton()
*
* Returns non-zero if the window is a push button. Returns flags that
* are interesting if it is. These flags are
*
*
*
\***************************************************************************/
UINT IsPushButton(
PWND pwnd)
{
BYTE bStyle;
UINT flags;
bStyle = TestWF(pwnd, BFTYPEMASK);
flags = 0;
switch (bStyle) {
case LOBYTE(BS_PUSHBUTTON):
flags |= PBF_PUSHABLE;
break;
case LOBYTE(BS_DEFPUSHBUTTON):
flags |= PBF_PUSHABLE | PBF_DEFAULT;
break;
default:
if (TestWF(pwnd, BFPUSHLIKE))
flags |= PBF_PUSHABLE;
break;
}
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(
PWND pwnd)
{
BYTE bHorz;
BYTE bVert;
bHorz = TestWF(pwnd, BFHORZMASK);
bVert = TestWF(pwnd, BFVERTMASK);
if (!bHorz || !bVert) {
if (IsPushButton(pwnd)) {
if (!bHorz)
bHorz = LOBYTE(BFCENTER);
} else {
if (!bHorz)
bHorz = LOBYTE(BFLEFT);
}
if (!bVert)
bVert = LOBYTE(BFVCENTER);
}
return bHorz | bVert;
}
/***************************************************************************\
*
* BNSetFont()
*
* Changes button font, and decides if we can use real bold font for default
* push buttons or if we have to simulate it.
*
\***************************************************************************/
void BNSetFont(
PBUTN pbutn,
HFONT hfn,
BOOL fRedraw)
{
PWND pwnd = pbutn->spwnd;
pbutn->hFont = hfn;
if (fRedraw && IsVisible(pwnd)) {
NtUserInvalidateRect(HWq(pwnd), NULL, TRUE);
}
}
/***************************************************************************\
* xxxBNInitDC
*
* History:
\***************************************************************************/
HBRUSH xxxBNInitDC(
PBUTN pbutn,
HDC hdc)
{
UINT wColor;
BYTE bStyle;
HBRUSH hbr;
PWND pwnd = pbutn->spwnd;
CheckLock(pwnd);
/*
* Set BkMode before getting brush so that the app can change it to
* transparent if it wants.
*/
SetBkMode(hdc, OPAQUE);
bStyle = TestWF(pwnd, BFTYPEMASK);
switch (bStyle) {
default:
if (TestWF(pwnd, WFWIN40COMPAT) && !TestWF(pwnd, BFPUSHLIKE)) {
wColor = WM_CTLCOLORSTATIC;
break;
}
case LOBYTE(BS_PUSHBUTTON):
case LOBYTE(BS_DEFPUSHBUTTON):
case LOBYTE(BS_OWNERDRAW):
case LOBYTE(BS_USERBUTTON):
wColor = WM_CTLCOLORBTN;
break;
}
hbr = GetControlBrush(HWq(pwnd), hdc, wColor);
/*
* 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)) {
IntersectClipRect(hdc, 0, 0,
pwnd->rcClient.right - pwnd->rcClient.left,
pwnd->rcClient.bottom - pwnd->rcClient.top);
}
if (TestWF(pwnd,WEFRTLREADING))
SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
return(hbr);
}
/***************************************************************************\
* xxxBNGetDC
*
* History:
\***************************************************************************/
HDC xxxBNGetDC(
PBUTN pbutn,
HBRUSH *lphbr)
{
HDC hdc;
PWND pwnd = pbutn->spwnd;
CheckLock(pwnd);
if (IsVisible(pwnd)) {
HBRUSH hbr;
hdc = NtUserGetDC(HWq(pwnd));
hbr = xxxBNInitDC(pbutn, hdc);
if (lphbr!=NULL)
*lphbr = hbr;
return hdc;
}
return NULL;
}
/***************************************************************************\
* BNReleaseDC
*
* History:
\***************************************************************************/
void BNReleaseDC(
PBUTN pbutn,
HDC hdc)
{
PWND pwnd = pbutn->spwnd;
if (TestWF(pwnd,WEFRTLREADING))
SetTextAlign(hdc, GetTextAlign(hdc) & ~TA_RTLREADING);
if (pbutn->hFont) {
SelectObject(hdc, ghFontSys);
}
ReleaseDC(HWq(pwnd), hdc);
}
/***************************************************************************\
* xxxBNOwnerDraw
*
* History:
\***************************************************************************/
void xxxBNOwnerDraw(
PBUTN pbutn,
HDC hdc,
UINT itemAction)
{
DRAWITEMSTRUCT drawItemStruct;
TL tlpwndParent;
PWND pwnd = pbutn->spwnd;
UINT itemState = 0;
if (TestWF(pwnd, WEFPUIFOCUSHIDDEN)) {
itemState |= ODS_NOFOCUSRECT;
}
if (TestWF(pwnd, WEFPUIACCELHIDDEN)) {
itemState |= ODS_NOACCEL;
}
if (BUTTONSTATE(pbutn) & BST_FOCUS) {
itemState |= ODS_FOCUS;
}
if (BUTTONSTATE(pbutn) & BST_PUSHED) {
itemState |= ODS_SELECTED;
}
if (TestWF(pwnd, WFDISABLED))
itemState |= ODS_DISABLED;
drawItemStruct.CtlType = ODT_BUTTON;
drawItemStruct.CtlID = PtrToUlong(pwnd->spmenu);
drawItemStruct.itemAction = itemAction;
drawItemStruct.itemState = itemState;
drawItemStruct.hwndItem = HWq(pwnd);
drawItemStruct.hDC = hdc;
_GetClientRect(pwnd, &drawItemStruct.rcItem);
drawItemStruct.itemData = 0L;
/*
* Send a WM_DRAWITEM message to the parent
* IanJa: in this case pMenu is being used as the control ID
*/
ThreadLock(REBASEPWND(pwnd, spwndParent), &tlpwndParent);
SendMessage(HW(REBASEPWND(pwnd, spwndParent)), WM_DRAWITEM, (WPARAM)pwnd->spmenu,
(LPARAM)&drawItemStruct);
ThreadUnlock(&tlpwndParent);
}
/***************************************************************************\
* CalcBtnRect
*
* History:
\***************************************************************************/
void BNCalcRect(
PWND pwnd,
HDC hdc,
LPRECT lprc,
int code,
UINT pbfFlags)
{
int cch;
SIZE extent;
int dy;
LPWSTR lpName;
UINT align;
_GetClientRect(pwnd, lprc);
align = GetAlignment(pwnd);
switch (code) {
case CBR_PUSHBUTTON:
// Subtract out raised edge all around
InflateRect(lprc, -SYSMET(CXEDGE), -SYSMET(CYEDGE));
if (pbfFlags & PBF_DEFAULT)
InflateRect(lprc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
break;
case CBR_CHECKBOX:
switch (align & LOBYTE(BFVERTMASK))
{
case LOBYTE(BFVCENTER):
lprc->top = (lprc->top + lprc->bottom - gpsi->oembmi[OBI_CHECK].cy) / 2;
break;
case LOBYTE(BFTOP):
case LOBYTE(BFBOTTOM):
PSMGetTextExtent(hdc, (LPWSTR)szOneChar, 1, &extent);
dy = extent.cy + extent.cy/4;
// Save vertical extent
extent.cx = dy;
// Get centered amount
dy = (dy - gpsi->oembmi[OBI_CHECK].cy) / 2;
if ((align & LOBYTE(BFVERTMASK)) == LOBYTE(BFTOP))
lprc->top += dy;
else
lprc->top = lprc->bottom - extent.cx + dy;
break;
}
if (TestWF(pwnd, BFRIGHTBUTTON))
lprc->left = lprc->right - gpsi->oembmi[OBI_CHECK].cx;
else
lprc->right = lprc->left + gpsi->oembmi[OBI_CHECK].cx;
break;
case CBR_CHECKTEXT:
if (TestWF(pwnd, BFRIGHTBUTTON)) {
lprc->right -= gpsi->oembmi[OBI_CHECK].cx;
// More spacing for 4.0 dudes
if (TestWF(pwnd, WFWIN40COMPAT)) {
PSMGetTextExtent(hdc, szOneChar, 1, &extent);
lprc->right -= extent.cx / 2;
}
} else {
lprc->left += gpsi->oembmi[OBI_CHECK].cx;
// More spacing for 4.0 dudes
if (TestWF(pwnd, WFWIN40COMPAT)) {
PSMGetTextExtent(hdc, szOneChar, 1, &extent);
lprc->left += extent.cx / 2;
}
}
break;
case CBR_GROUPTEXT:
if (!pwnd->strName.Length)
goto EmptyRect;
lpName = REBASE(pwnd, strName.Buffer);
if (!(cch = pwnd->strName.Length / sizeof(WCHAR))) {
EmptyRect:
SetRectEmpty(lprc);
break;
}
PSMGetTextExtent(hdc, lpName, cch, &extent);
extent.cx += SYSMET(CXEDGE) * 2;
switch (align & LOBYTE(BFHORZMASK))
{
// BFLEFT, nothing
case LOBYTE(BFLEFT):
lprc->left += (gpsi->cxSysFontChar - SYSMET(CXBORDER));
lprc->right = lprc->left + (int)(extent.cx);
break;
case LOBYTE(BFRIGHT):
lprc->right -= (gpsi->cxSysFontChar - SYSMET(CXBORDER));
lprc->left = lprc->right - (int)(extent.cx);
break;
case LOBYTE(BFCENTER):
lprc->left = (lprc->left + lprc->right - (int)(extent.cx)) / 2;
lprc->right = lprc->left + (int)(extent.cx);
break;
}
// Center aligned.
lprc->bottom = lprc->top + extent.cy + SYSMET(CYEDGE);
break;
case CBR_GROUPFRAME:
PSMGetTextExtent(hdc, (LPWSTR)szOneChar, 1, &extent);
lprc->top += extent.cy / 2;
break;
}
}
/***************************************************************************\
*
* BtnGetMultiExtent()
*
* Calculates button text extent, given alignment flags.
*
\***************************************************************************/
void BNMultiExtent(
WORD wFlags,
HDC hdc,
LPRECT lprcMax,
LPWSTR lpsz,
int cch,
PINT pcx,
PINT pcy)
{
RECT rcT;
UINT dtFlags = DT_CALCRECT | DT_WORDBREAK | DT_EDITCONTROL;
CopyRect(&rcT, 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(&rcT, -SYSMET(CXEDGE), -SYSMET(CYBORDER));
if ((wFlags & LOBYTE(BFHORZMASK)) == LOBYTE(BFCENTER))
dtFlags |= DT_CENTER;
if ((wFlags & LOBYTE(BFHORZMASK)) == LOBYTE(BFRIGHT))
dtFlags |= DT_RIGHT;
if ((wFlags & LOBYTE(BFVERTMASK)) == LOBYTE(BFVCENTER))
dtFlags |= DT_VCENTER;
if ((wFlags & LOBYTE(BFVERTMASK)) == LOBYTE(BFBOTTOM))
dtFlags |= DT_BOTTOM;
DrawTextExW(hdc, lpsz, cch, &rcT, dtFlags, NULL);
if (pcx)
*pcx = rcT.right-rcT.left;
if (pcy)
*pcy = rcT.bottom-rcT.top;
}
/***************************************************************************\
*
* BtnMultiDraw()
*
* Draws multiline button text
*
\***************************************************************************/
BOOL CALLBACK BNMultiDraw(
HDC hdc,
LPARAM lData,
WPARAM wData,
int cx,
int cy)
{
LPBTNDATA lpbd = (LPBTNDATA)lData;
int cch = (int)wData;
RECT rcT;
UINT dtFlags = DT_WORDBREAK | DT_EDITCONTROL;
PBUTN pbutn = lpbd->pbutn;
if (TestWF(pbutn->spwnd, WEFPUIACCELHIDDEN)) {
dtFlags |= DT_HIDEPREFIX;
} else if (pbutn->fPaintKbdCuesOnly){
dtFlags |= DT_PREFIXONLY;
}
if (TestWF(pbutn->spwnd, WEFRIGHT)) {
dtFlags |= DT_RIGHT;
}
rcT.left = 0;
rcT.top = 0;
rcT.right = cx;
rcT.bottom = cy;
// Horizontal alignment
UserAssert(DT_LEFT == 0);
switch (lpbd->wFlags & LOBYTE(BFHORZMASK)) {
case LOBYTE(BFCENTER):
dtFlags |= DT_CENTER;
break;
case LOBYTE(BFRIGHT):
dtFlags |= DT_RIGHT;
break;
}
// Vertical alignment
UserAssert(DT_TOP == 0);
switch (lpbd->wFlags & LOBYTE(BFVERTMASK)) {
case LOBYTE(BFVCENTER):
dtFlags |= DT_VCENTER;
break;
case LOBYTE(BFBOTTOM):
dtFlags |= DT_BOTTOM;
break;
}
DrawTextExW(hdc, lpbd->lpsz, cch, &rcT, dtFlags, NULL);
return(TRUE);
}
/***************************************************************************\
* xxxBNSetCapture
*
* History:
\***************************************************************************/
BOOL xxxBNSetCapture(
PBUTN pbutn,
UINT codeMouse)
{
PWND pwnd = pbutn->spwnd;
BUTTONSTATE(pbutn) |= codeMouse;
CheckLock(pwnd);
if (!(BUTTONSTATE(pbutn) & BST_CAPTURED)) {
NtUserSetCapture(HWq(pwnd));
BUTTONSTATE(pbutn) |= BST_CAPTURED;
/*
* To prevent redundant CLICK messages, we set the INCLICK bit so
* the WM_SETFOCUS code will not do a xxxButtonNotifyParent(BN_CLICKED).
*/
BUTTONSTATE(pbutn) |= BST_INCLICK;
NtUserSetFocus(HWq(pwnd));
BUTTONSTATE(pbutn) &= ~BST_INCLICK;
}
return(BUTTONSTATE(pbutn) & BST_CAPTURED);
}
/***************************************************************************\
* xxxButtonNotifyParent
*
* History:
\***************************************************************************/
void xxxButtonNotifyParent(
PWND pwnd,
UINT code)
{
TL tlpwndParent;
PWND pwndParent; // Parent if it exists
CheckLock(pwnd);
if (pwnd->spwndParent)
pwndParent = REBASEPWND(pwnd, spwndParent);
else
pwndParent = pwnd;
/*
* Note: A button's pwnd->spmenu is used to store the control ID
*/
ThreadLock(pwndParent, &tlpwndParent);
SendMessage(HW(pwndParent), WM_COMMAND,
MAKELONG(PTR_TO_ID(pwnd->spmenu), code), (LPARAM)HWq(pwnd));
ThreadUnlock(&tlpwndParent);
}
/***************************************************************************\
* xxxBNReleaseCapture
*
* History:
\***************************************************************************/
void xxxBNReleaseCapture(
PBUTN pbutn,
BOOL fCheck)
{
PWND pwndT;
UINT check;
BOOL fNotifyParent = FALSE;
TL tlpwndT;
PWND pwnd = pbutn->spwnd;
CheckLock(pwnd);
if (BUTTONSTATE(pbutn) & BST_PUSHED) {
SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE);
if (fCheck) {
switch (TestWF(pwnd, BFTYPEMASK)) {
case BS_AUTOCHECKBOX:
case BS_AUTO3STATE:
check = (UINT)((BUTTONSTATE(pbutn) & BST_CHECKMASK) + 1);
if (check > (UINT)(TestWF(pwnd, BFTYPEMASK) == BS_AUTO3STATE? BST_INDETERMINATE : BST_CHECKED)) {
check = BST_UNCHECKED;
}
SendMessageWorker(pwnd, BM_SETCHECK, check, 0, FALSE);
break;
case BS_AUTORADIOBUTTON:
pwndT = pwnd;
do {
ThreadLock(pwndT, &tlpwndT);
if ((UINT)SendMessage(HW(pwndT), WM_GETDLGCODE, 0, 0L) &
DLGC_RADIOBUTTON) {
SendMessage(HW(pwndT), BM_SETCHECK, (pwnd == pwndT), 0L);
}
pwndT = _GetNextDlgGroupItem(REBASEPWND(pwndT, spwndParent),
pwndT, FALSE);
ThreadUnlock(&tlpwndT);
} while (pwndT != pwnd);
}
fNotifyParent = TRUE;
}
}
if (BUTTONSTATE(pbutn) & BST_CAPTURED) {
BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE);
NtUserReleaseCapture();
}
if (fNotifyParent) {
/*
* We have to do the notification after setting the buttonstate bits.
*/
xxxButtonNotifyParent(pwnd, BN_CLICKED);
}
}
/***************************************************************************\
*
* DrawBtnText()
*
* Draws text of button.
*
\***************************************************************************/
void xxxBNDrawText(
PBUTN pbutn,
HDC hdc,
BOOL dbt,
BOOL fDepress)
{
RECT rc;
HBRUSH hbr;
int x;
int y;
int cx;
int cy;
LPWSTR lpName;
BYTE bStyle;
int cch;
UINT dsFlags;
BTNDATA bdt;
UINT pbfPush;
PWND pwnd = pbutn->spwnd;
bStyle = TestWF(pwnd, BFTYPEMASK);
if (bStyle > sizeof(mpStyleCbr)) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid button style");
} else if ((bStyle == LOBYTE(BS_GROUPBOX)) && (dbt == DBT_FOCUS))
return;
pbfPush = IsPushButton(pwnd);
if (pbfPush) {
BNCalcRect(pwnd, hdc, &rc, CBR_PUSHBUTTON, pbfPush);
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
//
// This is because we don'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, SYSRGB(3DFACE));
SetTextColor(hdc, SYSRGB(BTNTEXT));
hbr = SYSHBR(BTNTEXT);
} else {
BNCalcRect(pwnd, hdc, &rc, mpStyleCbr[bStyle], pbfPush);
// Skip stuff for ownerdraw buttons, since we aren't going to
// draw text/image.
if (bStyle == LOBYTE(BS_OWNERDRAW))
goto DrawFocus;
else
hbr = SYSHBR(WINDOWTEXT);
}
// Alignment
bdt.wFlags = GetAlignment(pwnd);
bdt.pbutn = pbutn;
// Bail if we have nothing to draw
if (TestWF(pwnd, BFBITMAP)) {
BITMAP bmp;
// Bitmap button
if (!pbutn->hImage)
return;
GetObject(pbutn->hImage, sizeof(BITMAP), &bmp);
cx = bmp.bmWidth;
cy = bmp.bmHeight;
dsFlags = DST_BITMAP;
goto UseImageForName;
} else if (TestWF(pwnd, BFICON)) {
// Icon button
if (!pbutn->hImage)
return;
NtUserGetIconSize(pbutn->hImage, 0, &cx, &cy);
cy /= 2; // The bitmap height is half because a Mask is present in NT
dsFlags = DST_ICON;
UseImageForName:
lpName = (LPWSTR)pbutn->hImage;
cch = TRUE;
} else {
// Text button
if (!pwnd->strName.Length)
return;
lpName = REBASE(pwnd, strName.Buffer);
cch = pwnd->strName.Length / sizeof(WCHAR);
if (TestWF(pwnd, BFMULTILINE)) {
bdt.lpsz = lpName;
BNMultiExtent(bdt.wFlags, hdc, &rc, lpName, cch, &cx, &cy);
lpName = (LPWSTR)(LPBTNDATA)&bdt;
dsFlags = DST_COMPLEX;
} else {
SIZE size;
PSMGetTextExtent(hdc, lpName, cch, &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 (TestWF(pwnd, WEFPUIACCELHIDDEN)) {
dsFlags |= DSS_HIDEPREFIX;
} else if (pbutn->fPaintKbdCuesOnly) {
dsFlags |= DSS_PREFIXONLY;
}
}
//
// 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 (bdt.wFlags & LOBYTE(BFHORZMASK)) {
//
// For left & right justified, we leave a margin of CXEDGE on either
// side for eye-pleasing space.
//
case LOBYTE(BFLEFT):
x = rc.left + SYSMET(CXEDGE);
break;
case LOBYTE(BFRIGHT):
x = rc.right - cx - SYSMET(CXEDGE);
break;
default:
x = (rc.left + rc.right - cx) / 2;
break;
}
// Vertical
switch (bdt.wFlags & LOBYTE(BFVERTMASK)) {
//
// For top & bottom justified, we leave a margin of CYBORDER on
// either side for more eye-pleasing space.
//
case LOBYTE(BFTOP):
y = rc.top + SYSMET(CYBORDER);
break;
case LOBYTE(BFBOTTOM):
y = rc.bottom - cy - SYSMET(CYBORDER);
break;
default:
y = (rc.top + rc.bottom - cy) / 2;
break;
}
//
// Draw the text
//
if (dbt & DBT_TEXT) {
//
// This isn't called for USER buttons.
//
UserAssert(bStyle != LOBYTE(BS_USERBUTTON));
if (fDepress) {
x += SYSMET(CXBORDER);
y += SYSMET(CYBORDER);
}
if (TestWF(pwnd, WFDISABLED)) {
UserAssert(HIBYTE(BFICON) == HIBYTE(BFBITMAP));
if (SYSMET(SLOWMACHINE) &&
!TestWF(pwnd, BFICON | BFBITMAP) &&
(GetBkColor(hdc) != SYSRGB(GRAYTEXT)))
{
// Perf && consistency with menus, statics
SetTextColor(hdc, SYSRGB(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 = SYSHBR(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, BNMultiDraw, (LPARAM)lpName,
(WPARAM)cch, 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.
DrawFocus:
if (dbt & 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.
xxxBNOwnerDraw(pbutn, hdc, ODA_FOCUS);
} else {
// Don't draw the focus if underlines are not turned on
if (!TestWF(pwnd, WEFPUIFOCUSHIDDEN)) {
// 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(pwnd, &rcClient);
if (bStyle == LOBYTE(BS_USERBUTTON))
CopyRect(&rc, &rcClient);
else {
// Try to leave a border all around text. That causes
// focus to hug text.
rc.top = max(rcClient.top, y-SYSMET(CYBORDER));
rc.bottom = min(rcClient.bottom, rc.top + SYSMET(CYEDGE) + cy);
rc.left = max(rcClient.left, x-SYSMET(CXBORDER));
rc.right = min(rcClient.right, rc.left + SYSMET(CXEDGE) + cx);
}
} else
InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
// Are back & fore colors set properly?
DrawFocusRect(hdc, &rc);
}
}
}
}
/***************************************************************************\
*
* DrawCheck()
*
\***************************************************************************/
void xxxButtonDrawCheck(
PBUTN pbutn,
HDC hdc,
HBRUSH hbr)
{
RECT rc;
int bm;
UINT flags;
BOOL fDoubleBlt = FALSE;
TL tlpwnd;
PWND pwnd = pbutn->spwnd;
PWND pwndParent;
BNCalcRect(pwnd, hdc, &rc, CBR_CHECKBOX, 0);
flags = 0;
if (BUTTONSTATE(pbutn) & BST_CHECKMASK)
flags |= DFCS_CHECKED;
if (BUTTONSTATE(pbutn) & BST_PUSHED)
flags |= DFCS_PUSHED;
if (TestWF(pwnd, WFDISABLED))
flags |= DFCS_INACTIVE;
bm = OBI_CHECK;
switch (TestWF(pwnd, BFTYPEMASK)) {
case BS_AUTORADIOBUTTON:
case BS_RADIOBUTTON:
fDoubleBlt = TRUE;
bm = OBI_RADIO;
flags |= DFCS_BUTTONRADIO;
break;
case BS_3STATE:
case BS_AUTO3STATE:
if ((BUTTONSTATE(pbutn) & BST_CHECKMASK) == BST_INDETERMINATE) {
bm = OBI_3STATE;
flags |= DFCS_BUTTON3STATE;
break;
}
// FALL THRU
default:
flags |= DFCS_BUTTONCHECK;
break;
}
rc.right = rc.left + gpsi->oembmi[bm].cx;
rc.bottom = rc.top + gpsi->oembmi[bm].cy;
ThreadLockAlways(pwnd->spwndParent, &tlpwnd);
pwndParent = REBASEPWND(pwnd, spwndParent);
PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc);
ThreadUnlock(&tlpwnd);
if (TestWF(pwnd, BFFLAT) && gpsi->BitCount != 1) {
flags |= DFCS_MONO | DFCS_FLAT;
DrawFrameControl(hdc, &rc, DFC_BUTTON, flags);
} else {
switch (flags & (DFCS_CHECKED | DFCS_PUSHED | DFCS_INACTIVE))
{
case 0:
break;
case DFCS_CHECKED:
bm += DOBI_CHECK;
break;
// These are mutually exclusive!
case DFCS_PUSHED:
case DFCS_INACTIVE:
bm += DOBI_DOWN; // DOBI_DOWN == DOBI_INACTIVE
break;
case DFCS_CHECKED | DFCS_PUSHED:
bm += DOBI_CHECKDOWN;
break;
case DFCS_CHECKED | DFCS_INACTIVE:
bm += DOBI_CHECKDOWN + 1;
break;
}
if (fDoubleBlt) {
// This is a diamond-shaped radio button -- Blt with a mask so that
// the exterior keeps the same color as the window's background
DWORD clrTextSave = SetTextColor(hdc, 0x00000000L);
DWORD clrBkSave = SetBkColor(hdc, 0x00FFFFFFL);
POEMBITMAPINFO pOem = gpsi->oembmi + OBI_RADIOMASK;
NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy,
pOem->x, pOem->y, SRCAND);
pOem = gpsi->oembmi + bm;
NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy,
pOem->x, pOem->y, SRCINVERT);
SetTextColor(hdc, clrTextSave);
SetBkColor(hdc, clrBkSave);
} else {
POEMBITMAPINFO pOem = gpsi->oembmi + bm;
DWORD dwROP = 0;
// We do not want to mirror the check box.
if (MIRRORED_HDC(hdc)) {
dwROP = NOMIRRORBITMAP;
}
NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy,
pOem->x, pOem->y, SRCCOPY | dwROP);
}
}
}
/***************************************************************************\
* xxxButtonDrawNewState
*
* History:
\***************************************************************************/
void xxxButtonDrawNewState(
PBUTN pbutn,
HDC hdc,
HBRUSH hbr,
UINT sOld)
{
PWND pwnd = pbutn->spwnd;
CheckLock(pwnd);
if (sOld != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED)) {
UINT pbfPush;
pbfPush = IsPushButton(pwnd);
switch (TestWF(pwnd, BFTYPEMASK)) {
case BS_GROUPBOX:
case BS_OWNERDRAW:
break;
default:
if (!pbfPush) {
xxxButtonDrawCheck(pbutn, hdc, hbr);
break;
}
case BS_PUSHBUTTON:
case BS_DEFPUSHBUTTON:
case BS_PUSHBOX:
xxxDrawButton(pbutn, hdc, pbfPush);
break;
}
}
}
/***************************************************************************\
*
* DrawButton()
*
* Draws push-like button with text
*
\***************************************************************************/
void xxxDrawButton(
PBUTN pbutn,
HDC hdc,
UINT pbfPush)
{
RECT rc;
UINT flags = 0;
UINT state = 0;
PWND pwnd = pbutn->spwnd;
if (BUTTONSTATE(pbutn) & BST_PUSHED)
state |= DFCS_PUSHED;
if (!pbutn->fPaintKbdCuesOnly) {
if (BUTTONSTATE(pbutn) & BST_CHECKMASK)
state |= DFCS_CHECKED;
if (TestWF(pwnd, WFWIN40COMPAT))
flags = BF_SOFT;
if (TestWF(pwnd, BFFLAT))
flags |= BF_FLAT | BF_MONO;
_GetClientRect(pwnd, &rc);
if (pbfPush & PBF_DEFAULT) {
DrawFrame(hdc, &rc, 1, DF_WINDOWFRAME);
InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
if (state & DFCS_PUSHED)
flags |= BF_FLAT;
}
DrawPushButton(hdc, &rc, state, flags);
}
xxxBNDrawText(pbutn, hdc, DBT_TEXT | (BUTTONSTATE(pbutn) &
BST_FOCUS ? DBT_FOCUS : 0), (state & DFCS_PUSHED));
}
/***************************************************************************\
* xxxBNPaint
*
* History:
\***************************************************************************/
void xxxBNPaint(
PBUTN pbutn,
HDC hdc)
{
UINT bsWnd;
RECT rc;
HBRUSH hbr;
HBRUSH hbrBtnSave;
TL tlpwndParent;
UINT pbfPush;
PWND pwnd = pbutn->spwnd;
PWND pwndParent;
CheckLock(pwnd);
hbr = xxxBNInitDC(pbutn, hdc);
bsWnd = TestWF(pwnd, BFTYPEMASK);
pbfPush = IsPushButton(pwnd);
if (!pbfPush && !pbutn->fPaintKbdCuesOnly) {
_GetClientRect(pwnd, &rc);
if ((bsWnd != LOBYTE(BS_OWNERDRAW)) &&
(bsWnd != LOBYTE(BS_GROUPBOX))) {
ThreadLock(pwnd->spwndParent, &tlpwndParent);
pwndParent = REBASEPWND(pwnd, spwndParent);
PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc);
ThreadUnlock(&tlpwndParent);
}
hbrBtnSave = SelectObject(hdc, hbr);
}
switch (bsWnd) {
case BS_CHECKBOX:
case BS_RADIOBUTTON:
case BS_AUTORADIOBUTTON:
case BS_3STATE:
case BS_AUTOCHECKBOX:
case BS_AUTO3STATE:
if (!pbfPush) {
xxxBNDrawText(pbutn, hdc,
DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
if (!pbutn->fPaintKbdCuesOnly) {
xxxButtonDrawCheck(pbutn, hdc, hbr);
}
break;
}
/*
* Fall through for PUSHLIKE buttons
*/
case BS_PUSHBUTTON:
case BS_DEFPUSHBUTTON:
xxxDrawButton(pbutn, hdc, pbfPush);
break;
case BS_PUSHBOX:
xxxBNDrawText(pbutn, hdc,
DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
xxxButtonDrawNewState(pbutn, hdc, hbr, 0);
break;
case BS_USERBUTTON:
xxxButtonNotifyParent(pwnd, BN_PAINT);
if (BUTTONSTATE(pbutn) & BST_PUSHED) {
xxxButtonNotifyParent(pwnd, BN_PUSHED);
}
if (TestWF(pwnd, WFDISABLED)) {
xxxButtonNotifyParent(pwnd, BN_DISABLE);
}
if (BUTTONSTATE(pbutn) & BST_FOCUS) {
xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE);
}
break;
case BS_OWNERDRAW:
xxxBNOwnerDraw(pbutn, hdc, ODA_DRAWENTIRE);
break;
case BS_GROUPBOX:
if (!pbutn->fPaintKbdCuesOnly) {
BNCalcRect(pwnd, hdc, &rc, CBR_GROUPFRAME, 0);
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT |
(TestWF(pwnd, BFFLAT) ? BF_FLAT | BF_MONO : 0));
BNCalcRect(pwnd, hdc, &rc, CBR_GROUPTEXT, 0);
ThreadLock(pwnd->spwndParent, &tlpwndParent);
pwndParent = REBASEPWND(pwnd, spwndParent);
PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc);
ThreadUnlock(&tlpwndParent);
}
/*
* FillRect(hdc, &rc, hbrBtn);
*/
xxxBNDrawText(pbutn, hdc, DBT_TEXT, FALSE);
break;
}
if (!pbfPush)
SelectObject(hdc, hbrBtnSave);
/*
* Release the font which may have been loaded by xxxButtonInitDC.
*/
if (pbutn->hFont) {
SelectObject(hdc, ghFontSys);
}
}
/***************************************************************************\
* RepaintButton
*
\***************************************************************************/
void RepaintButton (PBUTN pbutn)
{
HDC hdc = xxxBNGetDC(pbutn, NULL);
if (hdc != NULL) {
xxxBNPaint(pbutn, hdc);
BNReleaseDC(pbutn, hdc);
}
}
/***************************************************************************\
* ButtonWndProc
*
* WndProc for buttons, check boxes, etc.
*
* History:
\***************************************************************************/
LRESULT APIENTRY ButtonWndProcWorker(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
DWORD fAnsi)
{
HWND hwnd = HWq(pwnd);
UINT bsWnd;
UINT wOldState;
RECT rc;
POINT pt;
HDC hdc;
HBRUSH hbr;
PAINTSTRUCT ps;
TL tlpwndParent;
PBUTN pbutn;
PWND pwndParent;
static BOOL fInit = TRUE;
LONG lResult;
CheckLock(pwnd);
bsWnd = TestWF(pwnd, BFTYPEMASK);
VALIDATECLASSANDSIZE(pwnd, FNID_BUTTON);
INITCONTROLLOOKASIDE(&ButtonLookaside, BUTN, spwnd, 8);
/*
* Get the pbutn for the given window now since we will use it a lot in
* various handlers. This was stored using SetWindowLong(hwnd,0,pbutn) when
* we initially created the button control.
*/
pbutn = ((PBUTNWND)pwnd)->pbutn;
switch (message) {
case WM_NCHITTEST:
if (bsWnd == LOBYTE(BS_GROUPBOX)) {
return (LONG)HTTRANSPARENT;
} else {
goto CallDWP;
}
case WM_ERASEBKGND:
if (bsWnd == LOBYTE(BS_OWNERDRAW)) {
/*
* Handle erase background for owner draw buttons.
*/
_GetClientRect(pwnd, &rc);
ThreadLock(pwnd->spwndParent, &tlpwndParent);
pwndParent = REBASEPWND(pwnd, spwndParent);
PaintRect(HW(pwndParent), hwnd, (HDC)wParam, (HBRUSH)CTLCOLOR_BTN, &rc);
ThreadUnlock(&tlpwndParent);
}
/*
* Do nothing for other buttons, but don't let DefWndProc() do it
* either. It will be erased in xxxBNPaint().
*/
return (LONG)TRUE;
case WM_PRINTCLIENT:
xxxBNPaint(pbutn, (HDC)wParam);
break;
case WM_PAINT:
/*
* If wParam != NULL, then this is a subclassed paint.
*/
if ((hdc = (HDC)wParam) == NULL)
hdc = NtUserBeginPaint(hwnd, &ps);
if (IsVisible(pwnd))
xxxBNPaint(pbutn, hdc);
if (!wParam)
NtUserEndPaint(hwnd, &ps);
break;
case WM_SETFOCUS:
BUTTONSTATE(pbutn) |= BST_FOCUS;
if ((hdc = xxxBNGetDC(pbutn, NULL)) != NULL) {
xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE);
BNReleaseDC(pbutn, hdc);
}
if (TestWF(pwnd, BFNOTIFY))
xxxButtonNotifyParent(pwnd, BN_SETFOCUS);
if (!(BUTTONSTATE(pbutn) & BST_INCLICK)) {
switch (bsWnd) {
case LOBYTE(BS_RADIOBUTTON):
case LOBYTE(BS_AUTORADIOBUTTON):
if (!(BUTTONSTATE(pbutn) & BST_DONTCLICK)) {
if (!(BUTTONSTATE(pbutn) & BST_CHECKMASK)) {
xxxButtonNotifyParent(pwnd, BN_CLICKED);
}
}
break;
}
}
break;
case WM_GETDLGCODE:
switch (bsWnd) {
case LOBYTE(BS_DEFPUSHBUTTON):
wParam = DLGC_DEFPUSHBUTTON;
break;
case LOBYTE(BS_PUSHBUTTON):
case LOBYTE(BS_PUSHBOX):
wParam = DLGC_UNDEFPUSHBUTTON;
break;
case LOBYTE(BS_AUTORADIOBUTTON):
case LOBYTE(BS_RADIOBUTTON):
wParam = DLGC_RADIOBUTTON;
break;
case LOBYTE(BS_GROUPBOX):
return (LONG)DLGC_STATIC;
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('-'):
wParam = DLGC_WANTCHARS;
break;
default:
wParam = 0;
}
} else {
wParam = 0;
}
break;
default:
wParam = 0;
}
return (LONG)(wParam | DLGC_BUTTON);
case WM_CAPTURECHANGED:
if (BUTTONSTATE(pbutn) & BST_CAPTURED) {
// Unwittingly, we've been kicked out of capture,
// so undepress etc.
if (BUTTONSTATE(pbutn) & BST_MOUSE)
SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE);
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.
*/
SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE);
}
xxxBNReleaseCapture(pbutn, TRUE);
BUTTONSTATE(pbutn) &= ~BST_FOCUS;
if ((hdc = xxxBNGetDC(pbutn, NULL)) != NULL) {
xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE);
BNReleaseDC(pbutn, hdc);
}
if (TestWF(pwnd, BFNOTIFY))
xxxButtonNotifyParent(pwnd, 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.
*/
NtUserInvalidateRect(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 (bsWnd) {
default:
if (!TestWF(pwnd, BFNOTIFY))
goto btnclick;
case LOBYTE(BS_USERBUTTON):
case LOBYTE(BS_RADIOBUTTON):
case LOBYTE(BS_OWNERDRAW):
xxxButtonNotifyParent(pwnd, BN_DOUBLECLICKED);
break;
}
break;
case WM_LBUTTONUP:
if (BUTTONSTATE(pbutn) & BST_MOUSE) {
xxxBNReleaseCapture(pbutn, TRUE);
}
break;
case WM_MOUSEMOVE:
if (!(BUTTONSTATE(pbutn) & BST_MOUSE)) {
break;
}
/*
*** FALL THRU **
*/
case WM_LBUTTONDOWN:
btnclick:
if (xxxBNSetCapture(pbutn, BST_MOUSE)) {
_GetClientRect(pwnd, &rc);
POINTSTOPOINT(pt, lParam);
SendMessageWorker(pwnd, BM_SETSTATE, PtInRect(&rc, pt), 0, FALSE);
}
break;
case WM_CHAR:
if (BUTTONSTATE(pbutn) & BST_MOUSE)
goto CallDWP;
if (bsWnd != LOBYTE(BS_CHECKBOX) &&
bsWnd != LOBYTE(BS_AUTOCHECKBOX))
goto CallDWP;
switch (wParam) {
case TEXT('+'):
case TEXT('='):
wParam = 1; // we must Set the check mark on.
goto SetCheck;
case TEXT('-'):
wParam = 0; // Set the check mark off.
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 (bsWnd == LOBYTE(BS_AUTOCHECKBOX))
{
if (xxxBNSetCapture(pbutn, 0))
{
SendMessageWorker(pwnd, BM_SETCHECK, wParam, 0, FALSE);
xxxBNReleaseCapture(pbutn, TRUE);
}
}
xxxButtonNotifyParent(pwnd, BN_CLICKED);
}
break;
default:
goto CallDWP;
}
break;
case BM_CLICK:
// Don't recurse into this code!
if (BUTTONSTATE(pbutn) & BST_INBMCLICK)
break;
BUTTONSTATE(pbutn) |= BST_INBMCLICK;
SendMessageWorker(pwnd, WM_LBUTTONDOWN, 0, 0, FALSE);
SendMessageWorker(pwnd, WM_LBUTTONUP, 0, 0, FALSE);
BUTTONSTATE(pbutn) &= ~BST_INBMCLICK;
/*
*** FALL THRU **
*/
case WM_KEYDOWN:
if (BUTTONSTATE(pbutn) & BST_MOUSE)
break;
if (wParam == VK_SPACE) {
if (xxxBNSetCapture(pbutn, 0)) {
SendMessageWorker(pwnd, BM_SETSTATE, TRUE, 0, FALSE);
}
} else {
xxxBNReleaseCapture(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: pwnd is history after this call!
*/
xxxBNReleaseCapture(pbutn, (wParam == VK_SPACE));
if (message == WM_SYSKEYUP) {
goto CallDWP;
}
break;
case BM_GETSTATE:
return (LONG)BUTTONSTATE(pbutn);
case BM_SETSTATE:
wOldState = (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED);
if (wParam) {
BUTTONSTATE(pbutn) |= BST_PUSHED;
} else {
BUTTONSTATE(pbutn) &= ~BST_PUSHED;
}
if ((hdc = xxxBNGetDC(pbutn, &hbr)) != NULL) {
if (bsWnd == LOBYTE(BS_USERBUTTON)) {
xxxButtonNotifyParent(pwnd, (UINT)(wParam ? BN_PUSHED : BN_UNPUSHED));
} else if (bsWnd == LOBYTE(BS_OWNERDRAW)) {
if (wOldState != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED)) {
/*
* Only notify for drawing if state has changed..
*/
xxxBNOwnerDraw(pbutn, hdc, ODA_SELECT);
}
} else {
xxxButtonDrawNewState(pbutn, hdc, hbr, wOldState);
}
BNReleaseDC(pbutn, hdc);
}
if (wOldState != (BOOL)(BUTTONSTATE(pbutn) & BST_PUSHED)) {
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
}
break;
case BM_GETCHECK:
return (LONG)(BUTTONSTATE(pbutn) & BST_CHECKMASK);
case BM_SETCHECK:
switch (bsWnd) {
case LOBYTE(BS_RADIOBUTTON):
case LOBYTE(BS_AUTORADIOBUTTON):
if (wParam) {
SetWindowState(pwnd, WFTABSTOP);
} else {
ClearWindowState(pwnd, WFTABSTOP);
}
/*
*** 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 (!IsVisible(pwnd))
break;
if ((hdc = xxxBNGetDC(pbutn, &hbr)) != NULL) {
if (TestWF(pwnd, BFPUSHLIKE)) {
xxxDrawButton(pbutn, hdc, PBF_PUSHABLE);
} else {
xxxButtonDrawCheck(pbutn, hdc, hbr);
}
BNReleaseDC(pbutn, hdc);
}
NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
}
break;
}
break;
case BM_SETSTYLE:
NtUserAlterWindowStyle(hwnd, BS_TYPEMASK, (DWORD)wParam);
if (lParam) {
NtUserInvalidateRect(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 (bsWnd == LOBYTE(BS_GROUPBOX)) {
hdc = xxxBNGetDC(pbutn, &hbr);
if (hdc != NULL) {
BNCalcRect(pwnd, hdc, &rc, CBR_GROUPTEXT, 0);
NtUserInvalidateRect(hwnd, &rc, TRUE);
pwndParent = REBASEPWND(pwnd, spwndParent);
ThreadLock(pwnd->spwndParent, &tlpwndParent);
PaintRect(HW(pwndParent), hwnd, hdc, hbr, &rc);
ThreadUnlock(&tlpwndParent);
BNReleaseDC(pbutn, hdc);
}
}
lResult = _DefSetText(hwnd, (LPWSTR)lParam, (BOOL)fAnsi);
NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_WINDOW, INDEXID_CONTAINER);
goto DoEnable;
/*
*** FALL THRU **
*/
case WM_ENABLE:
lResult = 0L;
DoEnable:
RepaintButton(pbutn);
return lResult;
case WM_SETFONT:
/*
* wParam - handle to the font
* lParam - if true, redraw else don't
*/
BNSetFont(pbutn, (HFONT)wParam, (BOOL)(lParam != 0));
break;
case WM_GETFONT:
return (LRESULT)pbutn->hFont;
case BM_GETIMAGE:
case BM_SETIMAGE:
if (!IsValidImage(wParam, TestWF(pwnd, BFIMAGEMASK), IMAGE_BMMAX)) {
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid button image type");
} else {
HANDLE hOld = pbutn->hImage;
if (message == BM_SETIMAGE) {
pbutn->hImage = (HANDLE)lParam;
if (TestWF(pwnd, WFVISIBLE)) {
NtUserInvalidateRect(hwnd, NULL, TRUE);
}
}
return (LRESULT)hOld;
}
break;
case WM_NCDESTROY:
case WM_FINALDESTROY:
if (pbutn) {
Unlock(&pbutn->spwnd);
FreeLookasideEntry(&ButtonLookaside, pbutn);
}
NtUserSetWindowFNID(hwnd, FNID_CLEANEDUP_BIT);
break;
case WM_NCCREATE:
// 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 (TestWF(pwnd, WFWIN31COMPAT)) {
if(((!TestWF(pwnd, WFWIN40COMPAT)) &&
(((LOBYTE(pwnd->style)) & (LOBYTE(~BS_LEFTTEXT))) == LOBYTE(BS_USERBUTTON))) ||
(TestWF(pwnd, WFWIN40COMPAT) &&
(bsWnd == LOBYTE(BS_USERBUTTON))))
{
// BS_USERBUTTON is no longer allowed for 3.1 and beyond.
// Just turn to normal push button.
NtUserAlterWindowStyle(hwnd, BS_TYPEMASK, 0);
RIPMSG0(RIP_WARNING, "BS_USERBUTTON no longer supported");
}
}
if (TestWF(pwnd,WEFRIGHT)) {
NtUserAlterWindowStyle(hwnd, BS_RIGHT | BS_RIGHTBUTTON, BS_RIGHT | BS_RIGHTBUTTON);
}
goto CallDWP;
case WM_INPUTLANGCHANGEREQUEST:
//
// #115190
// If the window is one of controls on top of dialogbox,
// let the parent dialog handle it.
//
if (TestwndChild(pwnd) && pwnd->spwndParent) {
PWND pwndParent = REBASEPWND(pwnd, spwndParent);
if (pwndParent) {
PCLS pclsParent = REBASEALWAYS(pwndParent, pcls);
UserAssert(pclsParent != NULL);
if (pclsParent->atomClassName == gpsi->atomSysClass[ICLS_DIALOG]) {
RIPMSG0(RIP_VERBOSE, "Button: WM_INPUTLANGCHANGEREQUEST is sent to parent.\n");
return SendMessageWorker(pwndParent, message, wParam, lParam, FALSE);
}
}
}
goto CallDWP;
case WM_UPDATEUISTATE:
{
DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
if (ISBSTEXTOROD(pwnd)) {
pbutn->fPaintKbdCuesOnly = TRUE;
RepaintButton(pbutn);
pbutn->fPaintKbdCuesOnly = FALSE;
}
}
break;
default:
CallDWP:
return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
}
return 0L;
}
/***************************************************************************\
\***************************************************************************/
LRESULT WINAPI ButtonWndProcA(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
return (0L);
}
/*
* If the control is not interested in this message,
* pass it to DefWindowProc.
*/
if (!FWINDOWMSG(message, FNID_BUTTON))
return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE);
return ButtonWndProcWorker(pwnd, message, wParam, lParam, TRUE);
}
LRESULT WINAPI ButtonWndProcW(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
return (0L);
}
/*
* If the control is not interested in this message,
* pass it to DefWindowProc.
*/
if (!FWINDOWMSG(message, FNID_BUTTON))
return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE);
return ButtonWndProcWorker(pwnd, message, wParam, lParam, FALSE);
}