|
|
/**************************** 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); }
|