Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1955 lines
55 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: btnctl.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Radio Button and Check Box Handling Routines
  7. *
  8. * History:
  9. * ??-???-???? ?????? Ported from Win 3.0 sources
  10. * 01-Feb-1991 mikeke Added Revalidation code
  11. * 03-Jan-1992 ianja Neutralized (ANSI/wide-character)
  12. \***************************************************************************/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. /* ButtonCalcRect codes */
  16. #define CBR_CLIENTRECT 0
  17. #define CBR_CHECKBOX 1
  18. #define CBR_CHECKTEXT 2
  19. #define CBR_GROUPTEXT 3
  20. #define CBR_GROUPFRAME 4
  21. #define CBR_PUSHBUTTON 5
  22. CONST BYTE mpStyleCbr[] = {
  23. CBR_PUSHBUTTON, /* BS_PUSHBUTTON */
  24. CBR_PUSHBUTTON, /* BS_DEFPUSHBUTTON */
  25. CBR_CHECKTEXT, /* BS_CHECKBOX */
  26. CBR_CHECKTEXT, /* BS_AUTOCHECKBOX */
  27. CBR_CHECKTEXT, /* BS_RADIOBUTTON */
  28. CBR_CHECKTEXT, /* BS_3STATE */
  29. CBR_CHECKTEXT, /* BS_AUTO3STATE */
  30. CBR_GROUPTEXT, /* BS_GROUPBOX */
  31. CBR_CLIENTRECT, /* BS_USERBUTTON */
  32. CBR_CHECKTEXT, /* BS_AUTORADIOBUTTON */
  33. CBR_CLIENTRECT, /* BS_PUSHBOX */
  34. CBR_CLIENTRECT, /* BS_OWNERDRAW */
  35. };
  36. #define IMAGE_BMMAX IMAGE_CURSOR+1
  37. static CONST BYTE rgbType[IMAGE_BMMAX] = {
  38. BS_BITMAP, // IMAGE_BITMAP
  39. BS_ICON, // IMAGE_CURSOR
  40. BS_ICON // IMAGE_ICON
  41. };
  42. #define IsValidImage(imageType, realType, max) \
  43. ((imageType < max) && (rgbType[imageType] == realType))
  44. typedef struct tagBTNDATA {
  45. LPWSTR lpsz; // Text string
  46. PBUTN pbutn; // Button data
  47. WORD wFlags; // Alignment flags
  48. } BTNDATA, FAR * LPBTNDATA;
  49. void xxxDrawButton(PBUTN pbutn, HDC hdc, UINT pbfPush);
  50. LOOKASIDE ButtonLookaside;
  51. /***************************************************************************\
  52. *
  53. * IsPushButton()
  54. *
  55. * Returns non-zero if the window is a push button. Returns flags that
  56. * are interesting if it is. These flags are
  57. *
  58. *
  59. *
  60. \***************************************************************************/
  61. UINT IsPushButton(
  62. PWND pwnd)
  63. {
  64. BYTE bStyle;
  65. UINT flags;
  66. bStyle = TestWF(pwnd, BFTYPEMASK);
  67. flags = 0;
  68. switch (bStyle) {
  69. case LOBYTE(BS_PUSHBUTTON):
  70. flags |= PBF_PUSHABLE;
  71. break;
  72. case LOBYTE(BS_DEFPUSHBUTTON):
  73. flags |= PBF_PUSHABLE | PBF_DEFAULT;
  74. break;
  75. default:
  76. if (TestWF(pwnd, BFPUSHLIKE))
  77. flags |= PBF_PUSHABLE;
  78. break;
  79. }
  80. return(flags);
  81. }
  82. /***************************************************************************\
  83. *
  84. * GetAlignment()
  85. *
  86. * Gets default alignment of button. If BS_HORZMASK and/or BS_VERTMASK
  87. * is specified, uses those. Otherwise, uses default for button.
  88. *
  89. * It's probably a fine time to describe what alignment flags mean for
  90. * each type of button. Note that the presence of a bitmap/icon affects
  91. * the meaning of alignments.
  92. *
  93. * (1) Push like buttons
  94. * With one of {bitmap, icon, text}:
  95. * Just like you'd expect
  96. * With one of {bitmap, icon} AND text:
  97. * Image & text are centered as a unit; alignment means where
  98. * the image shows up. E.G., left-aligned means the image
  99. * on the left, text on the right.
  100. * (2) Radio/check like buttons
  101. * Left aligned means check/radio box is on left, then bitmap/icon
  102. * and text follows, left justified.
  103. * Right aligned means checkk/radio box is on right, preceded by
  104. * text and bitmap/icon, right justified.
  105. * Centered has no meaning.
  106. * With one of {bitmap, icon} AND text:
  107. * Top aligned means bitmap/icon above, text below
  108. * Bottom aligned means text above, bitmap/icon below
  109. * With one of {bitmap, icon, text}
  110. * Alignments mean what you'd expect.
  111. * (3) Group boxes
  112. * Left aligned means text is left justified on left side
  113. * Right aligned means text is right justified on right side
  114. * Center aligned means text is in middle
  115. *
  116. *
  117. \***************************************************************************/
  118. WORD GetAlignment(
  119. PWND pwnd)
  120. {
  121. BYTE bHorz;
  122. BYTE bVert;
  123. bHorz = TestWF(pwnd, BFHORZMASK);
  124. bVert = TestWF(pwnd, BFVERTMASK);
  125. if (!bHorz || !bVert) {
  126. if (IsPushButton(pwnd)) {
  127. if (!bHorz)
  128. bHorz = LOBYTE(BFCENTER);
  129. } else {
  130. if (!bHorz)
  131. bHorz = LOBYTE(BFLEFT);
  132. }
  133. if (!bVert)
  134. bVert = LOBYTE(BFVCENTER);
  135. }
  136. return bHorz | bVert;
  137. }
  138. /***************************************************************************\
  139. *
  140. * BNSetFont()
  141. *
  142. * Changes button font, and decides if we can use real bold font for default
  143. * push buttons or if we have to simulate it.
  144. *
  145. \***************************************************************************/
  146. void BNSetFont(
  147. PBUTN pbutn,
  148. HFONT hfn,
  149. BOOL fRedraw)
  150. {
  151. PWND pwnd = pbutn->spwnd;
  152. pbutn->hFont = hfn;
  153. if (fRedraw && IsVisible(pwnd)) {
  154. NtUserInvalidateRect(HWq(pwnd), NULL, TRUE);
  155. }
  156. }
  157. /***************************************************************************\
  158. * xxxBNInitDC
  159. *
  160. * History:
  161. \***************************************************************************/
  162. HBRUSH xxxBNInitDC(
  163. PBUTN pbutn,
  164. HDC hdc)
  165. {
  166. UINT wColor;
  167. BYTE bStyle;
  168. HBRUSH hbr;
  169. PWND pwnd = pbutn->spwnd;
  170. CheckLock(pwnd);
  171. /*
  172. * Set BkMode before getting brush so that the app can change it to
  173. * transparent if it wants.
  174. */
  175. SetBkMode(hdc, OPAQUE);
  176. bStyle = TestWF(pwnd, BFTYPEMASK);
  177. switch (bStyle) {
  178. default:
  179. if (TestWF(pwnd, WFWIN40COMPAT) && !TestWF(pwnd, BFPUSHLIKE)) {
  180. wColor = WM_CTLCOLORSTATIC;
  181. break;
  182. }
  183. case LOBYTE(BS_PUSHBUTTON):
  184. case LOBYTE(BS_DEFPUSHBUTTON):
  185. case LOBYTE(BS_OWNERDRAW):
  186. case LOBYTE(BS_USERBUTTON):
  187. wColor = WM_CTLCOLORBTN;
  188. break;
  189. }
  190. hbr = GetControlBrush(HWq(pwnd), hdc, wColor);
  191. /*
  192. * Select in the user's font if set, and save the old font so that we can
  193. * restore it when we release the dc.
  194. */
  195. if (pbutn->hFont) {
  196. SelectObject(hdc, pbutn->hFont);
  197. }
  198. /*
  199. * Clip output to the window rect if needed.
  200. */
  201. if (bStyle != LOBYTE(BS_GROUPBOX)) {
  202. IntersectClipRect(hdc, 0, 0,
  203. pwnd->rcClient.right - pwnd->rcClient.left,
  204. pwnd->rcClient.bottom - pwnd->rcClient.top);
  205. }
  206. if (TestWF(pwnd,WEFRTLREADING))
  207. SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
  208. return(hbr);
  209. }
  210. /***************************************************************************\
  211. * xxxBNGetDC
  212. *
  213. * History:
  214. \***************************************************************************/
  215. HDC xxxBNGetDC(
  216. PBUTN pbutn,
  217. HBRUSH *lphbr)
  218. {
  219. HDC hdc;
  220. PWND pwnd = pbutn->spwnd;
  221. CheckLock(pwnd);
  222. if (IsVisible(pwnd)) {
  223. HBRUSH hbr;
  224. hdc = NtUserGetDC(HWq(pwnd));
  225. hbr = xxxBNInitDC(pbutn, hdc);
  226. if (lphbr!=NULL)
  227. *lphbr = hbr;
  228. return hdc;
  229. }
  230. return NULL;
  231. }
  232. /***************************************************************************\
  233. * BNReleaseDC
  234. *
  235. * History:
  236. \***************************************************************************/
  237. void BNReleaseDC(
  238. PBUTN pbutn,
  239. HDC hdc)
  240. {
  241. PWND pwnd = pbutn->spwnd;
  242. if (TestWF(pwnd,WEFRTLREADING))
  243. SetTextAlign(hdc, GetTextAlign(hdc) & ~TA_RTLREADING);
  244. if (pbutn->hFont) {
  245. SelectObject(hdc, ghFontSys);
  246. }
  247. ReleaseDC(HWq(pwnd), hdc);
  248. }
  249. /***************************************************************************\
  250. * xxxBNOwnerDraw
  251. *
  252. * History:
  253. \***************************************************************************/
  254. void xxxBNOwnerDraw(
  255. PBUTN pbutn,
  256. HDC hdc,
  257. UINT itemAction)
  258. {
  259. DRAWITEMSTRUCT drawItemStruct;
  260. TL tlpwndParent;
  261. PWND pwnd = pbutn->spwnd;
  262. UINT itemState = 0;
  263. if (TestWF(pwnd, WEFPUIFOCUSHIDDEN)) {
  264. itemState |= ODS_NOFOCUSRECT;
  265. }
  266. if (TestWF(pwnd, WEFPUIACCELHIDDEN)) {
  267. itemState |= ODS_NOACCEL;
  268. }
  269. if (BUTTONSTATE(pbutn) & BST_FOCUS) {
  270. itemState |= ODS_FOCUS;
  271. }
  272. if (BUTTONSTATE(pbutn) & BST_PUSHED) {
  273. itemState |= ODS_SELECTED;
  274. }
  275. if (TestWF(pwnd, WFDISABLED))
  276. itemState |= ODS_DISABLED;
  277. drawItemStruct.CtlType = ODT_BUTTON;
  278. drawItemStruct.CtlID = PtrToUlong(pwnd->spmenu);
  279. drawItemStruct.itemAction = itemAction;
  280. drawItemStruct.itemState = itemState;
  281. drawItemStruct.hwndItem = HWq(pwnd);
  282. drawItemStruct.hDC = hdc;
  283. _GetClientRect(pwnd, &drawItemStruct.rcItem);
  284. drawItemStruct.itemData = 0L;
  285. /*
  286. * Send a WM_DRAWITEM message to the parent
  287. * IanJa: in this case pMenu is being used as the control ID
  288. */
  289. ThreadLock(REBASEPWND(pwnd, spwndParent), &tlpwndParent);
  290. SendMessage(HW(REBASEPWND(pwnd, spwndParent)), WM_DRAWITEM, (WPARAM)pwnd->spmenu,
  291. (LPARAM)&drawItemStruct);
  292. ThreadUnlock(&tlpwndParent);
  293. }
  294. /***************************************************************************\
  295. * CalcBtnRect
  296. *
  297. * History:
  298. \***************************************************************************/
  299. void BNCalcRect(
  300. PWND pwnd,
  301. HDC hdc,
  302. LPRECT lprc,
  303. int code,
  304. UINT pbfFlags)
  305. {
  306. int cch;
  307. SIZE extent;
  308. int dy;
  309. LPWSTR lpName;
  310. UINT align;
  311. _GetClientRect(pwnd, lprc);
  312. align = GetAlignment(pwnd);
  313. switch (code) {
  314. case CBR_PUSHBUTTON:
  315. // Subtract out raised edge all around
  316. InflateRect(lprc, -SYSMET(CXEDGE), -SYSMET(CYEDGE));
  317. if (pbfFlags & PBF_DEFAULT)
  318. InflateRect(lprc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
  319. break;
  320. case CBR_CHECKBOX:
  321. switch (align & LOBYTE(BFVERTMASK))
  322. {
  323. case LOBYTE(BFVCENTER):
  324. lprc->top = (lprc->top + lprc->bottom - gpsi->oembmi[OBI_CHECK].cy) / 2;
  325. break;
  326. case LOBYTE(BFTOP):
  327. case LOBYTE(BFBOTTOM):
  328. PSMGetTextExtent(hdc, (LPWSTR)szOneChar, 1, &extent);
  329. dy = extent.cy + extent.cy/4;
  330. // Save vertical extent
  331. extent.cx = dy;
  332. // Get centered amount
  333. dy = (dy - gpsi->oembmi[OBI_CHECK].cy) / 2;
  334. if ((align & LOBYTE(BFVERTMASK)) == LOBYTE(BFTOP))
  335. lprc->top += dy;
  336. else
  337. lprc->top = lprc->bottom - extent.cx + dy;
  338. break;
  339. }
  340. if (TestWF(pwnd, BFRIGHTBUTTON))
  341. lprc->left = lprc->right - gpsi->oembmi[OBI_CHECK].cx;
  342. else
  343. lprc->right = lprc->left + gpsi->oembmi[OBI_CHECK].cx;
  344. break;
  345. case CBR_CHECKTEXT:
  346. if (TestWF(pwnd, BFRIGHTBUTTON)) {
  347. lprc->right -= gpsi->oembmi[OBI_CHECK].cx;
  348. // More spacing for 4.0 dudes
  349. if (TestWF(pwnd, WFWIN40COMPAT)) {
  350. PSMGetTextExtent(hdc, szOneChar, 1, &extent);
  351. lprc->right -= extent.cx / 2;
  352. }
  353. } else {
  354. lprc->left += gpsi->oembmi[OBI_CHECK].cx;
  355. // More spacing for 4.0 dudes
  356. if (TestWF(pwnd, WFWIN40COMPAT)) {
  357. PSMGetTextExtent(hdc, szOneChar, 1, &extent);
  358. lprc->left += extent.cx / 2;
  359. }
  360. }
  361. break;
  362. case CBR_GROUPTEXT:
  363. if (!pwnd->strName.Length)
  364. goto EmptyRect;
  365. lpName = REBASE(pwnd, strName.Buffer);
  366. if (!(cch = pwnd->strName.Length / sizeof(WCHAR))) {
  367. EmptyRect:
  368. SetRectEmpty(lprc);
  369. break;
  370. }
  371. PSMGetTextExtent(hdc, lpName, cch, &extent);
  372. extent.cx += SYSMET(CXEDGE) * 2;
  373. switch (align & LOBYTE(BFHORZMASK))
  374. {
  375. // BFLEFT, nothing
  376. case LOBYTE(BFLEFT):
  377. lprc->left += (gpsi->cxSysFontChar - SYSMET(CXBORDER));
  378. lprc->right = lprc->left + (int)(extent.cx);
  379. break;
  380. case LOBYTE(BFRIGHT):
  381. lprc->right -= (gpsi->cxSysFontChar - SYSMET(CXBORDER));
  382. lprc->left = lprc->right - (int)(extent.cx);
  383. break;
  384. case LOBYTE(BFCENTER):
  385. lprc->left = (lprc->left + lprc->right - (int)(extent.cx)) / 2;
  386. lprc->right = lprc->left + (int)(extent.cx);
  387. break;
  388. }
  389. // Center aligned.
  390. lprc->bottom = lprc->top + extent.cy + SYSMET(CYEDGE);
  391. break;
  392. case CBR_GROUPFRAME:
  393. PSMGetTextExtent(hdc, (LPWSTR)szOneChar, 1, &extent);
  394. lprc->top += extent.cy / 2;
  395. break;
  396. }
  397. }
  398. /***************************************************************************\
  399. *
  400. * BtnGetMultiExtent()
  401. *
  402. * Calculates button text extent, given alignment flags.
  403. *
  404. \***************************************************************************/
  405. void BNMultiExtent(
  406. WORD wFlags,
  407. HDC hdc,
  408. LPRECT lprcMax,
  409. LPWSTR lpsz,
  410. int cch,
  411. PINT pcx,
  412. PINT pcy)
  413. {
  414. RECT rcT;
  415. UINT dtFlags = DT_CALCRECT | DT_WORDBREAK | DT_EDITCONTROL;
  416. CopyRect(&rcT, lprcMax);
  417. // Note that since we're just calculating the maximum dimensions,
  418. // left-justification and top-justification are not important.
  419. // Also, remember to leave margins horz and vert that follow our rules
  420. // in DrawBtnText().
  421. InflateRect(&rcT, -SYSMET(CXEDGE), -SYSMET(CYBORDER));
  422. if ((wFlags & LOBYTE(BFHORZMASK)) == LOBYTE(BFCENTER))
  423. dtFlags |= DT_CENTER;
  424. if ((wFlags & LOBYTE(BFHORZMASK)) == LOBYTE(BFRIGHT))
  425. dtFlags |= DT_RIGHT;
  426. if ((wFlags & LOBYTE(BFVERTMASK)) == LOBYTE(BFVCENTER))
  427. dtFlags |= DT_VCENTER;
  428. if ((wFlags & LOBYTE(BFVERTMASK)) == LOBYTE(BFBOTTOM))
  429. dtFlags |= DT_BOTTOM;
  430. DrawTextExW(hdc, lpsz, cch, &rcT, dtFlags, NULL);
  431. if (pcx)
  432. *pcx = rcT.right-rcT.left;
  433. if (pcy)
  434. *pcy = rcT.bottom-rcT.top;
  435. }
  436. /***************************************************************************\
  437. *
  438. * BtnMultiDraw()
  439. *
  440. * Draws multiline button text
  441. *
  442. \***************************************************************************/
  443. BOOL CALLBACK BNMultiDraw(
  444. HDC hdc,
  445. LPARAM lData,
  446. WPARAM wData,
  447. int cx,
  448. int cy)
  449. {
  450. LPBTNDATA lpbd = (LPBTNDATA)lData;
  451. int cch = (int)wData;
  452. RECT rcT;
  453. UINT dtFlags = DT_WORDBREAK | DT_EDITCONTROL;
  454. PBUTN pbutn = lpbd->pbutn;
  455. if (TestWF(pbutn->spwnd, WEFPUIACCELHIDDEN)) {
  456. dtFlags |= DT_HIDEPREFIX;
  457. } else if (pbutn->fPaintKbdCuesOnly){
  458. dtFlags |= DT_PREFIXONLY;
  459. }
  460. if (TestWF(pbutn->spwnd, WEFRIGHT)) {
  461. dtFlags |= DT_RIGHT;
  462. }
  463. rcT.left = 0;
  464. rcT.top = 0;
  465. rcT.right = cx;
  466. rcT.bottom = cy;
  467. // Horizontal alignment
  468. UserAssert(DT_LEFT == 0);
  469. switch (lpbd->wFlags & LOBYTE(BFHORZMASK)) {
  470. case LOBYTE(BFCENTER):
  471. dtFlags |= DT_CENTER;
  472. break;
  473. case LOBYTE(BFRIGHT):
  474. dtFlags |= DT_RIGHT;
  475. break;
  476. }
  477. // Vertical alignment
  478. UserAssert(DT_TOP == 0);
  479. switch (lpbd->wFlags & LOBYTE(BFVERTMASK)) {
  480. case LOBYTE(BFVCENTER):
  481. dtFlags |= DT_VCENTER;
  482. break;
  483. case LOBYTE(BFBOTTOM):
  484. dtFlags |= DT_BOTTOM;
  485. break;
  486. }
  487. DrawTextExW(hdc, lpbd->lpsz, cch, &rcT, dtFlags, NULL);
  488. return(TRUE);
  489. }
  490. /***************************************************************************\
  491. * xxxBNSetCapture
  492. *
  493. * History:
  494. \***************************************************************************/
  495. BOOL xxxBNSetCapture(
  496. PBUTN pbutn,
  497. UINT codeMouse)
  498. {
  499. PWND pwnd = pbutn->spwnd;
  500. BUTTONSTATE(pbutn) |= codeMouse;
  501. CheckLock(pwnd);
  502. if (!(BUTTONSTATE(pbutn) & BST_CAPTURED)) {
  503. NtUserSetCapture(HWq(pwnd));
  504. BUTTONSTATE(pbutn) |= BST_CAPTURED;
  505. /*
  506. * To prevent redundant CLICK messages, we set the INCLICK bit so
  507. * the WM_SETFOCUS code will not do a xxxButtonNotifyParent(BN_CLICKED).
  508. */
  509. BUTTONSTATE(pbutn) |= BST_INCLICK;
  510. NtUserSetFocus(HWq(pwnd));
  511. BUTTONSTATE(pbutn) &= ~BST_INCLICK;
  512. }
  513. return(BUTTONSTATE(pbutn) & BST_CAPTURED);
  514. }
  515. /***************************************************************************\
  516. * xxxButtonNotifyParent
  517. *
  518. * History:
  519. \***************************************************************************/
  520. void xxxButtonNotifyParent(
  521. PWND pwnd,
  522. UINT code)
  523. {
  524. TL tlpwndParent;
  525. PWND pwndParent; // Parent if it exists
  526. CheckLock(pwnd);
  527. if (pwnd->spwndParent)
  528. pwndParent = REBASEPWND(pwnd, spwndParent);
  529. else
  530. pwndParent = pwnd;
  531. /*
  532. * Note: A button's pwnd->spmenu is used to store the control ID
  533. */
  534. ThreadLock(pwndParent, &tlpwndParent);
  535. SendMessage(HW(pwndParent), WM_COMMAND,
  536. MAKELONG(PTR_TO_ID(pwnd->spmenu), code), (LPARAM)HWq(pwnd));
  537. ThreadUnlock(&tlpwndParent);
  538. }
  539. /***************************************************************************\
  540. * xxxBNReleaseCapture
  541. *
  542. * History:
  543. \***************************************************************************/
  544. void xxxBNReleaseCapture(
  545. PBUTN pbutn,
  546. BOOL fCheck)
  547. {
  548. PWND pwndT;
  549. UINT check;
  550. BOOL fNotifyParent = FALSE;
  551. TL tlpwndT;
  552. PWND pwnd = pbutn->spwnd;
  553. CheckLock(pwnd);
  554. if (BUTTONSTATE(pbutn) & BST_PUSHED) {
  555. SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE);
  556. if (fCheck) {
  557. switch (TestWF(pwnd, BFTYPEMASK)) {
  558. case BS_AUTOCHECKBOX:
  559. case BS_AUTO3STATE:
  560. check = (UINT)((BUTTONSTATE(pbutn) & BST_CHECKMASK) + 1);
  561. if (check > (UINT)(TestWF(pwnd, BFTYPEMASK) == BS_AUTO3STATE? BST_INDETERMINATE : BST_CHECKED)) {
  562. check = BST_UNCHECKED;
  563. }
  564. SendMessageWorker(pwnd, BM_SETCHECK, check, 0, FALSE);
  565. break;
  566. case BS_AUTORADIOBUTTON:
  567. pwndT = pwnd;
  568. do {
  569. ThreadLock(pwndT, &tlpwndT);
  570. if ((UINT)SendMessage(HW(pwndT), WM_GETDLGCODE, 0, 0L) &
  571. DLGC_RADIOBUTTON) {
  572. SendMessage(HW(pwndT), BM_SETCHECK, (pwnd == pwndT), 0L);
  573. }
  574. pwndT = _GetNextDlgGroupItem(REBASEPWND(pwndT, spwndParent),
  575. pwndT, FALSE);
  576. ThreadUnlock(&tlpwndT);
  577. } while (pwndT != pwnd);
  578. }
  579. fNotifyParent = TRUE;
  580. }
  581. }
  582. if (BUTTONSTATE(pbutn) & BST_CAPTURED) {
  583. BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE);
  584. NtUserReleaseCapture();
  585. }
  586. if (fNotifyParent) {
  587. /*
  588. * We have to do the notification after setting the buttonstate bits.
  589. */
  590. xxxButtonNotifyParent(pwnd, BN_CLICKED);
  591. }
  592. }
  593. /***************************************************************************\
  594. *
  595. * DrawBtnText()
  596. *
  597. * Draws text of button.
  598. *
  599. \***************************************************************************/
  600. void xxxBNDrawText(
  601. PBUTN pbutn,
  602. HDC hdc,
  603. BOOL dbt,
  604. BOOL fDepress)
  605. {
  606. RECT rc;
  607. HBRUSH hbr;
  608. int x;
  609. int y;
  610. int cx;
  611. int cy;
  612. LPWSTR lpName;
  613. BYTE bStyle;
  614. int cch;
  615. UINT dsFlags;
  616. BTNDATA bdt;
  617. UINT pbfPush;
  618. PWND pwnd = pbutn->spwnd;
  619. bStyle = TestWF(pwnd, BFTYPEMASK);
  620. if (bStyle > sizeof(mpStyleCbr)) {
  621. RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid button style");
  622. } else if ((bStyle == LOBYTE(BS_GROUPBOX)) && (dbt == DBT_FOCUS))
  623. return;
  624. pbfPush = IsPushButton(pwnd);
  625. if (pbfPush) {
  626. BNCalcRect(pwnd, hdc, &rc, CBR_PUSHBUTTON, pbfPush);
  627. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  628. //
  629. // This is because we don't have WM_CTLCOLOR / CTLCOLOR_BTN
  630. // actually set up the button colors. For old apps, CTLCOLOR_BTN
  631. // needs to work like CTLCOLOR_STATIC.
  632. //
  633. SetBkColor(hdc, SYSRGB(3DFACE));
  634. SetTextColor(hdc, SYSRGB(BTNTEXT));
  635. hbr = SYSHBR(BTNTEXT);
  636. } else {
  637. BNCalcRect(pwnd, hdc, &rc, mpStyleCbr[bStyle], pbfPush);
  638. // Skip stuff for ownerdraw buttons, since we aren't going to
  639. // draw text/image.
  640. if (bStyle == LOBYTE(BS_OWNERDRAW))
  641. goto DrawFocus;
  642. else
  643. hbr = SYSHBR(WINDOWTEXT);
  644. }
  645. // Alignment
  646. bdt.wFlags = GetAlignment(pwnd);
  647. bdt.pbutn = pbutn;
  648. // Bail if we have nothing to draw
  649. if (TestWF(pwnd, BFBITMAP)) {
  650. BITMAP bmp;
  651. // Bitmap button
  652. if (!pbutn->hImage)
  653. return;
  654. GetObject(pbutn->hImage, sizeof(BITMAP), &bmp);
  655. cx = bmp.bmWidth;
  656. cy = bmp.bmHeight;
  657. dsFlags = DST_BITMAP;
  658. goto UseImageForName;
  659. } else if (TestWF(pwnd, BFICON)) {
  660. // Icon button
  661. if (!pbutn->hImage)
  662. return;
  663. NtUserGetIconSize(pbutn->hImage, 0, &cx, &cy);
  664. cy /= 2; // The bitmap height is half because a Mask is present in NT
  665. dsFlags = DST_ICON;
  666. UseImageForName:
  667. lpName = (LPWSTR)pbutn->hImage;
  668. cch = TRUE;
  669. } else {
  670. // Text button
  671. if (!pwnd->strName.Length)
  672. return;
  673. lpName = REBASE(pwnd, strName.Buffer);
  674. cch = pwnd->strName.Length / sizeof(WCHAR);
  675. if (TestWF(pwnd, BFMULTILINE)) {
  676. bdt.lpsz = lpName;
  677. BNMultiExtent(bdt.wFlags, hdc, &rc, lpName, cch, &cx, &cy);
  678. lpName = (LPWSTR)(LPBTNDATA)&bdt;
  679. dsFlags = DST_COMPLEX;
  680. } else {
  681. SIZE size;
  682. PSMGetTextExtent(hdc, lpName, cch, &size);
  683. cx = size.cx;
  684. cy = size.cy;
  685. /*
  686. * If the control doesn't need underlines, set DST_HIDEPREFIX and
  687. * also do not show the focus indicator
  688. */
  689. dsFlags = DST_PREFIXTEXT;
  690. if (TestWF(pwnd, WEFPUIACCELHIDDEN)) {
  691. dsFlags |= DSS_HIDEPREFIX;
  692. } else if (pbutn->fPaintKbdCuesOnly) {
  693. dsFlags |= DSS_PREFIXONLY;
  694. }
  695. }
  696. //
  697. // Add on a pixel or two of vertical space to make centering
  698. // happier. That way underline won't abut focus rect unless
  699. // spacing is really tight.
  700. //
  701. cy++;
  702. }
  703. //
  704. // ALIGNMENT
  705. //
  706. // Horizontal
  707. switch (bdt.wFlags & LOBYTE(BFHORZMASK)) {
  708. //
  709. // For left & right justified, we leave a margin of CXEDGE on either
  710. // side for eye-pleasing space.
  711. //
  712. case LOBYTE(BFLEFT):
  713. x = rc.left + SYSMET(CXEDGE);
  714. break;
  715. case LOBYTE(BFRIGHT):
  716. x = rc.right - cx - SYSMET(CXEDGE);
  717. break;
  718. default:
  719. x = (rc.left + rc.right - cx) / 2;
  720. break;
  721. }
  722. // Vertical
  723. switch (bdt.wFlags & LOBYTE(BFVERTMASK)) {
  724. //
  725. // For top & bottom justified, we leave a margin of CYBORDER on
  726. // either side for more eye-pleasing space.
  727. //
  728. case LOBYTE(BFTOP):
  729. y = rc.top + SYSMET(CYBORDER);
  730. break;
  731. case LOBYTE(BFBOTTOM):
  732. y = rc.bottom - cy - SYSMET(CYBORDER);
  733. break;
  734. default:
  735. y = (rc.top + rc.bottom - cy) / 2;
  736. break;
  737. }
  738. //
  739. // Draw the text
  740. //
  741. if (dbt & DBT_TEXT) {
  742. //
  743. // This isn't called for USER buttons.
  744. //
  745. UserAssert(bStyle != LOBYTE(BS_USERBUTTON));
  746. if (fDepress) {
  747. x += SYSMET(CXBORDER);
  748. y += SYSMET(CYBORDER);
  749. }
  750. if (TestWF(pwnd, WFDISABLED)) {
  751. UserAssert(HIBYTE(BFICON) == HIBYTE(BFBITMAP));
  752. if (SYSMET(SLOWMACHINE) &&
  753. !TestWF(pwnd, BFICON | BFBITMAP) &&
  754. (GetBkColor(hdc) != SYSRGB(GRAYTEXT)))
  755. {
  756. // Perf && consistency with menus, statics
  757. SetTextColor(hdc, SYSRGB(GRAYTEXT));
  758. }
  759. else
  760. dsFlags |= DSS_DISABLED;
  761. }
  762. //
  763. // Use transparent mode for checked push buttons since we're going to
  764. // fill background with dither.
  765. //
  766. if (pbfPush) {
  767. switch (BUTTONSTATE(pbutn) & BST_CHECKMASK) {
  768. case BST_INDETERMINATE:
  769. hbr = SYSHBR(GRAYTEXT);
  770. dsFlags |= DSS_MONO;
  771. // FALL THRU
  772. case BST_CHECKED:
  773. // Drawing on dithered background...
  774. SetBkMode(hdc, TRANSPARENT);
  775. break;
  776. }
  777. }
  778. //
  779. // Use brush and colors currently selected into hdc when we grabbed
  780. // color
  781. //
  782. DrawState(hdc, hbr, BNMultiDraw, (LPARAM)lpName,
  783. (WPARAM)cch, x, y, cx, cy,
  784. dsFlags);
  785. }
  786. // Draw focus rect.
  787. //
  788. // This can get called for OWNERDRAW and USERDRAW buttons. However, only
  789. // OWNERDRAW buttons let the owner change the drawing of the focus button.
  790. DrawFocus:
  791. if (dbt & DBT_FOCUS) {
  792. if (bStyle == LOBYTE(BS_OWNERDRAW)) {
  793. // For ownerdraw buttons, this is only called in response to a
  794. // WM_SETFOCUS or WM_KILL FOCUS message. So, we can check the
  795. // new state of the focus by looking at the BUTTONSTATE bits
  796. // which are set before this procedure is called.
  797. xxxBNOwnerDraw(pbutn, hdc, ODA_FOCUS);
  798. } else {
  799. // Don't draw the focus if underlines are not turned on
  800. if (!TestWF(pwnd, WEFPUIFOCUSHIDDEN)) {
  801. // Let focus rect always hug edge of push buttons. We already
  802. // have the client area setup for push buttons, so we don't have
  803. // to do anything.
  804. if (!pbfPush) {
  805. RECT rcClient;
  806. _GetClientRect(pwnd, &rcClient);
  807. if (bStyle == LOBYTE(BS_USERBUTTON))
  808. CopyRect(&rc, &rcClient);
  809. else {
  810. // Try to leave a border all around text. That causes
  811. // focus to hug text.
  812. rc.top = max(rcClient.top, y-SYSMET(CYBORDER));
  813. rc.bottom = min(rcClient.bottom, rc.top + SYSMET(CYEDGE) + cy);
  814. rc.left = max(rcClient.left, x-SYSMET(CXBORDER));
  815. rc.right = min(rcClient.right, rc.left + SYSMET(CXEDGE) + cx);
  816. }
  817. } else
  818. InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
  819. // Are back & fore colors set properly?
  820. DrawFocusRect(hdc, &rc);
  821. }
  822. }
  823. }
  824. }
  825. /***************************************************************************\
  826. *
  827. * DrawCheck()
  828. *
  829. \***************************************************************************/
  830. void xxxButtonDrawCheck(
  831. PBUTN pbutn,
  832. HDC hdc,
  833. HBRUSH hbr)
  834. {
  835. RECT rc;
  836. int bm;
  837. UINT flags;
  838. BOOL fDoubleBlt = FALSE;
  839. TL tlpwnd;
  840. PWND pwnd = pbutn->spwnd;
  841. PWND pwndParent;
  842. BNCalcRect(pwnd, hdc, &rc, CBR_CHECKBOX, 0);
  843. flags = 0;
  844. if (BUTTONSTATE(pbutn) & BST_CHECKMASK)
  845. flags |= DFCS_CHECKED;
  846. if (BUTTONSTATE(pbutn) & BST_PUSHED)
  847. flags |= DFCS_PUSHED;
  848. if (TestWF(pwnd, WFDISABLED))
  849. flags |= DFCS_INACTIVE;
  850. bm = OBI_CHECK;
  851. switch (TestWF(pwnd, BFTYPEMASK)) {
  852. case BS_AUTORADIOBUTTON:
  853. case BS_RADIOBUTTON:
  854. fDoubleBlt = TRUE;
  855. bm = OBI_RADIO;
  856. flags |= DFCS_BUTTONRADIO;
  857. break;
  858. case BS_3STATE:
  859. case BS_AUTO3STATE:
  860. if ((BUTTONSTATE(pbutn) & BST_CHECKMASK) == BST_INDETERMINATE) {
  861. bm = OBI_3STATE;
  862. flags |= DFCS_BUTTON3STATE;
  863. break;
  864. }
  865. // FALL THRU
  866. default:
  867. flags |= DFCS_BUTTONCHECK;
  868. break;
  869. }
  870. rc.right = rc.left + gpsi->oembmi[bm].cx;
  871. rc.bottom = rc.top + gpsi->oembmi[bm].cy;
  872. ThreadLockAlways(pwnd->spwndParent, &tlpwnd);
  873. pwndParent = REBASEPWND(pwnd, spwndParent);
  874. PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc);
  875. ThreadUnlock(&tlpwnd);
  876. if (TestWF(pwnd, BFFLAT) && gpsi->BitCount != 1) {
  877. flags |= DFCS_MONO | DFCS_FLAT;
  878. DrawFrameControl(hdc, &rc, DFC_BUTTON, flags);
  879. } else {
  880. switch (flags & (DFCS_CHECKED | DFCS_PUSHED | DFCS_INACTIVE))
  881. {
  882. case 0:
  883. break;
  884. case DFCS_CHECKED:
  885. bm += DOBI_CHECK;
  886. break;
  887. // These are mutually exclusive!
  888. case DFCS_PUSHED:
  889. case DFCS_INACTIVE:
  890. bm += DOBI_DOWN; // DOBI_DOWN == DOBI_INACTIVE
  891. break;
  892. case DFCS_CHECKED | DFCS_PUSHED:
  893. bm += DOBI_CHECKDOWN;
  894. break;
  895. case DFCS_CHECKED | DFCS_INACTIVE:
  896. bm += DOBI_CHECKDOWN + 1;
  897. break;
  898. }
  899. if (fDoubleBlt) {
  900. // This is a diamond-shaped radio button -- Blt with a mask so that
  901. // the exterior keeps the same color as the window's background
  902. DWORD clrTextSave = SetTextColor(hdc, 0x00000000L);
  903. DWORD clrBkSave = SetBkColor(hdc, 0x00FFFFFFL);
  904. POEMBITMAPINFO pOem = gpsi->oembmi + OBI_RADIOMASK;
  905. NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy,
  906. pOem->x, pOem->y, SRCAND);
  907. pOem = gpsi->oembmi + bm;
  908. NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy,
  909. pOem->x, pOem->y, SRCINVERT);
  910. SetTextColor(hdc, clrTextSave);
  911. SetBkColor(hdc, clrBkSave);
  912. } else {
  913. POEMBITMAPINFO pOem = gpsi->oembmi + bm;
  914. DWORD dwROP = 0;
  915. // We do not want to mirror the check box.
  916. if (MIRRORED_HDC(hdc)) {
  917. dwROP = NOMIRRORBITMAP;
  918. }
  919. NtUserBitBltSysBmp(hdc, rc.left, rc.top, pOem->cx, pOem->cy,
  920. pOem->x, pOem->y, SRCCOPY | dwROP);
  921. }
  922. }
  923. }
  924. /***************************************************************************\
  925. * xxxButtonDrawNewState
  926. *
  927. * History:
  928. \***************************************************************************/
  929. void xxxButtonDrawNewState(
  930. PBUTN pbutn,
  931. HDC hdc,
  932. HBRUSH hbr,
  933. UINT sOld)
  934. {
  935. PWND pwnd = pbutn->spwnd;
  936. CheckLock(pwnd);
  937. if (sOld != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED)) {
  938. UINT pbfPush;
  939. pbfPush = IsPushButton(pwnd);
  940. switch (TestWF(pwnd, BFTYPEMASK)) {
  941. case BS_GROUPBOX:
  942. case BS_OWNERDRAW:
  943. break;
  944. default:
  945. if (!pbfPush) {
  946. xxxButtonDrawCheck(pbutn, hdc, hbr);
  947. break;
  948. }
  949. case BS_PUSHBUTTON:
  950. case BS_DEFPUSHBUTTON:
  951. case BS_PUSHBOX:
  952. xxxDrawButton(pbutn, hdc, pbfPush);
  953. break;
  954. }
  955. }
  956. }
  957. /***************************************************************************\
  958. *
  959. * DrawButton()
  960. *
  961. * Draws push-like button with text
  962. *
  963. \***************************************************************************/
  964. void xxxDrawButton(
  965. PBUTN pbutn,
  966. HDC hdc,
  967. UINT pbfPush)
  968. {
  969. RECT rc;
  970. UINT flags = 0;
  971. UINT state = 0;
  972. PWND pwnd = pbutn->spwnd;
  973. if (BUTTONSTATE(pbutn) & BST_PUSHED)
  974. state |= DFCS_PUSHED;
  975. if (!pbutn->fPaintKbdCuesOnly) {
  976. if (BUTTONSTATE(pbutn) & BST_CHECKMASK)
  977. state |= DFCS_CHECKED;
  978. if (TestWF(pwnd, WFWIN40COMPAT))
  979. flags = BF_SOFT;
  980. if (TestWF(pwnd, BFFLAT))
  981. flags |= BF_FLAT | BF_MONO;
  982. _GetClientRect(pwnd, &rc);
  983. if (pbfPush & PBF_DEFAULT) {
  984. DrawFrame(hdc, &rc, 1, DF_WINDOWFRAME);
  985. InflateRect(&rc, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
  986. if (state & DFCS_PUSHED)
  987. flags |= BF_FLAT;
  988. }
  989. DrawPushButton(hdc, &rc, state, flags);
  990. }
  991. xxxBNDrawText(pbutn, hdc, DBT_TEXT | (BUTTONSTATE(pbutn) &
  992. BST_FOCUS ? DBT_FOCUS : 0), (state & DFCS_PUSHED));
  993. }
  994. /***************************************************************************\
  995. * xxxBNPaint
  996. *
  997. * History:
  998. \***************************************************************************/
  999. void xxxBNPaint(
  1000. PBUTN pbutn,
  1001. HDC hdc)
  1002. {
  1003. UINT bsWnd;
  1004. RECT rc;
  1005. HBRUSH hbr;
  1006. HBRUSH hbrBtnSave;
  1007. TL tlpwndParent;
  1008. UINT pbfPush;
  1009. PWND pwnd = pbutn->spwnd;
  1010. PWND pwndParent;
  1011. CheckLock(pwnd);
  1012. hbr = xxxBNInitDC(pbutn, hdc);
  1013. bsWnd = TestWF(pwnd, BFTYPEMASK);
  1014. pbfPush = IsPushButton(pwnd);
  1015. if (!pbfPush && !pbutn->fPaintKbdCuesOnly) {
  1016. _GetClientRect(pwnd, &rc);
  1017. if ((bsWnd != LOBYTE(BS_OWNERDRAW)) &&
  1018. (bsWnd != LOBYTE(BS_GROUPBOX))) {
  1019. ThreadLock(pwnd->spwndParent, &tlpwndParent);
  1020. pwndParent = REBASEPWND(pwnd, spwndParent);
  1021. PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc);
  1022. ThreadUnlock(&tlpwndParent);
  1023. }
  1024. hbrBtnSave = SelectObject(hdc, hbr);
  1025. }
  1026. switch (bsWnd) {
  1027. case BS_CHECKBOX:
  1028. case BS_RADIOBUTTON:
  1029. case BS_AUTORADIOBUTTON:
  1030. case BS_3STATE:
  1031. case BS_AUTOCHECKBOX:
  1032. case BS_AUTO3STATE:
  1033. if (!pbfPush) {
  1034. xxxBNDrawText(pbutn, hdc,
  1035. DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
  1036. if (!pbutn->fPaintKbdCuesOnly) {
  1037. xxxButtonDrawCheck(pbutn, hdc, hbr);
  1038. }
  1039. break;
  1040. }
  1041. /*
  1042. * Fall through for PUSHLIKE buttons
  1043. */
  1044. case BS_PUSHBUTTON:
  1045. case BS_DEFPUSHBUTTON:
  1046. xxxDrawButton(pbutn, hdc, pbfPush);
  1047. break;
  1048. case BS_PUSHBOX:
  1049. xxxBNDrawText(pbutn, hdc,
  1050. DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
  1051. xxxButtonDrawNewState(pbutn, hdc, hbr, 0);
  1052. break;
  1053. case BS_USERBUTTON:
  1054. xxxButtonNotifyParent(pwnd, BN_PAINT);
  1055. if (BUTTONSTATE(pbutn) & BST_PUSHED) {
  1056. xxxButtonNotifyParent(pwnd, BN_PUSHED);
  1057. }
  1058. if (TestWF(pwnd, WFDISABLED)) {
  1059. xxxButtonNotifyParent(pwnd, BN_DISABLE);
  1060. }
  1061. if (BUTTONSTATE(pbutn) & BST_FOCUS) {
  1062. xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE);
  1063. }
  1064. break;
  1065. case BS_OWNERDRAW:
  1066. xxxBNOwnerDraw(pbutn, hdc, ODA_DRAWENTIRE);
  1067. break;
  1068. case BS_GROUPBOX:
  1069. if (!pbutn->fPaintKbdCuesOnly) {
  1070. BNCalcRect(pwnd, hdc, &rc, CBR_GROUPFRAME, 0);
  1071. DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT |
  1072. (TestWF(pwnd, BFFLAT) ? BF_FLAT | BF_MONO : 0));
  1073. BNCalcRect(pwnd, hdc, &rc, CBR_GROUPTEXT, 0);
  1074. ThreadLock(pwnd->spwndParent, &tlpwndParent);
  1075. pwndParent = REBASEPWND(pwnd, spwndParent);
  1076. PaintRect(HW(pwndParent), HWq(pwnd), hdc, hbr, &rc);
  1077. ThreadUnlock(&tlpwndParent);
  1078. }
  1079. /*
  1080. * FillRect(hdc, &rc, hbrBtn);
  1081. */
  1082. xxxBNDrawText(pbutn, hdc, DBT_TEXT, FALSE);
  1083. break;
  1084. }
  1085. if (!pbfPush)
  1086. SelectObject(hdc, hbrBtnSave);
  1087. /*
  1088. * Release the font which may have been loaded by xxxButtonInitDC.
  1089. */
  1090. if (pbutn->hFont) {
  1091. SelectObject(hdc, ghFontSys);
  1092. }
  1093. }
  1094. /***************************************************************************\
  1095. * RepaintButton
  1096. *
  1097. \***************************************************************************/
  1098. void RepaintButton (PBUTN pbutn)
  1099. {
  1100. HDC hdc = xxxBNGetDC(pbutn, NULL);
  1101. if (hdc != NULL) {
  1102. xxxBNPaint(pbutn, hdc);
  1103. BNReleaseDC(pbutn, hdc);
  1104. }
  1105. }
  1106. /***************************************************************************\
  1107. * ButtonWndProc
  1108. *
  1109. * WndProc for buttons, check boxes, etc.
  1110. *
  1111. * History:
  1112. \***************************************************************************/
  1113. LRESULT APIENTRY ButtonWndProcWorker(
  1114. PWND pwnd,
  1115. UINT message,
  1116. WPARAM wParam,
  1117. LPARAM lParam,
  1118. DWORD fAnsi)
  1119. {
  1120. HWND hwnd = HWq(pwnd);
  1121. UINT bsWnd;
  1122. UINT wOldState;
  1123. RECT rc;
  1124. POINT pt;
  1125. HDC hdc;
  1126. HBRUSH hbr;
  1127. PAINTSTRUCT ps;
  1128. TL tlpwndParent;
  1129. PBUTN pbutn;
  1130. PWND pwndParent;
  1131. static BOOL fInit = TRUE;
  1132. LONG lResult;
  1133. CheckLock(pwnd);
  1134. bsWnd = TestWF(pwnd, BFTYPEMASK);
  1135. VALIDATECLASSANDSIZE(pwnd, FNID_BUTTON);
  1136. INITCONTROLLOOKASIDE(&ButtonLookaside, BUTN, spwnd, 8);
  1137. /*
  1138. * Get the pbutn for the given window now since we will use it a lot in
  1139. * various handlers. This was stored using SetWindowLong(hwnd,0,pbutn) when
  1140. * we initially created the button control.
  1141. */
  1142. pbutn = ((PBUTNWND)pwnd)->pbutn;
  1143. switch (message) {
  1144. case WM_NCHITTEST:
  1145. if (bsWnd == LOBYTE(BS_GROUPBOX)) {
  1146. return (LONG)HTTRANSPARENT;
  1147. } else {
  1148. goto CallDWP;
  1149. }
  1150. case WM_ERASEBKGND:
  1151. if (bsWnd == LOBYTE(BS_OWNERDRAW)) {
  1152. /*
  1153. * Handle erase background for owner draw buttons.
  1154. */
  1155. _GetClientRect(pwnd, &rc);
  1156. ThreadLock(pwnd->spwndParent, &tlpwndParent);
  1157. pwndParent = REBASEPWND(pwnd, spwndParent);
  1158. PaintRect(HW(pwndParent), hwnd, (HDC)wParam, (HBRUSH)CTLCOLOR_BTN, &rc);
  1159. ThreadUnlock(&tlpwndParent);
  1160. }
  1161. /*
  1162. * Do nothing for other buttons, but don't let DefWndProc() do it
  1163. * either. It will be erased in xxxBNPaint().
  1164. */
  1165. return (LONG)TRUE;
  1166. case WM_PRINTCLIENT:
  1167. xxxBNPaint(pbutn, (HDC)wParam);
  1168. break;
  1169. case WM_PAINT:
  1170. /*
  1171. * If wParam != NULL, then this is a subclassed paint.
  1172. */
  1173. if ((hdc = (HDC)wParam) == NULL)
  1174. hdc = NtUserBeginPaint(hwnd, &ps);
  1175. if (IsVisible(pwnd))
  1176. xxxBNPaint(pbutn, hdc);
  1177. if (!wParam)
  1178. NtUserEndPaint(hwnd, &ps);
  1179. break;
  1180. case WM_SETFOCUS:
  1181. BUTTONSTATE(pbutn) |= BST_FOCUS;
  1182. if ((hdc = xxxBNGetDC(pbutn, NULL)) != NULL) {
  1183. xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE);
  1184. BNReleaseDC(pbutn, hdc);
  1185. }
  1186. if (TestWF(pwnd, BFNOTIFY))
  1187. xxxButtonNotifyParent(pwnd, BN_SETFOCUS);
  1188. if (!(BUTTONSTATE(pbutn) & BST_INCLICK)) {
  1189. switch (bsWnd) {
  1190. case LOBYTE(BS_RADIOBUTTON):
  1191. case LOBYTE(BS_AUTORADIOBUTTON):
  1192. if (!(BUTTONSTATE(pbutn) & BST_DONTCLICK)) {
  1193. if (!(BUTTONSTATE(pbutn) & BST_CHECKMASK)) {
  1194. xxxButtonNotifyParent(pwnd, BN_CLICKED);
  1195. }
  1196. }
  1197. break;
  1198. }
  1199. }
  1200. break;
  1201. case WM_GETDLGCODE:
  1202. switch (bsWnd) {
  1203. case LOBYTE(BS_DEFPUSHBUTTON):
  1204. wParam = DLGC_DEFPUSHBUTTON;
  1205. break;
  1206. case LOBYTE(BS_PUSHBUTTON):
  1207. case LOBYTE(BS_PUSHBOX):
  1208. wParam = DLGC_UNDEFPUSHBUTTON;
  1209. break;
  1210. case LOBYTE(BS_AUTORADIOBUTTON):
  1211. case LOBYTE(BS_RADIOBUTTON):
  1212. wParam = DLGC_RADIOBUTTON;
  1213. break;
  1214. case LOBYTE(BS_GROUPBOX):
  1215. return (LONG)DLGC_STATIC;
  1216. case LOBYTE(BS_CHECKBOX):
  1217. case LOBYTE(BS_AUTOCHECKBOX):
  1218. /*
  1219. * If this is a char that is a '=/+', or '-', we want it
  1220. */
  1221. if (lParam && ((LPMSG)lParam)->message == WM_CHAR) {
  1222. switch (wParam) {
  1223. case TEXT('='):
  1224. case TEXT('+'):
  1225. case TEXT('-'):
  1226. wParam = DLGC_WANTCHARS;
  1227. break;
  1228. default:
  1229. wParam = 0;
  1230. }
  1231. } else {
  1232. wParam = 0;
  1233. }
  1234. break;
  1235. default:
  1236. wParam = 0;
  1237. }
  1238. return (LONG)(wParam | DLGC_BUTTON);
  1239. case WM_CAPTURECHANGED:
  1240. if (BUTTONSTATE(pbutn) & BST_CAPTURED) {
  1241. // Unwittingly, we've been kicked out of capture,
  1242. // so undepress etc.
  1243. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  1244. SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE);
  1245. BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE);
  1246. }
  1247. break;
  1248. case WM_KILLFOCUS:
  1249. /*
  1250. * If we are losing the focus and we are in "capture mode", click
  1251. * the button. This allows tab and space keys to overlap for
  1252. * fast toggle of a series of buttons.
  1253. */
  1254. if (BUTTONSTATE(pbutn) & BST_MOUSE) {
  1255. /*
  1256. * If for some reason we are killing the focus, and we have the
  1257. * mouse captured, don't notify the parent we got clicked. This
  1258. * breaks Omnis Quartz otherwise.
  1259. */
  1260. SendMessageWorker(pwnd, BM_SETSTATE, FALSE, 0, FALSE);
  1261. }
  1262. xxxBNReleaseCapture(pbutn, TRUE);
  1263. BUTTONSTATE(pbutn) &= ~BST_FOCUS;
  1264. if ((hdc = xxxBNGetDC(pbutn, NULL)) != NULL) {
  1265. xxxBNDrawText(pbutn, hdc, DBT_FOCUS, FALSE);
  1266. BNReleaseDC(pbutn, hdc);
  1267. }
  1268. if (TestWF(pwnd, BFNOTIFY))
  1269. xxxButtonNotifyParent(pwnd, BN_KILLFOCUS);
  1270. /*
  1271. * Since the bold border around the defpushbutton is done by
  1272. * someone else, we need to invalidate the rect so that the
  1273. * focus rect is repainted properly.
  1274. */
  1275. NtUserInvalidateRect(hwnd, NULL, FALSE);
  1276. break;
  1277. case WM_LBUTTONDBLCLK:
  1278. /*
  1279. * Double click messages are recognized for BS_RADIOBUTTON,
  1280. * BS_USERBUTTON, and BS_OWNERDRAW styles. For all other buttons,
  1281. * double click is handled like a normal button down.
  1282. */
  1283. switch (bsWnd) {
  1284. default:
  1285. if (!TestWF(pwnd, BFNOTIFY))
  1286. goto btnclick;
  1287. case LOBYTE(BS_USERBUTTON):
  1288. case LOBYTE(BS_RADIOBUTTON):
  1289. case LOBYTE(BS_OWNERDRAW):
  1290. xxxButtonNotifyParent(pwnd, BN_DOUBLECLICKED);
  1291. break;
  1292. }
  1293. break;
  1294. case WM_LBUTTONUP:
  1295. if (BUTTONSTATE(pbutn) & BST_MOUSE) {
  1296. xxxBNReleaseCapture(pbutn, TRUE);
  1297. }
  1298. break;
  1299. case WM_MOUSEMOVE:
  1300. if (!(BUTTONSTATE(pbutn) & BST_MOUSE)) {
  1301. break;
  1302. }
  1303. /*
  1304. *** FALL THRU **
  1305. */
  1306. case WM_LBUTTONDOWN:
  1307. btnclick:
  1308. if (xxxBNSetCapture(pbutn, BST_MOUSE)) {
  1309. _GetClientRect(pwnd, &rc);
  1310. POINTSTOPOINT(pt, lParam);
  1311. SendMessageWorker(pwnd, BM_SETSTATE, PtInRect(&rc, pt), 0, FALSE);
  1312. }
  1313. break;
  1314. case WM_CHAR:
  1315. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  1316. goto CallDWP;
  1317. if (bsWnd != LOBYTE(BS_CHECKBOX) &&
  1318. bsWnd != LOBYTE(BS_AUTOCHECKBOX))
  1319. goto CallDWP;
  1320. switch (wParam) {
  1321. case TEXT('+'):
  1322. case TEXT('='):
  1323. wParam = 1; // we must Set the check mark on.
  1324. goto SetCheck;
  1325. case TEXT('-'):
  1326. wParam = 0; // Set the check mark off.
  1327. SetCheck:
  1328. // Must notify only if the check status changes
  1329. if ((WORD)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (WORD)wParam)
  1330. {
  1331. // We must check/uncheck only if it is AUTO
  1332. if (bsWnd == LOBYTE(BS_AUTOCHECKBOX))
  1333. {
  1334. if (xxxBNSetCapture(pbutn, 0))
  1335. {
  1336. SendMessageWorker(pwnd, BM_SETCHECK, wParam, 0, FALSE);
  1337. xxxBNReleaseCapture(pbutn, TRUE);
  1338. }
  1339. }
  1340. xxxButtonNotifyParent(pwnd, BN_CLICKED);
  1341. }
  1342. break;
  1343. default:
  1344. goto CallDWP;
  1345. }
  1346. break;
  1347. case BM_CLICK:
  1348. // Don't recurse into this code!
  1349. if (BUTTONSTATE(pbutn) & BST_INBMCLICK)
  1350. break;
  1351. BUTTONSTATE(pbutn) |= BST_INBMCLICK;
  1352. SendMessageWorker(pwnd, WM_LBUTTONDOWN, 0, 0, FALSE);
  1353. SendMessageWorker(pwnd, WM_LBUTTONUP, 0, 0, FALSE);
  1354. BUTTONSTATE(pbutn) &= ~BST_INBMCLICK;
  1355. /*
  1356. *** FALL THRU **
  1357. */
  1358. case WM_KEYDOWN:
  1359. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  1360. break;
  1361. if (wParam == VK_SPACE) {
  1362. if (xxxBNSetCapture(pbutn, 0)) {
  1363. SendMessageWorker(pwnd, BM_SETSTATE, TRUE, 0, FALSE);
  1364. }
  1365. } else {
  1366. xxxBNReleaseCapture(pbutn, FALSE);
  1367. }
  1368. break;
  1369. case WM_KEYUP:
  1370. case WM_SYSKEYUP:
  1371. if (BUTTONSTATE(pbutn) & BST_MOUSE) {
  1372. goto CallDWP;
  1373. }
  1374. /*
  1375. * Don't cancel the capture mode on the up of the tab in case the
  1376. * guy is overlapping tab and space keys.
  1377. */
  1378. if (wParam == VK_TAB) {
  1379. goto CallDWP;
  1380. }
  1381. /*
  1382. * WARNING: pwnd is history after this call!
  1383. */
  1384. xxxBNReleaseCapture(pbutn, (wParam == VK_SPACE));
  1385. if (message == WM_SYSKEYUP) {
  1386. goto CallDWP;
  1387. }
  1388. break;
  1389. case BM_GETSTATE:
  1390. return (LONG)BUTTONSTATE(pbutn);
  1391. case BM_SETSTATE:
  1392. wOldState = (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED);
  1393. if (wParam) {
  1394. BUTTONSTATE(pbutn) |= BST_PUSHED;
  1395. } else {
  1396. BUTTONSTATE(pbutn) &= ~BST_PUSHED;
  1397. }
  1398. if ((hdc = xxxBNGetDC(pbutn, &hbr)) != NULL) {
  1399. if (bsWnd == LOBYTE(BS_USERBUTTON)) {
  1400. xxxButtonNotifyParent(pwnd, (UINT)(wParam ? BN_PUSHED : BN_UNPUSHED));
  1401. } else if (bsWnd == LOBYTE(BS_OWNERDRAW)) {
  1402. if (wOldState != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED)) {
  1403. /*
  1404. * Only notify for drawing if state has changed..
  1405. */
  1406. xxxBNOwnerDraw(pbutn, hdc, ODA_SELECT);
  1407. }
  1408. } else {
  1409. xxxButtonDrawNewState(pbutn, hdc, hbr, wOldState);
  1410. }
  1411. BNReleaseDC(pbutn, hdc);
  1412. }
  1413. if (wOldState != (BOOL)(BUTTONSTATE(pbutn) & BST_PUSHED)) {
  1414. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  1415. }
  1416. break;
  1417. case BM_GETCHECK:
  1418. return (LONG)(BUTTONSTATE(pbutn) & BST_CHECKMASK);
  1419. case BM_SETCHECK:
  1420. switch (bsWnd) {
  1421. case LOBYTE(BS_RADIOBUTTON):
  1422. case LOBYTE(BS_AUTORADIOBUTTON):
  1423. if (wParam) {
  1424. SetWindowState(pwnd, WFTABSTOP);
  1425. } else {
  1426. ClearWindowState(pwnd, WFTABSTOP);
  1427. }
  1428. /*
  1429. *** FALL THRU **
  1430. */
  1431. case LOBYTE(BS_CHECKBOX):
  1432. case LOBYTE(BS_AUTOCHECKBOX):
  1433. if (wParam) {
  1434. wParam = 1;
  1435. }
  1436. goto CheckIt;
  1437. case LOBYTE(BS_3STATE):
  1438. case LOBYTE(BS_AUTO3STATE):
  1439. if (wParam > BST_INDETERMINATE) {
  1440. wParam = BST_INDETERMINATE;
  1441. }
  1442. CheckIt:
  1443. if ((UINT)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (UINT)wParam) {
  1444. BUTTONSTATE(pbutn) &= ~BST_CHECKMASK;
  1445. BUTTONSTATE(pbutn) |= (UINT)wParam;
  1446. if (!IsVisible(pwnd))
  1447. break;
  1448. if ((hdc = xxxBNGetDC(pbutn, &hbr)) != NULL) {
  1449. if (TestWF(pwnd, BFPUSHLIKE)) {
  1450. xxxDrawButton(pbutn, hdc, PBF_PUSHABLE);
  1451. } else {
  1452. xxxButtonDrawCheck(pbutn, hdc, hbr);
  1453. }
  1454. BNReleaseDC(pbutn, hdc);
  1455. }
  1456. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  1457. }
  1458. break;
  1459. }
  1460. break;
  1461. case BM_SETSTYLE:
  1462. NtUserAlterWindowStyle(hwnd, BS_TYPEMASK, (DWORD)wParam);
  1463. if (lParam) {
  1464. NtUserInvalidateRect(hwnd, NULL, TRUE);
  1465. }
  1466. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  1467. break;
  1468. case WM_SETTEXT:
  1469. /*
  1470. * In case the new group name is longer than the old name,
  1471. * this paints over the old name before repainting the group
  1472. * box with the new name.
  1473. */
  1474. if (bsWnd == LOBYTE(BS_GROUPBOX)) {
  1475. hdc = xxxBNGetDC(pbutn, &hbr);
  1476. if (hdc != NULL) {
  1477. BNCalcRect(pwnd, hdc, &rc, CBR_GROUPTEXT, 0);
  1478. NtUserInvalidateRect(hwnd, &rc, TRUE);
  1479. pwndParent = REBASEPWND(pwnd, spwndParent);
  1480. ThreadLock(pwnd->spwndParent, &tlpwndParent);
  1481. PaintRect(HW(pwndParent), hwnd, hdc, hbr, &rc);
  1482. ThreadUnlock(&tlpwndParent);
  1483. BNReleaseDC(pbutn, hdc);
  1484. }
  1485. }
  1486. lResult = _DefSetText(hwnd, (LPWSTR)lParam, (BOOL)fAnsi);
  1487. NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_WINDOW, INDEXID_CONTAINER);
  1488. goto DoEnable;
  1489. /*
  1490. *** FALL THRU **
  1491. */
  1492. case WM_ENABLE:
  1493. lResult = 0L;
  1494. DoEnable:
  1495. RepaintButton(pbutn);
  1496. return lResult;
  1497. case WM_SETFONT:
  1498. /*
  1499. * wParam - handle to the font
  1500. * lParam - if true, redraw else don't
  1501. */
  1502. BNSetFont(pbutn, (HFONT)wParam, (BOOL)(lParam != 0));
  1503. break;
  1504. case WM_GETFONT:
  1505. return (LRESULT)pbutn->hFont;
  1506. case BM_GETIMAGE:
  1507. case BM_SETIMAGE:
  1508. if (!IsValidImage(wParam, TestWF(pwnd, BFIMAGEMASK), IMAGE_BMMAX)) {
  1509. RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Invalid button image type");
  1510. } else {
  1511. HANDLE hOld = pbutn->hImage;
  1512. if (message == BM_SETIMAGE) {
  1513. pbutn->hImage = (HANDLE)lParam;
  1514. if (TestWF(pwnd, WFVISIBLE)) {
  1515. NtUserInvalidateRect(hwnd, NULL, TRUE);
  1516. }
  1517. }
  1518. return (LRESULT)hOld;
  1519. }
  1520. break;
  1521. case WM_NCDESTROY:
  1522. case WM_FINALDESTROY:
  1523. if (pbutn) {
  1524. Unlock(&pbutn->spwnd);
  1525. FreeLookasideEntry(&ButtonLookaside, pbutn);
  1526. }
  1527. NtUserSetWindowFNID(hwnd, FNID_CLEANEDUP_BIT);
  1528. break;
  1529. case WM_NCCREATE:
  1530. // Borland's OBEX has a button with style 0x98; We didn't strip
  1531. // these bits in win3.1 because we checked for 0x08.
  1532. // Stripping these bits cause a GP Fault in OBEX.
  1533. // For win3.1 guys, I use the old code to strip the style bits.
  1534. //
  1535. if (TestWF(pwnd, WFWIN31COMPAT)) {
  1536. if(((!TestWF(pwnd, WFWIN40COMPAT)) &&
  1537. (((LOBYTE(pwnd->style)) & (LOBYTE(~BS_LEFTTEXT))) == LOBYTE(BS_USERBUTTON))) ||
  1538. (TestWF(pwnd, WFWIN40COMPAT) &&
  1539. (bsWnd == LOBYTE(BS_USERBUTTON))))
  1540. {
  1541. // BS_USERBUTTON is no longer allowed for 3.1 and beyond.
  1542. // Just turn to normal push button.
  1543. NtUserAlterWindowStyle(hwnd, BS_TYPEMASK, 0);
  1544. RIPMSG0(RIP_WARNING, "BS_USERBUTTON no longer supported");
  1545. }
  1546. }
  1547. if (TestWF(pwnd,WEFRIGHT)) {
  1548. NtUserAlterWindowStyle(hwnd, BS_RIGHT | BS_RIGHTBUTTON, BS_RIGHT | BS_RIGHTBUTTON);
  1549. }
  1550. goto CallDWP;
  1551. case WM_INPUTLANGCHANGEREQUEST:
  1552. //
  1553. // #115190
  1554. // If the window is one of controls on top of dialogbox,
  1555. // let the parent dialog handle it.
  1556. //
  1557. if (TestwndChild(pwnd) && pwnd->spwndParent) {
  1558. PWND pwndParent = REBASEPWND(pwnd, spwndParent);
  1559. if (pwndParent) {
  1560. PCLS pclsParent = REBASEALWAYS(pwndParent, pcls);
  1561. UserAssert(pclsParent != NULL);
  1562. if (pclsParent->atomClassName == gpsi->atomSysClass[ICLS_DIALOG]) {
  1563. RIPMSG0(RIP_VERBOSE, "Button: WM_INPUTLANGCHANGEREQUEST is sent to parent.\n");
  1564. return SendMessageWorker(pwndParent, message, wParam, lParam, FALSE);
  1565. }
  1566. }
  1567. }
  1568. goto CallDWP;
  1569. case WM_UPDATEUISTATE:
  1570. {
  1571. DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
  1572. if (ISBSTEXTOROD(pwnd)) {
  1573. pbutn->fPaintKbdCuesOnly = TRUE;
  1574. RepaintButton(pbutn);
  1575. pbutn->fPaintKbdCuesOnly = FALSE;
  1576. }
  1577. }
  1578. break;
  1579. default:
  1580. CallDWP:
  1581. return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi);
  1582. }
  1583. return 0L;
  1584. }
  1585. /***************************************************************************\
  1586. \***************************************************************************/
  1587. LRESULT WINAPI ButtonWndProcA(
  1588. HWND hwnd,
  1589. UINT message,
  1590. WPARAM wParam,
  1591. LPARAM lParam)
  1592. {
  1593. PWND pwnd;
  1594. if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
  1595. return (0L);
  1596. }
  1597. /*
  1598. * If the control is not interested in this message,
  1599. * pass it to DefWindowProc.
  1600. */
  1601. if (!FWINDOWMSG(message, FNID_BUTTON))
  1602. return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE);
  1603. return ButtonWndProcWorker(pwnd, message, wParam, lParam, TRUE);
  1604. }
  1605. LRESULT WINAPI ButtonWndProcW(
  1606. HWND hwnd,
  1607. UINT message,
  1608. WPARAM wParam,
  1609. LPARAM lParam)
  1610. {
  1611. PWND pwnd;
  1612. if ((pwnd = ValidateHwnd(hwnd)) == NULL) {
  1613. return (0L);
  1614. }
  1615. /*
  1616. * If the control is not interested in this message,
  1617. * pass it to DefWindowProc.
  1618. */
  1619. if (!FWINDOWMSG(message, FNID_BUTTON))
  1620. return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE);
  1621. return ButtonWndProcWorker(pwnd, message, wParam, lParam, FALSE);
  1622. }