Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

btnctl.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: btnctl.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * Radio Button and Check Box Handling Routines 00007 * 00008 * History: 00009 * ??-???-???? ?????? Ported from Win 3.0 sources 00010 * 01-Feb-1991 mikeke Added Revalidation code 00011 * 03-Jan-1992 ianja Neutralized (ANSI/wide-character) 00012 \***************************************************************************/ 00013 00014 #include "precomp.h" 00015 #pragma hdrstop 00016 00017 /* ButtonCalcRect codes */ 00018 #define CBR_CLIENTRECT 0 00019 #define CBR_CHECKBOX 1 00020 #define CBR_CHECKTEXT 2 00021 #define CBR_GROUPTEXT 3 00022 #define CBR_GROUPFRAME 4 00023 #define CBR_PUSHBUTTON 5 00024 00025 CONST BYTE mpStyleCbr[] = { 00026 CBR_PUSHBUTTON, /* BS_PUSHBUTTON */ 00027 CBR_PUSHBUTTON, /* BS_DEFPUSHBUTTON */ 00028 CBR_CHECKTEXT, /* BS_CHECKBOX */ 00029 CBR_CHECKTEXT, /* BS_AUTOCHECKBOX */ 00030 CBR_CHECKTEXT, /* BS_RADIOBUTTON */ 00031 CBR_CHECKTEXT, /* BS_3STATE */ 00032 CBR_CHECKTEXT, /* BS_AUTO3STATE */ 00033 CBR_GROUPTEXT, /* BS_GROUPBOX */ 00034 CBR_CLIENTRECT, /* BS_USERBUTTON */ 00035 CBR_CHECKTEXT, /* BS_AUTORADIOBUTTON */ 00036 CBR_CLIENTRECT, /* BS_PUSHBOX */ 00037 CBR_CLIENTRECT, /* BS_OWNERDRAW */ 00038 }; 00039 00040 #define IMAGE_BMMAX IMAGE_CURSOR+1 00041 static CONST BYTE rgbType[IMAGE_BMMAX] = { 00042 BS_BITMAP, // IMAGE_BITMAP 00043 BS_ICON, // IMAGE_CURSOR 00044 BS_ICON // IMAGE_ICON 00045 }; 00046 00047 #define IsValidImage(imageType, realType, max) \ 00048 ((imageType < max) && (rgbType[imageType] == realType)) 00049 00050 typedef struct tagBTNDATA { 00051 LPWSTR lpsz; // Text string 00052 PBUTN pbutn; // Button data 00053 WORD wFlags; // Alignment flags 00054 } BTNDATA, FAR * LPBTNDATA; 00055 00056 void xxxDrawButton(PBUTN pbutn, HDC hdc, UINT pbfPush); 00057 00058 LOOKASIDE ButtonLookaside; 00059 00060 /***************************************************************************\ 00061 * 00062 * IsPushButton() 00063 * 00064 * Returns non-zero if the window is a push button. Returns flags that 00065 * are interesting if it is. These flags are 00066 * 00067 * 00068 * 00069 \***************************************************************************/ 00070 00071 UINT IsPushButton( 00072 PWND pwnd) 00073 { 00074 BYTE bStyle; 00075 UINT flags; 00076 00077 bStyle = TestWF(pwnd, BFTYPEMASK); 00078 00079 flags = 0; 00080 00081 switch (bStyle) { 00082 case LOBYTE(BS_PUSHBUTTON): 00083 flags |= PBF_PUSHABLE; 00084 break; 00085 00086 case LOBYTE(BS_DEFPUSHBUTTON): 00087 flags |= PBF_PUSHABLE | PBF_DEFAULT; 00088 break; 00089 00090 default: 00091 if (TestWF(pwnd, BFPUSHLIKE)) 00092 flags |= PBF_PUSHABLE; 00093 break; 00094 } 00095 00096 return(flags); 00097 } 00098 00099 /***************************************************************************\ 00100 * 00101 * GetAlignment() 00102 * 00103 * Gets default alignment of button. If BS_HORZMASK and/or BS_VERTMASK 00104 * is specified, uses those. Otherwise, uses default for button. 00105 * 00106 * It's probably a fine time to describe what alignment flags mean for 00107 * each type of button. Note that the presence of a bitmap/icon affects 00108 * the meaning of alignments. 00109 * 00110 * (1) Push like buttons 00111 * With one of {bitmap, icon, text}: 00112 * Just like you'd expect 00113 * With one of {bitmap, icon} AND text: 00114 * Image & text are centered as a unit; alignment means where 00115 * the image shows up. E.G., left-aligned means the image 00116 * on the left, text on the right. 00117 * (2) Radio/check like buttons 00118 * Left aligned means check/radio box is on left, then bitmap/icon 00119 * and text follows, left justified. 00120 * Right aligned means checkk/radio box is on right, preceded by 00121 * text and bitmap/icon, right justified. 00122 * Centered has no meaning. 00123 * With one of {bitmap, icon} AND text: 00124 * Top aligned means bitmap/icon above, text below 00125 * Bottom aligned means text above, bitmap/icon below 00126 * With one of {bitmap, icon, text} 00127 * Alignments mean what you'd expect. 00128 * (3) Group boxes 00129 * Left aligned means text is left justified on left side 00130 * Right aligned means text is right justified on right side 00131 * Center aligned means text is in middle 00132 * 00133 * 00134 \***************************************************************************/ 00135 00136 WORD GetAlignment( 00137 PWND pwnd) 00138 { 00139 BYTE bHorz; 00140 BYTE bVert; 00141 00142 bHorz = TestWF(pwnd, BFHORZMASK); 00143 bVert = TestWF(pwnd, BFVERTMASK); 00144 00145 if (!bHorz || !bVert) { 00146 if (IsPushButton(pwnd)) { 00147 if (!bHorz) 00148 bHorz = LOBYTE(BFCENTER); 00149 } else { 00150 if (!bHorz) 00151 bHorz = LOBYTE(BFLEFT); 00152 } 00153 00154 if (!bVert) 00155 bVert = LOBYTE(BFVCENTER); 00156 } 00157 00158 return bHorz | bVert; 00159 } 00160 00161 00162 /***************************************************************************\ 00163 * 00164 * BNSetFont() 00165 * 00166 * Changes button font, and decides if we can use real bold font for default 00167 * push buttons or if we have to simulate it. 00168 * 00169 \***************************************************************************/ 00170 00171 void BNSetFont( 00172 PBUTN pbutn, 00173 HFONT hfn, 00174 BOOL fRedraw) 00175 { 00176 PWND pwnd = pbutn->spwnd; 00177 00178 pbutn->hFont = hfn; 00179 00180 if (fRedraw && IsVisible(pwnd)) { 00181 NtUserInvalidateRect(HWq(pwnd), NULL, TRUE); 00182 } 00183 00184 } 00185 00186 00187 /***************************************************************************\ 00188 * xxxBNInitDC 00189 * 00190 * History: 00191 \***************************************************************************/ 00192 00193 HBRUSH xxxBNInitDC( 00194 PBUTN pbutn, 00195 HDC hdc) 00196 { 00197 UINT wColor; 00198 BYTE bStyle; 00199 HBRUSH hbr; 00200 PWND pwnd = pbutn->spwnd; 00201 00202 CheckLock(pwnd); 00203 00204 /* 00205 * Set BkMode before getting brush so that the app can change it to 00206 * transparent if it wants. 00207 */ 00208 SetBkMode(hdc, OPAQUE); 00209 00210 bStyle = TestWF(pwnd, BFTYPEMASK); 00211 00212 switch (bStyle) { 00213 default: 00214 if (TestWF(pwnd, WFWIN40COMPAT) && !TestWF(pwnd, BFPUSHLIKE)) { 00215 wColor = WM_CTLCOLORSTATIC; 00216 break; 00217 } 00218 00219 case LOBYTE(BS_PUSHBUTTON): 00220 case LOBYTE(BS_DEFPUSHBUTTON): 00221 case LOBYTE(BS_OWNERDRAW): 00222 case LOBYTE(BS_USERBUTTON): 00223 wColor = WM_CTLCOLORBTN; 00224 break; 00225 } 00226 00227 hbr = GetControlBrush(HWq(pwnd), hdc, wColor); 00228 00229 /* 00230 * Select in the user's font if set, and save the old font so that we can 00231 * restore it when we release the dc. 00232 */ 00233 if (pbutn->hFont) { 00234 SelectObject(hdc, pbutn->hFont); 00235 } 00236 00237 /* 00238 * Clip output to the window rect if needed. 00239 */ 00240 if (bStyle != LOBYTE(BS_GROUPBOX)) { 00241 IntersectClipRect(hdc, 0, 0, 00242 pwnd->rcClient.right - pwnd->rcClient.left, 00243 pwnd->rcClient.bottom - pwnd->rcClient.top); 00244 } 00245 00246 if (TestWF(pwnd,WEFRTLREADING)) 00247 SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc)); 00248 00249 return(hbr); 00250 } 00251 00252 /***************************************************************************\ 00253 * xxxBNGetDC 00254 * 00255 * History: 00256 \***************************************************************************/ 00257 00258 HDC xxxBNGetDC( 00259 PBUTN pbutn, 00260 HBRUSH *lphbr) 00261 { 00262 HDC hdc; 00263 PWND pwnd = pbutn->spwnd; 00264 00265 CheckLock(pwnd); 00266 00267 if (IsVisible(pwnd)) { 00268 HBRUSH hbr; 00269 00270 hdc = NtUserGetDC(HWq(pwnd)); 00271 hbr = xxxBNInitDC(pbutn, hdc); 00272 00273 if (lphbr!=NULL) 00274 *lphbr = hbr; 00275 00276 return hdc; 00277 } 00278 00279 return NULL; 00280 } 00281 00282 /***************************************************************************\ 00283 * BNReleaseDC 00284 * 00285 * History: 00286 \***************************************************************************/ 00287 00288 void BNReleaseDC( 00289 PBUTN pbutn, 00290 HDC hdc) 00291 { 00292 PWND pwnd = pbutn->spwnd; 00293 00294 if (TestWF(pwnd,WEFRTLREADING)) 00295 SetTextAlign(hdc, GetTextAlign(hdc) & ~TA_RTLREADING); 00296 00297 if (pbutn->hFont) { 00298 SelectObject(hdc, ghFontSys); 00299 } 00300 00301 ReleaseDC(HWq(pwnd), hdc); 00302 } 00303 00304 /***************************************************************************\ 00305 * xxxBNOwnerDraw 00306 * 00307 * History: 00308 \***************************************************************************/ 00309 00310 void xxxBNOwnerDraw( 00311 PBUTN pbutn, 00312 HDC hdc, 00313 UINT itemAction) 00314 { 00315 DRAWITEMSTRUCT drawItemStruct; 00316 TL tlpwndParent; 00317 PWND pwnd = pbutn->spwnd; 00318 UINT itemState = 0; 00319 00320 if (TestWF(pwnd, WEFPUIFOCUSHIDDEN)) { 00321 itemState |= ODS_NOFOCUSRECT; 00322 } 00323 if (TestWF(pwnd, WEFPUIACCELHIDDEN)) { 00324 itemState |= ODS_NOACCEL; 00325 } 00326 if (BUTTONSTATE(pbutn) & BST_FOCUS) { 00327 itemState |= ODS_FOCUS; 00328 } 00329 if (BUTTONSTATE(pbutn) & BST_PUSHED) { 00330 itemState |= ODS_SELECTED; 00331 } 00332 00333 if (TestWF(pwnd, WFDISABLED)) 00334 itemState |= ODS_DISABLED; 00335 00336 drawItemStruct.CtlType = ODT_BUTTON; 00337 drawItemStruct.CtlID = PtrToUlong(pwnd->spmenu); 00338 drawItemStruct.itemAction = itemAction; 00339 drawItemStruct.itemState = itemState; 00340 drawItemStruct.hwndItem = HWq(pwnd); 00341 drawItemStruct.hDC = hdc; 00342 _GetClientRect(pwnd, &drawItemStruct.rcItem); 00343 drawItemStruct.itemData = 0L; 00344 00345 /* 00346 * Send a WM_DRAWITEM message to the parent 00347 * IanJa: in this case pMenu is being used as the control ID 00348 */ 00349 ThreadLock(REBASEPWND(pwnd, spwndParent), &tlpwndParent); 00350 SendMessage(HW(REBASEPWND(pwnd, spwndParent)), WM_DRAWITEM, (WPARAM)pwnd->spmenu, 00351 (LPARAM)&drawItemStruct); 00352 ThreadUnlock(&tlpwndParent); 00353 } 00354 00355 /***************************************************************************\ 00356 * CalcBtnRect 00357 * 00358 * History: 00359 \***************************************************************************/ 00360 00361 void BNCalcRect( 00362 PWND pwnd, 00363 HDC hdc, 00364 LPRECT lprc, 00365 int code, 00366 UINT pbfFlags) 00367 { 00368 int cch; 00369 SIZE extent; 00370 int dy; 00371 LPWSTR lpName; 00372 UINT align; 00373 00374 _GetClientRect(pwnd, lprc); 00375 00376 align = GetAlignment(pwnd); 00377 00378 switch (code) { 00379 case CBR_PUSHBUTTON: 00380 // Subtract out raised edge all around 00381 InflateRect(lprc, -SYSMET(CXEDGE), -SYSMET(CYEDGE)); 00382 00383 if (pbfFlags & PBF_DEFAULT) 00384 InflateRect(lprc, -SYSMET(CXBORDER), -SYSMET(CYBORDER)); 00385 break; 00386 00387 case CBR_CHECKBOX: 00388 switch (align & LOBYTE(BFVERTMASK)) 00389 { 00390 case LOBYTE(BFVCENTER): 00391 lprc->top = (lprc->top + lprc->bottom - gpsi->oembmi[OBI_CHECK].cy) / 2; 00392 break; 00393 00394 case LOBYTE(BFTOP): 00395 case LOBYTE(BFBOTTOM): 00396 PSMGetTextExtent(hdc, (LPWSTR)szOneChar, 1, &extent); 00397 dy = extent.cy + extent.cy/4; 00398 00399 // Save vertical extent 00400 extent.cx = dy; 00401 00402 // Get centered amount 00403 00404 dy = (dy - gpsi->oembmi[OBI_CHECK].cy) / 2; 00405 if ((align & LOBYTE(BFVERTMASK)) == LOBYTE(BFTOP)) 00406 lprc->top += dy; 00407 else 00408 lprc->top = lprc->bottom - extent.cx + dy; 00409 break; 00410 } 00411 00412 if (TestWF(pwnd, BFRIGHTBUTTON)) 00413 lprc->left = lprc->right - gpsi->oembmi[OBI_CHECK].cx; 00414 else 00415 lprc->right = lprc->left + gpsi->oembmi[OBI_CHECK].cx; 00416 00417 break; 00418 00419 case CBR_CHECKTEXT: 00420 if (TestWF(pwnd, BFRIGHTBUTTON)) { 00421 lprc->right -= gpsi->oembmi[OBI_CHECK].cx; 00422 00423 // More spacing for 4.0 dudes 00424 if (TestWF(pwnd, WFWIN40COMPAT)) { 00425 PSMGetTextExtent(hdc, szOneChar, 1, &extent); 00426 lprc->right -= extent.cx / 2; 00427 } 00428 } else { 00429 lprc->left += gpsi->oembmi[OBI_CHECK].cx; 00430 00431 // More spacing for 4.0 dudes 00432 if (TestWF(pwnd, WFWIN40COMPAT)) { 00433 PSMGetTextExtent(hdc, szOneChar, 1, &extent); 00434 lprc->left += extent.cx / 2; 00435 } 00436 } 00437 break; 00438 00439 case CBR_GROUPTEXT: 00440 if (!pwnd->strName.Length) 00441 goto EmptyRect; 00442 00443 lpName = REBASE(pwnd, strName.Buffer); 00444 if (!(cch = pwnd->strName.Length / sizeof(WCHAR))) { 00445 EmptyRect: 00446 SetRectEmpty(lprc); 00447 break; 00448 } 00449 00450 PSMGetTextExtent(hdc, lpName, cch, &extent); 00451 extent.cx += SYSMET(CXEDGE) * 2; 00452 00453 switch (align & LOBYTE(BFHORZMASK)) 00454 { 00455 // BFLEFT, nothing 00456 case LOBYTE(BFLEFT): 00457 lprc->left += (gpsi->cxSysFontChar - SYSMET(CXBORDER)); 00458 lprc->right = lprc->left + (int)(extent.cx); 00459 break; 00460 00461 case LOBYTE(BFRIGHT): 00462 lprc->right -= (gpsi->cxSysFontChar - SYSMET(CXBORDER)); 00463 lprc->left = lprc->right - (int)(extent.cx); 00464 break; 00465 00466 case LOBYTE(BFCENTER): 00467 lprc->left = (lprc->left + lprc->right - (int)(extent.cx)) / 2; 00468 lprc->right = lprc->left + (int)(extent.cx); 00469 break; 00470 } 00471 00472 // Center aligned. 00473 lprc->bottom = lprc->top + extent.cy + SYSMET(CYEDGE); 00474 break; 00475 00476 case CBR_GROUPFRAME: 00477 PSMGetTextExtent(hdc, (LPWSTR)szOneChar, 1, &extent); 00478 lprc->top += extent.cy / 2; 00479 break; 00480 } 00481 } 00482 00483 /***************************************************************************\ 00484 * 00485 * BtnGetMultiExtent() 00486 * 00487 * Calculates button text extent, given alignment flags. 00488 * 00489 \***************************************************************************/ 00490 00491 void BNMultiExtent( 00492 WORD wFlags, 00493 HDC hdc, 00494 LPRECT lprcMax, 00495 LPWSTR lpsz, 00496 int cch, 00497 PINT pcx, 00498 PINT pcy) 00499 { 00500 RECT rcT; 00501 00502 UINT dtFlags = DT_CALCRECT | DT_WORDBREAK | DT_EDITCONTROL; 00503 CopyRect(&rcT, lprcMax); 00504 00505 // Note that since we're just calculating the maximum dimensions, 00506 // left-justification and top-justification are not important. 00507 // Also, remember to leave margins horz and vert that follow our rules 00508 // in DrawBtnText(). 00509 00510 InflateRect(&rcT, -SYSMET(CXEDGE), -SYSMET(CYBORDER)); 00511 00512 if ((wFlags & LOWORD(BS_HORZMASK)) == LOWORD(BS_CENTER)) 00513 dtFlags |= DT_CENTER; 00514 00515 if ((wFlags & LOWORD(BS_VERTMASK)) == LOWORD(BS_VCENTER)) 00516 dtFlags |= DT_VCENTER; 00517 00518 DrawTextExW(hdc, lpsz, cch, &rcT, dtFlags, NULL); 00519 00520 if (pcx) 00521 *pcx = rcT.right-rcT.left; 00522 if (pcy) 00523 *pcy = rcT.bottom-rcT.top; 00524 } 00525 00526 /***************************************************************************\ 00527 * 00528 * BtnMultiDraw() 00529 * 00530 * Draws multiline button text 00531 * 00532 \***************************************************************************/ 00533 00534 BOOL BNMultiDraw( 00535 HDC hdc, 00536 LPBTNDATA lpbd, 00537 int cch, 00538 int cx, 00539 int cy) 00540 { 00541 RECT rcT; 00542 UINT dtFlags = DT_WORDBREAK | DT_EDITCONTROL; 00543 PBUTN pbutn = lpbd->pbutn; 00544 00545 if (TestWF(pbutn->spwnd, WEFPUIACCELHIDDEN)) { 00546 dtFlags |= DT_HIDEPREFIX; 00547 } else if (pbutn->fPaintKbdCuesOnly){ 00548 dtFlags |= DT_PREFIXONLY; 00549 } 00550 00551 rcT.left = 0; 00552 rcT.top = 0; 00553 rcT.right = cx; 00554 rcT.bottom = cy; 00555 00556 // Horizontal alignment 00557 UserAssert(DT_LEFT == 0); 00558 switch (lpbd->wFlags & LOWORD(BS_HORZMASK)) { 00559 case LOWORD(BS_CENTER): 00560 dtFlags |= DT_CENTER; 00561 break; 00562 00563 case LOWORD(BS_RIGHT): 00564 dtFlags |= DT_RIGHT; 00565 break; 00566 } 00567 00568 // Vertical alignment 00569 UserAssert(DT_TOP == 0); 00570 switch (lpbd->wFlags & LOWORD(BS_VERTMASK)) { 00571 case LOWORD(BS_VCENTER): 00572 dtFlags |= DT_VCENTER; 00573 break; 00574 00575 case LOWORD(BS_BOTTOM): 00576 dtFlags |= DT_BOTTOM; 00577 break; 00578 } 00579 00580 DrawTextExW(hdc, lpbd->lpsz, cch, &rcT, dtFlags, NULL); 00581 return(TRUE); 00582 } 00583 00584 /***************************************************************************\ 00585 * xxxBNSetCapture 00586 * 00587 * History: 00588 \***************************************************************************/ 00589 00590 BOOL xxxBNSetCapture( 00591 PBUTN pbutn, 00592 UINT codeMouse) 00593 { 00594 PWND pwnd = pbutn->spwnd; 00595 00596 BUTTONSTATE(pbutn) |= codeMouse; 00597 00598 CheckLock(pwnd); 00599 00600 if (!(BUTTONSTATE(pbutn) & BST_CAPTURED)) { 00601 NtUserSetCapture(HWq(pwnd)); 00602 BUTTONSTATE(pbutn) |= BST_CAPTURED; 00603 00604 /* 00605 * To prevent redundant CLICK messages, we set the INCLICK bit so 00606 * the WM_SETFOCUS code will not do a xxxButtonNotifyParent(BN_CLICKED). 00607 */ 00608 00609 BUTTONSTATE(pbutn) |= BST_INCLICK; 00610 00611 NtUserSetFocus(HWq(pwnd)); 00612 00613 BUTTONSTATE(pbutn) &= ~BST_INCLICK; 00614 } 00615 return(BUTTONSTATE(pbutn) & BST_CAPTURED); 00616 } 00617 00618 00619 /***************************************************************************\ 00620 * xxxButtonNotifyParent 00621 * 00622 * History: 00623 \***************************************************************************/ 00624 00625 void xxxButtonNotifyParent( 00626 PWND pwnd, 00627 UINT code) 00628 { 00629 TL tlpwndParent; 00630 PWND pwndParent; // Parent if it exists 00631 00632 CheckLock(pwnd); 00633 00634 if (pwnd->spwndParent) 00635 pwndParent = REBASEPWND(pwnd, spwndParent); 00636 else 00637 pwndParent = pwnd; 00638 00639 /* 00640 * Note: A button's pwnd->spmenu is used to store the control ID 00641 */ 00642 ThreadLock(pwndParent, &tlpwndParent); 00643 SendMessage(HW(pwndParent), WM_COMMAND, 00644 MAKELONG(PTR_TO_ID(pwnd->spmenu), code), (LPARAM)HWq(pwnd)); 00645 ThreadUnlock(&tlpwndParent); 00646 } 00647 00648 /***************************************************************************\ 00649 * xxxBNReleaseCapture 00650 * 00651 * History: 00652 \***************************************************************************/ 00653 00654 void xxxBNReleaseCapture( 00655 PBUTN pbutn, 00656 BOOL fCheck) 00657 { 00658 PWND pwndT; 00659 UINT check; 00660 BOOL fNotifyParent = FALSE; 00661 TL tlpwndT; 00662 PWND pwnd = pbutn->spwnd; 00663 00664 CheckLock(pwnd); 00665 00666 if (BUTTONSTATE(pbutn) & BST_PUSHED) { 00667 SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE); 00668 if (fCheck) { 00669 switch (TestWF(pwnd, BFTYPEMASK)) { 00670 case BS_AUTOCHECKBOX: 00671 case BS_AUTO3STATE: 00672 check = (UINT)((BUTTONSTATE(pbutn) & BST_CHECKMASK) + 1); 00673 00674 if (check > (UINT)(TestWF(pwnd, BFTYPEMASK) == BS_AUTO3STATE? BST_INDETERMINATE : BST_CHECKED)) { 00675 check = BST_UNCHECKED; 00676 } 00677 SendMessageWorker(pwnd, BM_SETCHECK, check, 0, FALSE); 00678 break; 00679 00680 case BS_AUTORADIOBUTTON: 00681 pwndT = pwnd; 00682 do { 00683 ThreadLock(pwndT, &tlpwndT); 00684 00685 if ((UINT)SendMessage(HW(pwndT), WM_GETDLGCODE, 0, 0L) & 00686 DLGC_RADIOBUTTON) { 00687 SendMessage(HW(pwndT), BM_SETCHECK, (pwnd == pwndT), 0L); 00688 } 00689 pwndT = _GetNextDlgGroupItem(REBASEPWND(pwndT, spwndParent), 00690 pwndT, FALSE); 00691 ThreadUnlock(&tlpwndT); 00692 00693 } while (pwndT != pwnd); 00694 } 00695 00696 fNotifyParent = TRUE; 00697 } 00698 } 00699 00700 if (BUTTONSTATE(pbutn) & BST_CAPTURED) { 00701 BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE); 00702 NtUserReleaseCapture(); 00703 } 00704 00705 if (fNotifyParent) { 00706 00707 /* 00708 * We have to do the notification after setting the buttonstate bits. 00709 */ 00710 xxxButtonNotifyParent(pwnd, BN_CLICKED); 00711 } 00712 } 00713 00714 /***************************************************************************\ 00715 * 00716 * DrawBtnText() 00717 * 00718 * Draws text of button. 00719 * 00720 \***************************************************************************/ 00721 00722 void xxxBNDrawText( 00723 PBUTN pbutn, 00724 HDC hdc, 00725 BOOL dbt, 00726 BOOL fDepress) 00727 { 00728 RECT rc; 00729 HBRUSH hbr; 00730 int x; 00731 int y; 00732 int cx; 00733 int cy; 00734 LPWSTR lpName; 00735 BYTE bStyle; 00736 int cch; 00737 UINT dsFlags; 00738 BTNDATA bdt; 00739 UINT pbfPush; 00740 PWND pwnd = pbutn->spwnd; 00741 00742 bStyle = TestWF(pwnd, BFTYPEMASK); 00743 00744 if (bStyle > sizeof(mpStyleCbr)) { 00745 RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid button style"); 00746 } else if ((bStyle == LOBYTE(BS_GROUPBOX)) && (dbt == DBT_FOCUS)) 00747 return; 00748 00749 pbfPush = IsPushButton(pwnd); 00750 if (pbfPush) { 00751 BNCalcRect(pwnd, hdc, &rc, CBR_PUSHBUTTON, pbfPush); 00752 IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); 00753 00754 // 00755 // This is because we were too stupid to have WM_CTLCOLOR, 00756 // CTLCOLOR_BTN actually set up the damned button colors. For 00757 // old apps, CTLCOLOR_BTN needs to work like CTLCOLOR_STATIC. 00758 // 00759 SetBkColor(hdc, SYSRGB(3DFACE)); 00760 SetTextColor(hdc, SYSRGB(BTNTEXT)); 00761 hbr = SYSHBR(BTNTEXT); 00762 } else { 00763 BNCalcRect(pwnd, hdc, &rc, mpStyleCbr[bStyle], pbfPush); 00764 00765 // Skip stuff for ownerdraw buttons, since we aren't going to 00766 // draw text/image. 00767 if (bStyle == LOBYTE(BS_OWNERDRAW)) 00768 goto DrawFocus; 00769 else 00770 hbr = SYSHBR(WINDOWTEXT); 00771 } 00772 00773 // Alignment 00774 bdt.wFlags = GetAlignment(pwnd); 00775 bdt.pbutn = pbutn; 00776 00777 // Bail if we have nothing to draw 00778 if (TestWF(pwnd, BFBITMAP)) { 00779 BITMAP bmp; 00780 00781 // Bitmap button 00782 if (!pbutn->hImage) 00783 return; 00784 00785 GetObject(pbutn->hImage, sizeof(BITMAP), &bmp); 00786 cx = bmp.bmWidth; 00787 cy = bmp.bmHeight; 00788 00789 dsFlags = DST_BITMAP; 00790 goto UseImageForName; 00791 } else if (TestWF(pwnd, BFICON)) { 00792 // Icon button 00793 if (!pbutn->hImage) 00794 return; 00795 00796 NtUserGetIconSize(pbutn->hImage, 0, &cx, &cy); 00797 cy /= 2; // The bitmap height is half because a Mask is present in NT 00798 00799 dsFlags = DST_ICON; 00800 UseImageForName: 00801 lpName = (LPWSTR)pbutn->hImage; 00802 cch = TRUE; 00803 } else { 00804 // Text button 00805 if (!pwnd->strName.Length) 00806 return; 00807 00808 lpName = REBASE(pwnd, strName.Buffer); 00809 cch = pwnd->strName.Length / sizeof(WCHAR); 00810 00811 if (TestWF(pwnd, BFMULTILINE)) { 00812 00813 bdt.lpsz = lpName; 00814 00815 BNMultiExtent(bdt.wFlags, hdc, &rc, lpName, cch, &cx, &cy); 00816 00817 lpName = (LPWSTR)(LPBTNDATA)&bdt; 00818 dsFlags = DST_COMPLEX; 00819 00820 } else { 00821 SIZE size; 00822 00823 PSMGetTextExtent(hdc, lpName, cch, &size); 00824 cx = size.cx; 00825 cy = size.cy; 00826 /* 00827 * If the control doesn't need underlines, set DST_HIDEPREFIX and 00828 * also do not show the focus indicator 00829 */ 00830 dsFlags = DST_PREFIXTEXT; 00831 if (TestWF(pwnd, WEFPUIACCELHIDDEN)) { 00832 dsFlags |= DSS_HIDEPREFIX; 00833 } else if (pbutn->fPaintKbdCuesOnly) { 00834 dsFlags |= DSS_PREFIXONLY; 00835 } 00836 } 00837 00838 00839 // 00840 // Add on a pixel or two of vertical space to make centering 00841 // happier. That way underline won't abut focus rect unless 00842 // spacing is really tight. 00843 // 00844 cy++; 00845 } 00846 00847 // 00848 // ALIGNMENT 00849 // 00850 00851 // Horizontal 00852 switch (bdt.wFlags & LOBYTE(BFHORZMASK)) { 00853 // 00854 // For left & right justified, we leave a margin of CXEDGE on either 00855 // side for eye-pleasing space. 00856 // 00857 case LOBYTE(BFLEFT): 00858 x = rc.left + SYSMET(CXEDGE); 00859 break; 00860 00861 case LOBYTE(BFRIGHT): 00862 x = rc.right - cx - SYSMET(CXEDGE); 00863 break; 00864 00865 default: 00866 x = (rc.left + rc.right - cx) / 2; 00867 break; 00868 } 00869 00870 // Vertical 00871 switch (bdt.wFlags & LOBYTE(BFVERTMASK)) { 00872 // 00873 // For top & bottom justified, we leave a margin of CYBORDER on 00874 // either side for more eye-pleasing space. 00875 // 00876 case LOBYTE(BFTOP): 00877 y = rc.top + SYSMET(CYBORDER); 00878 break; 00879 00880 case LOBYTE(BFBOTTOM): 00881 y = rc.bottom - cy - SYSMET(CYBORDER); 00882 break; 00883 00884 default: 00885 y = (rc.top + rc.bottom - cy) / 2; 00886 break; 00887 } 00888 00889 // 00890 // Draw the text 00891 // 00892 if (dbt & DBT_TEXT) { 00893 // 00894 // This isn't called for USER buttons. 00895 // 00896 UserAssert(bStyle != LOBYTE(BS_USERBUTTON)); 00897 00898 if (fDepress) { 00899 x += SYSMET(CXBORDER); 00900 y += SYSMET(CYBORDER); 00901 } 00902 00903 if (TestWF(pwnd, WFDISABLED)) { 00904 UserAssert(HIBYTE(BFICON) == HIBYTE(BFBITMAP)); 00905 if (SYSMET(SLOWMACHINE) && 00906 !TestWF(pwnd, BFICON | BFBITMAP) && 00907 (GetBkColor(hdc) != SYSRGB(GRAYTEXT))) 00908 { 00909 // Perf && consistency with menus, statics 00910 SetTextColor(hdc, SYSRGB(GRAYTEXT)); 00911 } 00912 else 00913 dsFlags |= DSS_DISABLED; 00914 } 00915 00916 // 00917 // Use transparent mode for checked push buttons since we're going to 00918 // fill background with dither. 00919 // 00920 if (pbfPush) { 00921 switch (BUTTONSTATE(pbutn) & BST_CHECKMASK) { 00922 case BST_INDETERMINATE: 00923 hbr = SYSHBR(GRAYTEXT); 00924 dsFlags |= DSS_MONO; 00925 // FALL THRU 00926 00927 case BST_CHECKED: 00928 // Drawing on dithered background... 00929 SetBkMode(hdc, TRANSPARENT); 00930 break; 00931 } 00932 } 00933 00934 // 00935 // Use brush and colors currently selected into hdc when we grabbed 00936 // color 00937 // 00938 DrawState(hdc, hbr, (DRAWSTATEPROC)BNMultiDraw, (LPARAM)lpName, 00939 (WPARAM)cch, x, y, cx, cy, 00940 dsFlags); 00941 } 00942 00943 // Draw focus rect. 00944 // 00945 // This can get called for OWNERDRAW and USERDRAW buttons. However, only 00946 // OWNERDRAW buttons let the owner change the drawing of the focus button. 00947 DrawFocus: 00948 if (dbt & DBT_FOCUS) { 00949 if (bStyle == LOBYTE(BS_OWNERDRAW)) { 00950 // For ownerdraw buttons, this is only called in response to a 00951 // WM_SETFOCUS or WM_KILL FOCUS message. So, we can check the 00952 // new state of the focus by looking at the BUTTONSTATE bits 00953 // which are set before this procedure is called. 00954 xxxBNOwnerDraw(pbutn, hdc, ODA_FOCUS); 00955 } else { 00956 // Don't draw the focus if underlines are not turned on 00957 if (!TestWF(pwnd, WEFPUIFOCUSHIDDEN)) { 00958 00959 // Let focus rect always hug edge of push buttons. We already 00960 // have the client area setup for push buttons, so we don't have 00961 // to do anything. 00962 if (!pbfPush) { 00963 00964 RECT rcClient; 00965 00966 _GetClientRect(pwnd, &rcClient); 00967 if (bStyle == LOBYTE(BS_USERBUTTON)) 00968 CopyRect(&rc, &rcClient); 00969 else { 00970 // Try to leave a border all around text. That causes 00971 // focus to hug text. 00972 rc.top = max(rcClient.top, y-SYSMET(CYBORDER)); 00973 rc.bottom = min(rcClient.bottom, rc.top + SYSMET(CYEDGE) + cy); 00974 00975 rc.left = max(rcClient.left, x-SYSMET(CXBORDER)); 00976 rc.right = min(rcClient.right, rc.left + SYSMET(CXEDGE) + cx); 00977 } 00978 } else 00979 InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER)); 00980 00981 // Are back & fore colors set properly? 00982 DrawFocusRect(hdc, &rc); 00983 } 00984 } 00985 } 00986 } 00987 00988 00989 /***************************************************************************\ 00990 * 00991 * DrawCheck() 00992 * 00993 \***************************************************************************/ 00994 00995 void xxxButtonDrawCheck( 00996 PBUTN pbutn, 00997 HDC hdc, 00998 HBRUSH hbr) 00999 { 01000 RECT rc; 01001 int bm; 01002 UINT flags; 01003 BOOL fDoubleBlt = FALSE; 01004 TL tlpwnd; 01005 PWND pwnd = pbutn->spwnd; 01006 PWND pwndParent; 01007 01008 BNCalcRect(pwnd, hdc, &rc, CBR_CHECKBOX, 0); 01009 01010 flags = 0; 01011 if (BUTTONSTATE(pbutn) & BST_CHECKMASK) 01012 flags |= DFCS_CHECKED; 01013 if (BUTTONSTATE(pbutn) & BST_PUSHED) 01014 flags |= DFCS_PUSHED; 01015 if (TestWF(pwnd, WFDISABLED)) 01016 flags |= DFCS_INACTIVE; 01017 01018 bm = OBI_CHECK; 01019 switch (TestWF(pwnd, BFTYPEMASK)) { 01020 case BS_AUTORADIOBUTTON: 01021 case BS_RADIOBUTTON: 01022 fDoubleBlt = TRUE; 01023 bm = OBI_RADIO; 01024 flags |= DFCS_BUTTONRADIO; 01025 break; 01026 01027 case BS_3STATE: 01028 case BS_AUTO3STATE: 01029 if ((BUTTONSTATE(pbutn) & BST_CHECKMASK) == BST_INDETERMINATE) { 01030 bm = OBI_3STATE; 01031 flags |= DFCS_BUTTON3STATE; 01032 break; 01033 } 01034 // FALL THRU 01035 01036 default: 01037 flags |= DFCS_BUTTONCHECK; 01038 break; 01039 } 01040 01041 rc.right = rc.left + gpsi->oembmi[bm].cx; 01042 rc.bottom = rc.top + gpsi->oembmi[bm].cy; 01043 01044 ThreadLockAlways(pwnd->spwndParent, &tlpwnd); 01045 pwndParent = REBASEPWND(pwnd, spwndParent); 01046 PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc); 01047 ThreadUnlock(&tlpwnd); 01048 01049 if (TestWF(pwnd, BFFLAT) && gpsi->BitCount != 1) { 01050 flags |= DFCS_MONO | DFCS_FLAT; 01051 DrawFrameControl(hdc, &rc, DFC_BUTTON, flags); 01052 } else { 01053 01054 switch (flags & (DFCS_CHECKED | DFCS_PUSHED | DFCS_INACTIVE)) 01055 { 01056 case 0: 01057 break; 01058 01059 case DFCS_CHECKED: 01060 bm += DOBI_CHECK; 01061 break; 01062 01063 // These are mutually exclusive! 01064 case DFCS_PUSHED: 01065 case DFCS_INACTIVE: 01066 bm += DOBI_DOWN; // DOBI_DOWN == DOBI_INACTIVE 01067 break; 01068 01069 case DFCS_CHECKED | DFCS_PUSHED: 01070 bm += DOBI_CHECKDOWN; 01071 break; 01072 01073 case DFCS_CHECKED | DFCS_INACTIVE: 01074 bm += DOBI_CHECKDOWN + 1; 01075 break; 01076 } 01077 01078 if (fDoubleBlt) { 01079 // This is a diamond-shaped radio button -- Blt with a mask so that 01080 // the exterior keeps the same color as the window's background 01081 DWORD clrTextSave = SetTextColor(hdc, 0x00000000L); 01082 DWORD clrBkSave = SetBkColor(hdc, 0x00FFFFFFL); 01083 POEMBITMAPINFO pOem = gpsi->oembmi + OBI_RADIOMASK; 01084 01085 NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy, 01086 pOem->x, pOem->y, SRCAND); 01087 01088 pOem = gpsi->oembmi + bm; 01089 NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy, 01090 pOem->x, pOem->y, SRCINVERT); 01091 01092 SetTextColor(hdc, clrTextSave); 01093 SetBkColor(hdc, clrBkSave); 01094 } else { 01095 POEMBITMAPINFO pOem = gpsi->oembmi + bm; 01096 DWORD dwROP = 0; 01097 #ifdef USE_MIRRORING 01098 // We do not want to mirror the check box. 01099 if (MIRRORED_HDC(hdc)) { 01100 dwROP = NOMIRRORBITMAP; 01101 } 01102 #endif 01103 NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy, 01104 pOem->x, pOem->y, SRCCOPY | dwROP); 01105 } 01106 } 01107 } 01108 01109 01110 /***************************************************************************\ 01111 * xxxButtonDrawNewState 01112 * 01113 * History: 01114 \***************************************************************************/ 01115 01116 void xxxButtonDrawNewState( 01117 PBUTN pbutn, 01118 HDC hdc, 01119 HBRUSH hbr, 01120 UINT sOld) 01121 { 01122 PWND pwnd = pbutn->spwnd; 01123 01124 CheckLock(pwnd); 01125 01126 if (sOld != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED)) { 01127 UINT pbfPush; 01128 01129 pbfPush = IsPushButton(pwnd); 01130 01131 switch (TestWF(pwnd, BFTYPEMASK)) { 01132 case BS_GROUPBOX: 01133 case BS_OWNERDRAW: 01134 break; 01135 01136 default: 01137 if (!pbfPush) { 01138 xxxButtonDrawCheck(pbutn, hdc, hbr); 01139 break; 01140 } 01141 01142 case BS_PUSHBUTTON: 01143 case BS_DEFPUSHBUTTON: 01144 case BS_PUSHBOX: 01145 xxxDrawButton(pbutn, hdc, pbfPush); 01146 break; 01147 } 01148 } 01149 } 01150 01151 /***************************************************************************\ 01152 * 01153 * DrawButton() 01154 * 01155 * Draws push-like button with text 01156 * 01157 \***************************************************************************/ 01158 01159 void xxxDrawButton( 01160 PBUTN pbutn, 01161 HDC hdc, 01162 UINT pbfPush) 01163 { 01164 RECT rc; 01165 UINT flags = 0; 01166 UINT state = 0; 01167 PWND pwnd = pbutn->spwnd; 01168 01169 if (BUTTONSTATE(pbutn) & BST_PUSHED) 01170 state |= DFCS_PUSHED; 01171 01172 if (!pbutn->fPaintKbdCuesOnly) { 01173 if (BUTTONSTATE(pbutn) & BST_CHECKMASK) 01174 state |= DFCS_CHECKED; 01175 01176 if (TestWF(pwnd, WFWIN40COMPAT)) 01177 flags = BF_SOFT; 01178 01179 if (TestWF(pwnd, BFFLAT)) 01180 flags |= BF_FLAT | BF_MONO; 01181 01182 _GetClientRect(pwnd, &rc); 01183 01184 if (pbfPush & PBF_DEFAULT) { 01185 DrawFrame(hdc, &rc, 1, DF_WINDOWFRAME); 01186 InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER)); 01187 01188 if (state & DFCS_PUSHED) 01189 flags |= BF_FLAT; 01190 } 01191 01192 DrawPushButton(hdc, &rc, state, flags); 01193 } 01194 01195 xxxBNDrawText(pbutn, hdc, DBT_TEXT | (BUTTONSTATE(pbutn) & 01196 BST_FOCUS ? DBT_FOCUS : 0), (state & DFCS_PUSHED)); 01197 } 01198 01199 01200 /***************************************************************************\ 01201 * xxxBNPaint 01202 * 01203 * History: 01204 \***************************************************************************/ 01205 01206 void xxxBNPaint( 01207 PBUTN pbutn, 01208 HDC hdc) 01209 { 01210 UINT bsWnd; 01211 RECT rc; 01212 HBRUSH hbr; 01213 HBRUSH hbrBtnSave; 01214 TL tlpwndParent; 01215 UINT pbfPush; 01216 PWND pwnd = pbutn->spwnd; 01217 PWND pwndParent; 01218 01219 CheckLock(pwnd); 01220 01221 hbr = xxxBNInitDC(pbutn, hdc); 01222 01223 bsWnd = TestWF(pwnd, BFTYPEMASK); 01224 pbfPush = IsPushButton(pwnd); 01225 if (!pbfPush && !pbutn->fPaintKbdCuesOnly) { 01226 _GetClientRect(pwnd, &rc); 01227 01228 if ((bsWnd != LOBYTE(BS_OWNERDRAW)) && 01229 (bsWnd != LOBYTE(BS_GROUPBOX))) { 01230 ThreadLock(pwnd->spwndParent, &tlpwndParent); 01231 pwndParent = REBASEPWND(pwnd, spwndParent); 01232 PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc); 01233 ThreadUnlock(&tlpwndParent); 01234 } 01235 01236 hbrBtnSave = SelectObject(hdc, hbr); 01237 } 01238 01239 switch (bsWnd) { 01240 case BS_CHECKBOX: 01241 case BS_RADIOBUTTON: 01242 case BS_AUTORADIOBUTTON: 01243 case BS_3STATE: 01244 case BS_AUTOCHECKBOX: 01245 case BS_AUTO3STATE: 01246 if (!pbfPush) { 01247 xxxBNDrawText(pbutn, hdc, 01248 DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE); 01249 if (!pbutn->fPaintKbdCuesOnly) { 01250 xxxButtonDrawCheck(pbutn, hdc, hbr); 01251 } 01252 break; 01253 } 01254 /* 01255 * Fall through for PUSHLIKE buttons 01256 */ 01257 01258 case BS_PUSHBUTTON: 01259 case BS_DEFPUSHBUTTON: 01260 xxxDrawButton(pbutn, hdc, pbfPush); 01261 break; 01262 01263 case BS_PUSHBOX: 01264 xxxBNDrawText(pbutn, hdc, 01265 DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE); 01266 01267 xxxButtonDrawNewState(pbutn, hdc, hbr, 0); 01268 break; 01269 01270 case BS_USERBUTTON: 01271 xxxButtonNotifyParent(pwnd, BN_PAINT); 01272 01273 if (BUTTONSTATE(pbutn) & BST_PUSHED) { 01274 xxxButtonNotifyParent(pwnd, BN_PUSHED); 01275 } 01276 if (TestWF(pwnd, WFDISABLED)) { 01277 xxxButtonNotifyParent(pwnd, BN_DISABLE); 01278 } 01279 if (BUTTONSTATE(pbutn) & BST_FOCUS) { 01280 xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE); 01281 } 01282 break; 01283 01284 case BS_OWNERDRAW: 01285 xxxBNOwnerDraw(pbutn, hdc, ODA_DRAWENTIRE); 01286 break; 01287 01288 case BS_GROUPBOX: 01289 if (!pbutn->fPaintKbdCuesOnly) { 01290 BNCalcRect(pwnd, hdc, &rc, CBR_GROUPFRAME, 0); 01291 DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT | 01292 (TestWF(pwnd, BFFLAT) ? BF_FLAT | BF_MONO : 0)); 01293 01294 BNCalcRect(pwnd, hdc, &rc, CBR_GROUPTEXT, 0); 01295 ThreadLock(pwnd->spwndParent, &tlpwndParent); 01296 pwndParent = REBASEPWND(pwnd, spwndParent); 01297 PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc); 01298 ThreadUnlock(&tlpwndParent); 01299 } 01300 01301 /* 01302 * FillRect(hdc, &rc, hbrBtn); 01303 */ 01304 xxxBNDrawText(pbutn, hdc, DBT_TEXT, FALSE); 01305 break; 01306 } 01307 01308 if (!pbfPush) 01309 SelectObject(hdc, hbrBtnSave); 01310 01311 /* 01312 * Release the font which may have been loaded by xxxButtonInitDC. 01313 */ 01314 if (pbutn->hFont) { 01315 SelectObject(hdc, ghFontSys); 01316 } 01317 } 01318 /***************************************************************************\ 01319 * RepaintButton 01320 * 01321 \***************************************************************************/ 01322 void RepaintButton (PBUTN pbutn) 01323 { 01324 HDC hdc = xxxBNGetDC(pbutn, NULL); 01325 if (hdc != NULL) { 01326 xxxBNPaint(pbutn, hdc); 01327 BNReleaseDC(pbutn, hdc); 01328 } 01329 } 01330 /***************************************************************************\ 01331 * ButtonWndProc 01332 * 01333 * WndProc for buttons, check boxes, etc. 01334 * 01335 * History: 01336 \***************************************************************************/ 01337 01338 LRESULT APIENTRY ButtonWndProcWorker( 01339 PWND pwnd, 01340 UINT message, 01341 WPARAM wParam, 01342 LPARAM lParam, 01343 DWORD fAnsi) 01344 { 01345 HWND hwnd = HWq(pwnd); 01346 UINT bsWnd; 01347 UINT wOldState; 01348 RECT rc; 01349 POINT pt; 01350 HDC hdc; 01351 HBRUSH hbr; 01352 PAINTSTRUCT ps; 01353 TL tlpwndParent; 01354 PBUTN pbutn; 01355 PWND pwndParent; 01356 static BOOL fInit = TRUE; 01357 LONG lResult; 01358 01359 CheckLock(pwnd); 01360 01361 bsWnd = TestWF(pwnd, BFTYPEMASK); 01362 01363 VALIDATECLASSANDSIZE(pwnd, FNID_BUTTON); 01364 INITCONTROLLOOKASIDE(&ButtonLookaside, BUTN, spwnd, 8); 01365 01366 /* 01367 * Get the pbutn for the given window now since we will use it a lot in 01368 * various handlers. This was stored using SetWindowLong(hwnd,0,pbutn) when 01369 * we initially created the button control. 01370 */ 01371 pbutn = ((PBUTNWND)pwnd)->pbutn; 01372 01373 switch (message) { 01374 case WM_NCHITTEST: 01375 if (bsWnd == LOBYTE(BS_GROUPBOX)) { 01376 return (LONG)HTTRANSPARENT; 01377 } else { 01378 goto CallDWP; 01379 } 01380 01381 case WM_ERASEBKGND: 01382 if (bsWnd == LOBYTE(BS_OWNERDRAW)) { 01383 01384 /* 01385 * Handle erase background for owner draw buttons. 01386 */ 01387 _GetClientRect(pwnd, &rc); 01388 ThreadLock(pwnd->spwndParent, &tlpwndParent); 01389 pwndParent = REBASEPWND(pwnd, spwndParent); 01390 PaintRect(HW(pwndParent), hwnd, (HDC)wParam, (HBRUSH)CTLCOLOR_BTN, &rc); 01391 ThreadUnlock(&tlpwndParent); 01392 } 01393 01394 /* 01395 * Do nothing for other buttons, but don't let DefWndProc() do it 01396 * either. It will be erased in xxxBNPaint(). 01397 */ 01398 return (LONG)TRUE; 01399 01400 case WM_PRINTCLIENT: 01401 xxxBNPaint(pbutn, (HDC)wParam); 01402 break; 01403 01404 case WM_PAINT: 01405 01406 /* 01407 * If wParam != NULL, then this is a subclassed paint. 01408 */ 01409 if ((hdc = (HDC)wParam) == NULL) 01410 hdc = NtUserBeginPaint(hwnd, &ps); 01411 01412 if (IsVisible(pwnd)) 01413 xxxBNPaint(pbutn, hdc); 01414 01415 if (!wParam) 01416 NtUserEndPaint(hwnd, &ps); 01417 break; 01418 01419 case WM_SETFOCUS: 01420 BUTTONSTATE(pbutn) |= BST_FOCUS; 01421 if ((hdc = xxxBNGetDC(pbutn, NULL)) != NULL) { 01422 xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE); 01423 01424 BNReleaseDC(pbutn, hdc); 01425 } 01426 01427 if (TestWF(pwnd, BFNOTIFY)) 01428 xxxButtonNotifyParent(pwnd, BN_SETFOCUS); 01429 01430 if (!(BUTTONSTATE(pbutn) & BST_INCLICK)) { 01431 switch (bsWnd) { 01432 case LOBYTE(BS_RADIOBUTTON): 01433 case LOBYTE(BS_AUTORADIOBUTTON): 01434 if (!(BUTTONSTATE(pbutn) & BST_DONTCLICK)) { 01435 if (!(BUTTONSTATE(pbutn) & BST_CHECKMASK)) { 01436 xxxButtonNotifyParent(pwnd, BN_CLICKED); 01437 } 01438 } 01439 break; 01440 } 01441 } 01442 break; 01443 01444 case WM_GETDLGCODE: 01445 switch (bsWnd) { 01446 case LOBYTE(BS_DEFPUSHBUTTON): 01447 wParam = DLGC_DEFPUSHBUTTON; 01448 break; 01449 01450 case LOBYTE(BS_PUSHBUTTON): 01451 case LOBYTE(BS_PUSHBOX): 01452 wParam = DLGC_UNDEFPUSHBUTTON; 01453 break; 01454 01455 case LOBYTE(BS_AUTORADIOBUTTON): 01456 case LOBYTE(BS_RADIOBUTTON): 01457 wParam = DLGC_RADIOBUTTON; 01458 break; 01459 01460 case LOBYTE(BS_GROUPBOX): 01461 return (LONG)DLGC_STATIC; 01462 01463 case LOBYTE(BS_CHECKBOX): 01464 case LOBYTE(BS_AUTOCHECKBOX): 01465 01466 /* 01467 * If this is a char that is a '=/+', or '-', we want it 01468 */ 01469 if (lParam && ((LPMSG)lParam)->message == WM_CHAR) { 01470 switch (wParam) { 01471 case TEXT('='): 01472 case TEXT('+'): 01473 case TEXT('-'): 01474 wParam = DLGC_WANTCHARS; 01475 break; 01476 01477 default: 01478 wParam = 0; 01479 } 01480 } else { 01481 wParam = 0; 01482 } 01483 break; 01484 01485 default: 01486 wParam = 0; 01487 } 01488 return (LONG)(wParam | DLGC_BUTTON); 01489 01490 case WM_CAPTURECHANGED: 01491 if (BUTTONSTATE(pbutn) & BST_CAPTURED) { 01492 // Unwittingly, we've been kicked out of capture, 01493 // so undepress etc. 01494 if (BUTTONSTATE(pbutn) & BST_MOUSE) 01495 SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE); 01496 BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE); 01497 } 01498 break; 01499 01500 case WM_KILLFOCUS: 01501 01502 /* 01503 * If we are losing the focus and we are in "capture mode", click 01504 * the button. This allows tab and space keys to overlap for 01505 * fast toggle of a series of buttons. 01506 */ 01507 if (BUTTONSTATE(pbutn) & BST_MOUSE) { 01508 01509 /* 01510 * If for some reason we are killing the focus, and we have the 01511 * mouse captured, don't notify the parent we got clicked. This 01512 * breaks Omnis Quartz otherwise. 01513 */ 01514 SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE); 01515 } 01516 01517 xxxBNReleaseCapture(pbutn, TRUE); 01518 01519 BUTTONSTATE(pbutn) &= ~BST_FOCUS; 01520 if ((hdc = xxxBNGetDC(pbutn, NULL)) != NULL) { 01521 xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE); 01522 01523 BNReleaseDC(pbutn, hdc); 01524 } 01525 01526 if (TestWF(pwnd, BFNOTIFY)) 01527 xxxButtonNotifyParent(pwnd, BN_KILLFOCUS); 01528 01529 /* 01530 * Since the bold border around the defpushbutton is done by 01531 * someone else, we need to invalidate the rect so that the 01532 * focus rect is repainted properly. 01533 */ 01534 NtUserInvalidateRect(hwnd, NULL, FALSE); 01535 break; 01536 01537 case WM_LBUTTONDBLCLK: 01538 01539 /* 01540 * Double click messages are recognized for BS_RADIOBUTTON, 01541 * BS_USERBUTTON, and BS_OWNERDRAW styles. For all other buttons, 01542 * double click is handled like a normal button down. 01543 */ 01544 switch (bsWnd) { 01545 default: 01546 if (!TestWF(pwnd, BFNOTIFY)) 01547 goto btnclick; 01548 01549 case LOBYTE(BS_USERBUTTON): 01550 case LOBYTE(BS_RADIOBUTTON): 01551 case LOBYTE(BS_OWNERDRAW): 01552 xxxButtonNotifyParent(pwnd, BN_DOUBLECLICKED); 01553 break; 01554 } 01555 break; 01556 01557 case WM_LBUTTONUP: 01558 if (BUTTONSTATE(pbutn) & BST_MOUSE) { 01559 xxxBNReleaseCapture(pbutn, TRUE); 01560 } 01561 break; 01562 01563 case WM_MOUSEMOVE: 01564 if (!(BUTTONSTATE(pbutn) & BST_MOUSE)) { 01565 break; 01566 } 01567 01568 /* 01569 *** FALL THRU ** 01570 */ 01571 case WM_LBUTTONDOWN: 01572 btnclick: 01573 if (xxxBNSetCapture(pbutn, BST_MOUSE)) { 01574 _GetClientRect(pwnd, &rc); 01575 POINTSTOPOINT(pt, lParam); 01576 SendMessageWorker(pwnd, BM_SETSTATE, PtInRect(&rc, pt), 0, FALSE); 01577 } 01578 break; 01579 01580 case WM_CHAR: 01581 if (BUTTONSTATE(pbutn) & BST_MOUSE) 01582 goto CallDWP; 01583 01584 if (bsWnd != LOBYTE(BS_CHECKBOX) && 01585 bsWnd != LOBYTE(BS_AUTOCHECKBOX)) 01586 goto CallDWP; 01587 01588 switch (wParam) { 01589 case TEXT('+'): 01590 case TEXT('='): 01591 wParam = 1; // we must Set the check mark on. 01592 goto SetCheck; 01593 01594 case TEXT('-'): 01595 wParam = 0; // Set the check mark off. 01596 SetCheck: 01597 // Must notify only if the check status changes 01598 if ((WORD)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (WORD)wParam) 01599 { 01600 // We must check/uncheck only if it is AUTO 01601 if (bsWnd == LOBYTE(BS_AUTOCHECKBOX)) 01602 { 01603 if (xxxBNSetCapture(pbutn, 0)) 01604 { 01605 SendMessageWorker(pwnd, BM_SETCHECK, wParam, 0, FALSE); 01606 01607 xxxBNReleaseCapture(pbutn, TRUE); 01608 } 01609 } 01610 01611 xxxButtonNotifyParent(pwnd, BN_CLICKED); 01612 } 01613 break; 01614 01615 default: 01616 goto CallDWP; 01617 } 01618 break; 01619 01620 case BM_CLICK: 01621 // Don't recurse into this code! 01622 if (BUTTONSTATE(pbutn) & BST_INBMCLICK) 01623 break; 01624 01625 BUTTONSTATE(pbutn) |= BST_INBMCLICK; 01626 SendMessageWorker(pwnd, WM_LBUTTONDOWN, 0, 0, FALSE); 01627 SendMessageWorker(pwnd, WM_LBUTTONUP, 0, 0, FALSE); 01628 BUTTONSTATE(pbutn) &= ~BST_INBMCLICK; 01629 01630 /* 01631 *** FALL THRU ** 01632 */ 01633 01634 case WM_KEYDOWN: 01635 if (BUTTONSTATE(pbutn) & BST_MOUSE) 01636 break; 01637 01638 if (wParam == VK_SPACE) { 01639 if (xxxBNSetCapture(pbutn, 0)) { 01640 SendMessageWorker(pwnd, BM_SETSTATE, TRUE, 0, FALSE); 01641 } 01642 } else { 01643 xxxBNReleaseCapture(pbutn, FALSE); 01644 } 01645 break; 01646 01647 case WM_KEYUP: 01648 case WM_SYSKEYUP: 01649 if (BUTTONSTATE(pbutn) & BST_MOUSE) { 01650 goto CallDWP; 01651 } 01652 01653 /* 01654 * Don't cancel the capture mode on the up of the tab in case the 01655 * guy is overlapping tab and space keys. 01656 */ 01657 if (wParam == VK_TAB) { 01658 goto CallDWP; 01659 } 01660 01661 /* 01662 * WARNING: pwnd is history after this call! 01663 */ 01664 xxxBNReleaseCapture(pbutn, (wParam == VK_SPACE)); 01665 01666 if (message == WM_SYSKEYUP) { 01667 goto CallDWP; 01668 } 01669 break; 01670 01671 case BM_GETSTATE: 01672 return (LONG)BUTTONSTATE(pbutn); 01673 01674 case BM_SETSTATE: 01675 wOldState = (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED); 01676 if (wParam) { 01677 BUTTONSTATE(pbutn) |= BST_PUSHED; 01678 } else { 01679 BUTTONSTATE(pbutn) &= ~BST_PUSHED; 01680 } 01681 01682 if ((hdc = xxxBNGetDC(pbutn, &hbr)) != NULL) { 01683 if (bsWnd == LOBYTE(BS_USERBUTTON)) { 01684 xxxButtonNotifyParent(pwnd, (UINT)(wParam ? BN_PUSHED : BN_UNPUSHED)); 01685 } else if (bsWnd == LOBYTE(BS_OWNERDRAW)) { 01686 if (wOldState != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED)) { 01687 /* 01688 * Only notify for drawing if state has changed.. 01689 */ 01690 xxxBNOwnerDraw(pbutn, hdc, ODA_SELECT); 01691 } 01692 } else { 01693 xxxButtonDrawNewState(pbutn, hdc, hbr, wOldState); 01694 } 01695 01696 BNReleaseDC(pbutn, hdc); 01697 } 01698 if (FWINABLE() && (wOldState != (BOOL)(BUTTONSTATE(pbutn) & BST_PUSHED))) { 01699 NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER); 01700 } 01701 break; 01702 01703 case BM_GETCHECK: 01704 return (LONG)(BUTTONSTATE(pbutn) & BST_CHECKMASK); 01705 01706 case BM_SETCHECK: 01707 switch (bsWnd) { 01708 case LOBYTE(BS_RADIOBUTTON): 01709 case LOBYTE(BS_AUTORADIOBUTTON): 01710 if (wParam) { 01711 SetWindowState(pwnd, WFTABSTOP); 01712 } else { 01713 ClearWindowState(pwnd, WFTABSTOP); 01714 } 01715 01716 /* 01717 *** FALL THRU ** 01718 */ 01719 case LOBYTE(BS_CHECKBOX): 01720 case LOBYTE(BS_AUTOCHECKBOX): 01721 if (wParam) { 01722 wParam = 1; 01723 } 01724 goto CheckIt; 01725 01726 case LOBYTE(BS_3STATE): 01727 case LOBYTE(BS_AUTO3STATE): 01728 if (wParam > BST_INDETERMINATE) { 01729 wParam = BST_INDETERMINATE; 01730 } 01731 CheckIt: 01732 if ((UINT)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (UINT)wParam) { 01733 BUTTONSTATE(pbutn) &= ~BST_CHECKMASK; 01734 BUTTONSTATE(pbutn) |= (UINT)wParam; 01735 01736 if (!IsVisible(pwnd)) 01737 break; 01738 01739 if ((hdc = xxxBNGetDC(pbutn, &hbr)) != NULL) { 01740 if (TestWF(pwnd, BFPUSHLIKE)) { 01741 xxxDrawButton(pbutn, hdc, PBF_PUSHABLE); 01742 } else { 01743 xxxButtonDrawCheck(pbutn, hdc, hbr); 01744 } 01745 BNReleaseDC(pbutn, hdc); 01746 } 01747 01748 if (FWINABLE()) 01749 NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER); 01750 } 01751 break; 01752 } 01753 break; 01754 01755 case BM_SETSTYLE: 01756 NtUserAlterWindowStyle(hwnd, BS_TYPEMASK, (DWORD)wParam); 01757 01758 if (lParam) { 01759 NtUserInvalidateRect(hwnd, NULL, TRUE); 01760 } 01761 if (FWINABLE()) { 01762 NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER); 01763 } 01764 break; 01765 01766 case WM_SETTEXT: 01767 01768 /* 01769 * In case the new group name is longer than the old name, 01770 * this paints over the old name before repainting the group 01771 * box with the new name. 01772 */ 01773 if (bsWnd == LOBYTE(BS_GROUPBOX)) { 01774 hdc = xxxBNGetDC(pbutn, &hbr); 01775 if (hdc != NULL) { 01776 BNCalcRect(pwnd, hdc, &rc, CBR_GROUPTEXT, 0); 01777 NtUserInvalidateRect(hwnd, &rc, TRUE); 01778 01779 pwndParent = REBASEPWND(pwnd, spwndParent); 01780 ThreadLock(pwnd->spwndParent, &tlpwndParent); 01781 PaintRect(HW(pwndParent), hwnd, hdc, hbr, &rc); 01782 ThreadUnlock(&tlpwndParent); 01783 01784 BNReleaseDC(pbutn, hdc); 01785 } 01786 } 01787 01788 lResult = _DefSetText(hwnd, (LPWSTR)lParam, (BOOL)fAnsi); 01789 01790 if (FWINABLE()) { 01791 NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_WINDOW, INDEXID_CONTAINER); 01792 } 01793 goto DoEnable; 01794 01795 /* 01796 *** FALL THRU ** 01797 */ 01798 case WM_ENABLE: 01799 lResult = 0L; 01800 DoEnable: 01801 RepaintButton(pbutn); 01802 return lResult; 01803 01804 case WM_SETFONT: 01805 /* 01806 * wParam - handle to the font 01807 * lParam - if true, redraw else don't 01808 */ 01809 BNSetFont(pbutn, (HFONT)wParam, (BOOL)(lParam != 0)); 01810 break; 01811 01812 case WM_GETFONT: 01813 return (LRESULT)pbutn->hFont; 01814 01815 case BM_GETIMAGE: 01816 case BM_SETIMAGE: 01817 if (!IsValidImage(wParam, TestWF(pwnd, BFIMAGEMASK), IMAGE_BMMAX)) { 01818 RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid button image type"); 01819 } else { 01820 HANDLE hOld = pbutn->hImage; 01821 01822 if (message == BM_SETIMAGE) { 01823 pbutn->hImage = (HANDLE)lParam; 01824 if (TestWF(pwnd, WFVISIBLE)) { 01825 NtUserInvalidateRect(hwnd, NULL, TRUE); 01826 } 01827 } 01828 return (LRESULT)hOld; 01829 } 01830 break; 01831 01832 case WM_NCDESTROY: 01833 case WM_FINALDESTROY: 01834 if (pbutn) { 01835 Unlock(&pbutn->spwnd); 01836 FreeLookasideEntry(&ButtonLookaside, pbutn); 01837 } 01838 NtUserSetWindowFNID(hwnd, FNID_CLEANEDUP_BIT); 01839 break; 01840 01841 case WM_NCCREATE: 01842 // Borland's OBEX has a button with style 0x98; We didn't strip 01843 // these bits in win3.1 because we checked for 0x08. 01844 // Stripping these bits cause a GP Fault in OBEX. 01845 // For win3.1 guys, I use the old code to strip the style bits. 01846 // 01847 if (TestWF(pwnd, WFWIN31COMPAT)) { 01848 if(((!TestWF(pwnd, WFWIN40COMPAT)) && 01849 (((LOBYTE(pwnd->style)) & (LOBYTE(~BS_LEFTTEXT))) == LOBYTE(BS_USERBUTTON))) || 01850 (TestWF(pwnd, WFWIN40COMPAT) && 01851 (bsWnd == LOBYTE(BS_USERBUTTON)))) 01852 { 01853 // BS_USERBUTTON is no longer allowed for 3.1 and beyond. 01854 // Just turn to normal push button. 01855 NtUserAlterWindowStyle(hwnd, BS_TYPEMASK, 0); 01856 RIPMSG0(RIP_WARNING, "BS_USERBUTTON no longer supported"); 01857 } 01858 } 01859 if (TestWF(pwnd,WEFRIGHT)) { 01860 NtUserAlterWindowStyle(hwnd, BS_RIGHT | BS_RIGHTBUTTON, BS_RIGHT | BS_RIGHTBUTTON); 01861 } 01862 goto CallDWP; 01863 01864 case WM_INPUTLANGCHANGEREQUEST: 01865 01866 // 01867 // #115190 01868 // If the window is one of controls on top of dialogbox, 01869 // let the parent dialog handle it. 01870 // 01871 if (TestwndChild(pwnd) && pwnd->spwndParent) { 01872 PWND pwndParent = REBASEPWND(pwnd, spwndParent); 01873 if (pwndParent) { 01874 PCLS pclsParent = REBASEALWAYS(pwndParent, pcls); 01875 01876 UserAssert(pclsParent != NULL); 01877 if (pclsParent->atomClassName == gpsi->atomSysClass[ICLS_DIALOG]) { 01878 RIPMSG0(RIP_VERBOSE, "Button: WM_INPUTLANGCHANGEREQUEST is sent to parent.\n"); 01879 return SendMessageWorker(pwndParent, message, wParam, lParam, FALSE); 01880 } 01881 } 01882 } 01883 goto CallDWP; 01884 01885 case WM_UPDATEUISTATE: 01886 { 01887 DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); 01888 if (ISBSTEXTOROD(pwnd)) { 01889 pbutn->fPaintKbdCuesOnly = TRUE; 01890 RepaintButton(pbutn); 01891 pbutn->fPaintKbdCuesOnly = FALSE; 01892 } 01893 } 01894 break; 01895 01896 default: 01897 CallDWP: 01898 return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); 01899 } 01900 01901 return 0L; 01902 } 01903 01904 /***************************************************************************\ 01905 \***************************************************************************/ 01906 01907 LRESULT WINAPI ButtonWndProcA( 01908 HWND hwnd, 01909 UINT message, 01910 WPARAM wParam, 01911 LPARAM lParam) 01912 { 01913 PWND pwnd; 01914 01915 if ((pwnd = ValidateHwnd(hwnd)) == NULL) { 01916 return (0L); 01917 } 01918 01919 /* 01920 * If the control is not interested in this message, 01921 * pass it to DefWindowProc. 01922 */ 01923 if (!FWINDOWMSG(message, FNID_BUTTON)) 01924 return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE); 01925 01926 return ButtonWndProcWorker(pwnd, message, wParam, lParam, TRUE); 01927 } 01928 01929 LRESULT WINAPI ButtonWndProcW( 01930 HWND hwnd, 01931 UINT message, 01932 WPARAM wParam, 01933 LPARAM lParam) 01934 { 01935 PWND pwnd; 01936 01937 if ((pwnd = ValidateHwnd(hwnd)) == NULL) { 01938 return (0L); 01939 } 01940 01941 /* 01942 * If the control is not interested in this message, 01943 * pass it to DefWindowProc. 01944 */ 01945 if (!FWINDOWMSG(message, FNID_BUTTON)) 01946 return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE); 01947 01948 return ButtonWndProcWorker(pwnd, message, wParam, lParam, FALSE); 01949 }

Generated on Sat May 15 19:39:18 2004 for test by doxygen 1.3.7