Source code of Windows XP (NT5)
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.

3474 lines
92 KiB

  1. #include "ctlspriv.h"
  2. #pragma hdrstop
  3. #include "usrctl32.h"
  4. #include "button.h"
  5. //
  6. // ButtonCalcRect codes
  7. //
  8. #define CBR_CLIENTRECT 0
  9. #define CBR_CHECKBOX 1
  10. #define CBR_CHECKTEXT 2
  11. #define CBR_GROUPTEXT 3
  12. #define CBR_GROUPFRAME 4
  13. #define CBR_PUSHBUTTON 5
  14. #define CBR_RADIOBUTTON 6
  15. #define Button_IsThemed(pbutn) ((pbutn)->hTheme && (pbutn)->hImage == NULL)
  16. //---------------------------------------------------------------------------//
  17. CONST BYTE mpStyleCbr[] =
  18. {
  19. CBR_PUSHBUTTON, // BS_PUSHBUTTON
  20. CBR_PUSHBUTTON, // BS_DEFPUSHBUTTON
  21. CBR_CHECKTEXT, // BS_CHECKBOX
  22. CBR_CHECKTEXT, // BS_AUTOCHECKBOX
  23. CBR_CHECKTEXT, // BS_RADIOBUTTON
  24. CBR_CHECKTEXT, // BS_3STATE
  25. CBR_CHECKTEXT, // BS_AUTO3STATE
  26. CBR_GROUPTEXT, // BS_GROUPBOX
  27. CBR_CLIENTRECT, // BS_USERBUTTON
  28. CBR_CHECKTEXT, // BS_AUTORADIOBUTTON
  29. CBR_CLIENTRECT, // BS_PUSHBOX
  30. CBR_CLIENTRECT, // BS_OWNERDRAW
  31. };
  32. #define IMAGE_BMMAX IMAGE_CURSOR+1
  33. static CONST BYTE rgbType[IMAGE_BMMAX] =
  34. {
  35. BS_BITMAP, // IMAGE_BITMAP
  36. BS_ICON, // IMAGE_CURSOR
  37. BS_ICON // IMAGE_ICON
  38. };
  39. #define IsValidImage(imageType, realType, max) \
  40. ((imageType < max) && (rgbType[imageType] == realType))
  41. typedef struct tagBTNDATA
  42. {
  43. LPTSTR lpsz; // Text string
  44. PBUTN pbutn; // Button data
  45. WORD wFlags; // Alignment flags
  46. } BTNDATA, *LPBTNDATA;
  47. //---- to support multiple themes in a single process, move these into PBUTN ----
  48. static SIZE sizeCheckBox = {0};
  49. static SIZE sizeRadioBox = {0};
  50. //---------------------------------------------------------------------------//
  51. //
  52. // Forwards
  53. //
  54. VOID Button_DrawPush(PBUTN pbutn, HDC hdc, UINT pbfPush);
  55. VOID GetCheckBoxSize(HDC hdc, PBUTN pbutn, BOOL fCheckBox, LPSIZE psize);
  56. WORD GetAlignment(PBUTN pbutn);
  57. VOID Button_CalcRect(PBUTN pbutn, HDC hdc, LPRECT lprc, int iCode, UINT uFlags);
  58. VOID Button_MultiExtent(WORD wFlags, HDC hdc, LPRECT lprcMax, LPTSTR lpsz, int cch, PINT pcx, PINT pcy);
  59. __inline UINT IsPushButton(PBUTN pbutn);
  60. __inline ULONG GetButtonType(ULONG ulWinStyle);
  61. //---------------------------------------------------------------------------//
  62. //
  63. // InitButtonClass() - Registers the control's window class
  64. //
  65. BOOL InitButtonClass(HINSTANCE hInstance)
  66. {
  67. WNDCLASS wc;
  68. wc.lpfnWndProc = Button_WndProc;
  69. wc.lpszClassName = WC_BUTTON;
  70. wc.style = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
  71. wc.cbClsExtra = 0;
  72. wc.cbWndExtra = sizeof(PBUTN);
  73. wc.hInstance = hInstance;
  74. wc.hIcon = NULL;
  75. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  76. wc.hbrBackground = NULL;
  77. wc.lpszMenuName = NULL;
  78. if (!RegisterClass(&wc) && !GetClassInfo(hInstance, WC_BUTTON, &wc))
  79. return FALSE;
  80. return TRUE;
  81. }
  82. //---------------------------------------------------------------------------//
  83. //
  84. // Button_GetThemeIds() - Gets the associated iPartId and iStateId needed for
  85. // the theme manager APIs for the button control passed
  86. // in pbutn.
  87. //
  88. HRESULT Button_GetThemeIds(PBUTN pbutn, LPINT piPartId, LPINT piStateId)
  89. {
  90. if ( piPartId )
  91. {
  92. ULONG ulStyle = GET_STYLE(pbutn);
  93. if (IsPushButton(pbutn))
  94. {
  95. *piPartId = BP_PUSHBUTTON;
  96. }
  97. else
  98. {
  99. switch (GetButtonType(ulStyle))
  100. {
  101. case BS_CHECKBOX:
  102. case BS_AUTOCHECKBOX:
  103. case BS_3STATE:
  104. case BS_AUTO3STATE:
  105. *piPartId = BP_CHECKBOX;
  106. break;
  107. case BS_RADIOBUTTON:
  108. case BS_AUTORADIOBUTTON:
  109. *piPartId = BP_RADIOBUTTON;
  110. break;
  111. case BS_GROUPBOX:
  112. *piPartId = BP_GROUPBOX;
  113. break;
  114. case BS_OWNERDRAW:
  115. //
  116. // don't do anything with owerdrawn buttons
  117. //
  118. return E_FAIL;
  119. default:
  120. TraceMsg(TF_STANDARD, "What kind of buttonType is this, %#.2x", GetButtonType(ulStyle));
  121. *piPartId = BP_PUSHBUTTON;
  122. break;
  123. }
  124. }
  125. if (piStateId)
  126. {
  127. switch (*piPartId)
  128. {
  129. case BP_PUSHBUTTON:
  130. if ((pbutn->buttonState & BST_PUSHED) ||
  131. ((pbutn->buttonState & (BST_CHECKED|BST_HOT)) == BST_CHECKED))
  132. {
  133. *piStateId = PBS_PRESSED;
  134. }
  135. else if (!IsWindowEnabled(pbutn->ci.hwnd))
  136. {
  137. *piStateId = PBS_DISABLED;
  138. }
  139. else if (pbutn->buttonState & BST_HOT)
  140. {
  141. *piStateId = PBS_HOT;
  142. }
  143. else if (ulStyle & BS_DEFPUSHBUTTON)
  144. {
  145. *piStateId = PBS_DEFAULTED;
  146. }
  147. else
  148. {
  149. *piStateId = PBS_NORMAL;
  150. }
  151. break;
  152. case BP_CHECKBOX:
  153. case BP_RADIOBUTTON:
  154. //
  155. // NOTE (phellyar): We're relying on the order of the RADIOBUTTONSTATES and
  156. // CHECKBOXSTATES enums in tmdefs.h to calculate the correct
  157. // StateId. If the ordering of those enums changes, revisit
  158. // the logic here.
  159. // Note also that CHECKBOXSTATES is a super set of
  160. // RADIOBUTTONSTATES which is why we're using CBS_* here.
  161. //
  162. if ( pbutn->buttonState & BST_CHECKED )
  163. {
  164. //
  165. // button is checked
  166. //
  167. *piStateId = CBS_CHECKEDNORMAL;
  168. }
  169. else if ( pbutn->buttonState & BST_INDETERMINATE )
  170. {
  171. //
  172. // button is intedeterminate
  173. //
  174. *piStateId = CBS_MIXEDNORMAL;
  175. }
  176. else
  177. {
  178. //
  179. // button is unchecked
  180. //
  181. *piStateId = CBS_UNCHECKEDNORMAL;
  182. }
  183. if ( pbutn->buttonState & BST_PUSHED )
  184. {
  185. //
  186. // being pressed
  187. //
  188. *piStateId += 2;
  189. }
  190. else if (!IsWindowEnabled(pbutn->ci.hwnd))
  191. {
  192. //
  193. // disabled
  194. //
  195. *piStateId += 3;
  196. }
  197. else if (pbutn->buttonState & BST_HOT )
  198. {
  199. //
  200. // mouse over
  201. //
  202. *piStateId += 1;
  203. }
  204. break;
  205. case BP_GROUPBOX:
  206. if (!IsWindowEnabled(pbutn->ci.hwnd))
  207. {
  208. *piStateId = GBS_DISABLED;
  209. }
  210. else
  211. {
  212. *piStateId = GBS_NORMAL;
  213. }
  214. break;
  215. }
  216. }
  217. }
  218. return S_OK;
  219. }
  220. //---------------------------------------------------------------------------//
  221. //
  222. // Button_GetTextFlags() - Returns the DrawTextEx flags that should be used
  223. // when rendering text for this control, needed by
  224. // DrawThemeText.
  225. //
  226. DWORD Button_GetTextFlags(PBUTN pbutn)
  227. {
  228. DWORD dwTextFlags = 0;
  229. WORD wAlign = GetAlignment(pbutn);
  230. ULONG ulStyle = GET_STYLE(pbutn);
  231. //
  232. // Set up text flags
  233. //
  234. //
  235. // horizontal text alignment
  236. //
  237. switch (wAlign & HIBYTE(BS_HORZMASK))
  238. {
  239. case HIBYTE(BS_LEFT):
  240. dwTextFlags |= DT_LEFT;
  241. break;
  242. case HIBYTE(BS_RIGHT):
  243. dwTextFlags |= DT_RIGHT;
  244. break;
  245. case HIBYTE(BS_CENTER):
  246. dwTextFlags |= DT_CENTER;
  247. break;
  248. }
  249. //
  250. // vertical text alignment
  251. //
  252. switch (wAlign & HIBYTE(BS_VERTMASK))
  253. {
  254. case HIBYTE(BS_TOP):
  255. dwTextFlags |= DT_TOP;
  256. break;
  257. case HIBYTE(BS_BOTTOM):
  258. dwTextFlags |= DT_BOTTOM;
  259. break;
  260. case HIBYTE(BS_VCENTER):
  261. dwTextFlags |= DT_VCENTER;
  262. break;
  263. }
  264. //
  265. // line break
  266. //
  267. if (ulStyle & BS_MULTILINE)
  268. {
  269. dwTextFlags |= (DT_WORDBREAK | DT_EDITCONTROL);
  270. }
  271. else
  272. {
  273. dwTextFlags |= DT_SINGLELINE;
  274. }
  275. if (ulStyle & SS_NOPREFIX)
  276. {
  277. dwTextFlags |= DT_NOPREFIX;
  278. }
  279. //
  280. // Draw the underscore for accelorators?
  281. //
  282. if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
  283. {
  284. dwTextFlags |= DT_HIDEPREFIX;
  285. }
  286. return dwTextFlags;
  287. }
  288. DWORD ButtonStateToCustomDrawState(PBUTN pbutn)
  289. {
  290. DWORD itemState = 0;
  291. if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN))
  292. {
  293. itemState |= CDIS_SHOWKEYBOARDCUES;
  294. }
  295. if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
  296. {
  297. itemState |= CDIS_SHOWKEYBOARDCUES;
  298. }
  299. if (BUTTONSTATE(pbutn) & BST_FOCUS)
  300. {
  301. itemState |= CDIS_FOCUS;
  302. }
  303. if (BUTTONSTATE(pbutn) & BST_PUSHED)
  304. {
  305. itemState |= CDIS_SELECTED;
  306. }
  307. if (BUTTONSTATE(pbutn) & BST_HOT)
  308. {
  309. itemState |= CDIS_HOT;
  310. }
  311. if (!IsWindowEnabled(pbutn->ci.hwnd))
  312. {
  313. itemState |= CDIS_DISABLED;
  314. }
  315. return itemState;
  316. }
  317. void Button_GetImagePosition(PBUTN pbutn, RECT* prc, int* px, int* py)
  318. {
  319. int cx = 0;
  320. int cy = 0;
  321. CCGetIconSize(&pbutn->ci, pbutn->himl, &cx, &cy);
  322. cx += pbutn->rcIcon.left + pbutn->rcIcon.right;
  323. cy += pbutn->rcIcon.top + pbutn->rcIcon.bottom;
  324. switch (pbutn->uAlign)
  325. {
  326. case BUTTON_IMAGELIST_ALIGN_RIGHT:
  327. *px = prc->right - cx;
  328. *py = prc->top + (RECTHEIGHT(*prc) - cy) / 2 + pbutn->rcIcon.top;
  329. prc->right -= cx;
  330. break;
  331. case BUTTON_IMAGELIST_ALIGN_CENTER: // This means no text
  332. *px = prc->left + (RECTWIDTH(*prc) - cx) / 2 + pbutn->rcIcon.left;
  333. *py = prc->top + (RECTHEIGHT(*prc) - cy) / 2 + pbutn->rcIcon.top;
  334. break;
  335. case BUTTON_IMAGELIST_ALIGN_TOP:
  336. *px = prc->left + (RECTWIDTH(*prc) - cx) / 2 + pbutn->rcIcon.left;
  337. *py = pbutn->rcIcon.top;
  338. prc->top += cy;
  339. break;
  340. case BUTTON_IMAGELIST_ALIGN_BOTTOM:
  341. *px = (RECTWIDTH(*prc) - cx) / 2 + pbutn->rcIcon.left;
  342. *py = prc->bottom - cy;
  343. prc->bottom -= cy;
  344. break;
  345. case BUTTON_IMAGELIST_ALIGN_LEFT:
  346. // Fall
  347. default:
  348. *px = prc->left + pbutn->rcIcon.left;
  349. *py = prc->top + (RECTHEIGHT(*prc) - cy) / 2 + pbutn->rcIcon.top;
  350. prc->left += cx;
  351. break;
  352. }
  353. }
  354. //---------------------------------------------------------------------------//
  355. //
  356. // Button_DrawThemed() - Renders button control according to the current
  357. // theme.
  358. // pbutn - the button control to render
  359. // hdc - the hdc to draw on
  360. // iPartId - the button part
  361. // iStateId - the button state
  362. //
  363. HRESULT Button_DrawThemed(PBUTN pbutn, HDC hdc, int iPartId, int iStateId)
  364. {
  365. HRESULT hr;
  366. RECT rcClient;
  367. RECT rcContent;
  368. RECT rcFocus;
  369. RECT rcCheck;
  370. DWORD dwTextFlags;
  371. LPWSTR pszText;
  372. int cch;
  373. NMCUSTOMDRAW nmcd = {0};
  374. BOOL fRadioOrCheck = (iPartId == BP_RADIOBUTTON || iPartId == BP_CHECKBOX );
  375. //
  376. // Render the button background
  377. //
  378. GetClientRect(pbutn->ci.hwnd, &rcClient);
  379. rcCheck = rcContent = rcClient;
  380. if ( fRadioOrCheck )
  381. {
  382. SIZE sizeChar;
  383. SIZE sizeCheck;
  384. int iCode;
  385. //
  386. // Compat....
  387. //
  388. GetTextExtentPoint32(hdc, TEXT("0"), 1, &sizeChar);
  389. GetCheckBoxSize(hdc, pbutn, (iPartId == BP_CHECKBOX), &sizeCheck);
  390. if (iPartId == BP_CHECKBOX)
  391. iCode = CBR_CHECKBOX;
  392. else
  393. iCode = CBR_RADIOBUTTON;
  394. Button_CalcRect(pbutn, hdc, &rcCheck, iCode, 0);
  395. rcCheck.bottom = rcCheck.top + sizeCheck.cx;
  396. if ((GET_STYLE(pbutn) & BS_RIGHTBUTTON) != 0)
  397. {
  398. rcCheck.left = rcContent.right - sizeCheck.cx;
  399. rcContent.right = rcCheck.left - (sizeChar.cx/2);
  400. }
  401. else
  402. {
  403. rcCheck.right = rcContent.left + sizeCheck.cx;
  404. rcContent.left = rcCheck.right + (sizeChar.cx/2);
  405. }
  406. //---- shrink radiobutton/checkbox button to fix client rect ----
  407. if (RECTWIDTH(rcClient) < RECTWIDTH(rcCheck))
  408. {
  409. rcCheck.right = rcCheck.left + RECTWIDTH(rcClient);
  410. }
  411. if (RECTHEIGHT(rcClient) < RECTHEIGHT(rcCheck))
  412. {
  413. rcCheck.bottom = rcCheck.top + RECTHEIGHT(rcClient);
  414. }
  415. }
  416. nmcd.hdc = hdc;
  417. nmcd.rc = rcClient;
  418. nmcd.dwItemSpec = GetWindowID(pbutn->ci.hwnd);
  419. nmcd.uItemState = ButtonStateToCustomDrawState(pbutn);
  420. pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREERASE, &nmcd);
  421. if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
  422. {
  423. hr = DrawThemeBackground(pbutn->hTheme, hdc, iPartId, iStateId, &rcCheck, 0);
  424. if (FAILED(hr))
  425. {
  426. TraceMsg(TF_STANDARD, "Failed to render theme background");
  427. return hr;
  428. }
  429. if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
  430. CICustomDrawNotify(&pbutn->ci, CDDS_POSTERASE, &nmcd);
  431. pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREPAINT, &nmcd);
  432. if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
  433. {
  434. //
  435. // Render the button text
  436. //
  437. GetThemeBackgroundContentRect(pbutn->hTheme, hdc, iPartId, iStateId, &rcContent, &rcContent);
  438. rcFocus = rcContent;
  439. if (pbutn->himl)
  440. {
  441. int x, y;
  442. int iImage = 0;
  443. if (ImageList_GetImageCount(pbutn->himl) > 1)
  444. {
  445. iImage = (iStateId - PBS_NORMAL);
  446. }
  447. Button_GetImagePosition(pbutn, &rcContent, &x, &y);
  448. ImageList_Draw(pbutn->himl, iImage, hdc, x, y, ILD_TRANSPARENT | (CCDPIScale(pbutn->ci)?ILD_DPISCALE:0));
  449. }
  450. //
  451. // Get the button text
  452. //
  453. cch = GetWindowTextLength(pbutn->ci.hwnd);
  454. if (cch == 0)
  455. {
  456. //
  457. // Nothing to draw
  458. //
  459. return hr;
  460. }
  461. pszText = UserLocalAlloc(0, sizeof(WCHAR)*(cch+1));
  462. if (pszText == NULL)
  463. {
  464. TraceMsg(TF_STANDARD, "Can't allocate buffer");
  465. return E_FAIL;
  466. }
  467. GetWindowTextW(pbutn->ci.hwnd, pszText, cch+1);
  468. dwTextFlags = Button_GetTextFlags(pbutn);
  469. if ( TESTFLAG(GET_STYLE(pbutn), BS_MULTILINE) || fRadioOrCheck )
  470. {
  471. int cxWidth, cyHeight;
  472. TEXTMETRIC tm;
  473. if ( TESTFLAG(GET_STYLE(pbutn), BS_MULTILINE) )
  474. {
  475. RECT rcTextExtent = rcContent;
  476. cyHeight = DrawTextEx(hdc, pszText, cch, &rcTextExtent, dwTextFlags|DT_CALCRECT, NULL);
  477. cxWidth = RECTWIDTH(rcTextExtent);
  478. }
  479. else
  480. {
  481. SIZE size;
  482. LPWSTR pszStrip = UserLocalAlloc(0, sizeof(WCHAR)*(cch+1));
  483. if ( pszStrip )
  484. {
  485. int cchStrip = StripAccelerators(pszText, pszStrip, TRUE);
  486. GetTextExtentPoint32(hdc, pszStrip, cchStrip, &size);
  487. UserLocalFree(pszStrip);
  488. }
  489. else
  490. {
  491. GetTextExtentPoint32(hdc, pszText, cch, &size);
  492. }
  493. cyHeight = size.cy;
  494. cxWidth = size.cx;
  495. }
  496. if ( fRadioOrCheck && (cyHeight < RECTHEIGHT(rcCheck)))
  497. {
  498. // optimization for single line check/radios, align them with the top
  499. // of the check no matter when the vertical alignment
  500. rcContent.top = rcCheck.top;
  501. }
  502. else
  503. {
  504. if (dwTextFlags & DT_VCENTER)
  505. {
  506. rcContent.top += (RECTHEIGHT(rcContent) - cyHeight) / 2;
  507. }
  508. else if (dwTextFlags & DT_BOTTOM)
  509. {
  510. rcContent.top = rcContent.bottom - cyHeight;
  511. }
  512. }
  513. if ( GetTextMetrics( hdc, &tm ) && (tm.tmInternalLeading == 0) )
  514. {
  515. // Far East fonts that have no leading. Leave space to prevent
  516. // focus rect from obscuring text.
  517. rcContent.top += g_cyBorder;
  518. }
  519. rcContent.bottom = rcContent.top + cyHeight;
  520. if (dwTextFlags & DT_CENTER)
  521. {
  522. rcContent.left += (RECTWIDTH(rcContent) - cxWidth) / 2;
  523. }
  524. else if (dwTextFlags & DT_RIGHT)
  525. {
  526. rcContent.left = rcContent.right - cxWidth;
  527. }
  528. rcContent.right= rcContent.left + cxWidth;
  529. if ( fRadioOrCheck )
  530. {
  531. //
  532. // Inflate the bounding rect a litte, but contrained to
  533. // within the client area.
  534. //
  535. rcFocus.top = max(rcClient.top, rcContent.top-1);
  536. rcFocus.bottom = min(rcClient.bottom, rcContent.bottom+1);
  537. rcFocus.left = max(rcClient.left, rcContent.left-1);
  538. rcFocus.right = min(rcClient.right, rcContent.right+1);
  539. }
  540. }
  541. hr = DrawThemeText(pbutn->hTheme, hdc, iPartId, iStateId, pszText, cch, dwTextFlags, 0, &rcContent);
  542. if (FAILED(hr))
  543. {
  544. TraceMsg(TF_STANDARD, "Failed to render button text");
  545. }
  546. if (!TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN) && (BUTTONSTATE(pbutn) & BST_FOCUS))
  547. {
  548. DrawFocusRect(hdc, &rcFocus);
  549. }
  550. UserLocalFree(pszText);
  551. if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
  552. {
  553. CICustomDrawNotify(&pbutn->ci, CDDS_POSTPAINT, &nmcd);
  554. }
  555. }
  556. }
  557. return hr;
  558. }
  559. //---------------------------------------------------------------------------//
  560. //
  561. // Button_GetTheme() - Get a handle to the theme for this button control
  562. //
  563. HTHEME Button_GetTheme(PBUTN pbutn)
  564. {
  565. //
  566. // Button's with predefined IDs can be
  567. // themed differently
  568. //
  569. static LPWSTR szButtonClasses[] =
  570. {
  571. L"Button", // =0
  572. L"Button-OK;Button", // IDOK=1
  573. L"Button-CANCEL;Button", // IDCANCEL=2
  574. L"Button-ABORT;Button", // IDABORT=3
  575. L"Button-RETRY;Button", // IDRETRY=4
  576. L"Button-IGNORE;Button", // IDIGNORE=5
  577. L"Button-YES;Button", // IDYES=6
  578. L"Button-NO;Button", // IDNO=7
  579. L"Button-CLOSE;Button", // IDCLOSE=8
  580. L"Button-HELP;Button", // IDHELP=9
  581. L"Button-TRYAGAIN;Button", // IDTRYAGAIN=10
  582. L"Button-CONTINUE;Button", // IDCONTINUE=11
  583. L"Button-APPLY;Button", // IDAPPLY=12 (not yet std)
  584. };
  585. int iButtonId = GetWindowID(pbutn->ci.hwnd);
  586. if (iButtonId < 0 || iButtonId >= ARRAYSIZE(szButtonClasses)) // outside range
  587. {
  588. iButtonId = 0;
  589. }
  590. EnableThemeDialogTexture(GetParent(pbutn->ci.hwnd), ETDT_ENABLE);
  591. return OpenThemeData(pbutn->ci.hwnd, szButtonClasses[iButtonId]);
  592. }
  593. //---------------------------------------------------------------------------//
  594. //
  595. VOID GetCheckBoxSize(HDC hdc, PBUTN pbutn, BOOL fCheckBox, LPSIZE psize)
  596. {
  597. SIZE *psz;
  598. if (fCheckBox)
  599. psz = &sizeCheckBox;
  600. else
  601. psz = &sizeRadioBox;
  602. if ((! psz->cx) && (! psz->cy)) // not yet calculated
  603. {
  604. BOOL fGotSize = FALSE;
  605. if (pbutn->hTheme) // get themed size
  606. {
  607. int iPartId;
  608. HRESULT hr;
  609. if (fCheckBox)
  610. iPartId = BP_CHECKBOX;
  611. else
  612. iPartId = BP_RADIOBUTTON;
  613. hr = GetThemePartSize(pbutn->hTheme, hdc, iPartId, 1, NULL, TS_DRAW, psz);
  614. if (FAILED(hr))
  615. {
  616. TraceMsg(TF_STANDARD, "Failed to get theme part size for checkbox/radiobutton");
  617. }
  618. else
  619. {
  620. fGotSize = TRUE;
  621. }
  622. }
  623. if (! fGotSize) // get classic size (use checkbox for both)
  624. {
  625. HBITMAP hbmp = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECKBOXES));
  626. if (hbmp != NULL)
  627. {
  628. BITMAP bmp;
  629. GetObject(hbmp, sizeof(BITMAP), &bmp);
  630. //
  631. // Checkbox bitmap is arranged 4 over and three down. Only need to get
  632. // the size of a single checkbox, so do the math here.
  633. //
  634. psz->cx = bmp.bmWidth / 4;
  635. psz->cy = bmp.bmHeight / 3;
  636. DeleteObject(hbmp);
  637. }
  638. else
  639. {
  640. AssertMsg(hbmp != NULL, TEXT("Unable to load checkbox bitmap"));
  641. }
  642. }
  643. }
  644. *psize = *psz;
  645. }
  646. //---------------------------------------------------------------------------//
  647. //
  648. __inline BYTE GetButtonStyle(ULONG ulWinStyle)
  649. {
  650. return (BYTE) LOBYTE(ulWinStyle & BS_TYPEMASK);
  651. }
  652. //---------------------------------------------------------------------------//
  653. //
  654. __inline ULONG GetButtonType(ULONG ulWinStyle)
  655. {
  656. return ulWinStyle & BS_TYPEMASK;
  657. }
  658. //---------------------------------------------------------------------------//
  659. //
  660. // IsPushButton()
  661. //
  662. // Returns non-zero if the window is a push button. Returns flags that
  663. // are interesting if it is. These flags are
  664. //
  665. UINT IsPushButton(PBUTN pbutn)
  666. {
  667. BYTE bStyle;
  668. UINT flags;
  669. ULONG ulStyle = GET_STYLE(pbutn);
  670. bStyle = GetButtonStyle(ulStyle);
  671. flags = 0;
  672. switch (bStyle)
  673. {
  674. case LOBYTE(BS_PUSHBUTTON):
  675. flags |= PBF_PUSHABLE;
  676. break;
  677. case LOBYTE(BS_DEFPUSHBUTTON):
  678. flags |= PBF_PUSHABLE | PBF_DEFAULT;
  679. break;
  680. default:
  681. if (ulStyle & BS_PUSHLIKE)
  682. {
  683. flags |= PBF_PUSHABLE;
  684. }
  685. }
  686. return flags;
  687. }
  688. //---------------------------------------------------------------------------//
  689. //
  690. // GetAlignment()
  691. //
  692. // Gets default alignment of button. If BS_HORZMASK and/or BS_VERTMASK
  693. // is specified, uses those. Otherwise, uses default for button.
  694. //
  695. // It's probably a fine time to describe what alignment flags mean for
  696. // each type of button. Note that the presence of a bitmap/icon affects
  697. // the meaning of alignments.
  698. //
  699. // (1) Push like buttons
  700. // With one of {bitmap, icon, text}:
  701. // Just like you'd expect
  702. // With one of {bitmap, icon} AND text:
  703. // Image & text are centered as a unit; alignment means where
  704. // the image shows up. E.G., left-aligned means the image
  705. // on the left, text on the right.
  706. // (2) Radio/check like buttons
  707. // Left aligned means check/radio box is on left, then bitmap/icon
  708. // and text follows, left justified.
  709. // Right aligned means checkk/radio box is on right, preceded by
  710. // text and bitmap/icon, right justified.
  711. // Centered has no meaning.
  712. // With one of {bitmap, icon} AND text:
  713. // Top aligned means bitmap/icon above, text below
  714. // Bottom aligned means text above, bitmap/icon below
  715. // With one of {bitmap, icon, text}
  716. // Alignments mean what you'd expect.
  717. // (3) Group boxes
  718. // Left aligned means text is left justified on left side
  719. // Right aligned means text is right justified on right side
  720. // Center aligned means text is in middle
  721. //
  722. WORD GetAlignment(PBUTN pbutn)
  723. {
  724. BYTE bHorz;
  725. BYTE bVert;
  726. ULONG ulStyle = GET_STYLE(pbutn);
  727. bHorz = HIBYTE(ulStyle & BS_HORZMASK);
  728. bVert = HIBYTE(ulStyle & BS_VERTMASK);
  729. if (!bHorz || !bVert)
  730. {
  731. if (IsPushButton(pbutn))
  732. {
  733. if (!bHorz)
  734. {
  735. bHorz = HIBYTE(BS_CENTER);
  736. }
  737. }
  738. else
  739. {
  740. if (!bHorz)
  741. {
  742. bHorz = HIBYTE(BS_LEFT);
  743. }
  744. }
  745. if (GetButtonStyle(ulStyle) == BS_GROUPBOX)
  746. {
  747. if (!bVert)
  748. {
  749. bVert = HIBYTE(BS_TOP);
  750. }
  751. }
  752. else
  753. {
  754. if (!bVert)
  755. {
  756. bVert = HIBYTE(BS_VCENTER);
  757. }
  758. }
  759. }
  760. return bHorz | bVert;
  761. }
  762. //---------------------------------------------------------------------------//
  763. //
  764. // Button_SetFont()
  765. //
  766. // Changes button font, and decides if we can use real bold font for default
  767. // push buttons or if we have to simulate it.
  768. //
  769. VOID Button_SetFont(PBUTN pbutn, HFONT hFont, BOOL fRedraw)
  770. {
  771. pbutn->hFont = hFont;
  772. if (fRedraw && IsWindowVisible(pbutn->ci.hwnd))
  773. {
  774. InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
  775. }
  776. }
  777. //---------------------------------------------------------------------------//
  778. //
  779. HBRUSH Button_InitDC(PBUTN pbutn, HDC hdc)
  780. {
  781. UINT uMsg;
  782. BYTE bStyle;
  783. HBRUSH hBrush;
  784. ULONG ulStyle = GET_STYLE(pbutn);
  785. ULONG ulStyleEx = GET_EXSTYLE(pbutn);
  786. //
  787. // Set BkMode before getting brush so that the app can change it to
  788. // transparent if it wants.
  789. //
  790. SetBkMode(hdc, OPAQUE);
  791. bStyle = GetButtonStyle(ulStyle);
  792. switch (bStyle)
  793. {
  794. default:
  795. if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT) && ((ulStyle & BS_PUSHLIKE) == 0))
  796. {
  797. uMsg = WM_CTLCOLORSTATIC;
  798. break;
  799. }
  800. case LOBYTE(BS_PUSHBUTTON):
  801. case LOBYTE(BS_DEFPUSHBUTTON):
  802. case LOBYTE(BS_OWNERDRAW):
  803. case LOBYTE(BS_USERBUTTON):
  804. uMsg = WM_CTLCOLORBTN;
  805. break;
  806. }
  807. hBrush = (HBRUSH)SendMessage(GetParent(pbutn->ci.hwnd), uMsg, (WPARAM)hdc, (LPARAM)pbutn->ci.hwnd);
  808. //
  809. // Select in the user's font if set, and save the old font so that we can
  810. // restore it when we release the dc.
  811. //
  812. if (pbutn->hFont)
  813. {
  814. SelectObject(hdc, pbutn->hFont);
  815. }
  816. //
  817. // Clip output to the window rect if needed.
  818. //
  819. if (bStyle != LOBYTE(BS_GROUPBOX))
  820. {
  821. RECT rcClient;
  822. GetClientRect(pbutn->ci.hwnd, &rcClient);
  823. IntersectClipRect(hdc, 0, 0,
  824. rcClient.right,
  825. rcClient.bottom);
  826. }
  827. if ((ulStyleEx & WS_EX_RTLREADING) != 0)
  828. {
  829. SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
  830. }
  831. return hBrush;
  832. }
  833. //---------------------------------------------------------------------------//
  834. //
  835. HDC Button_GetDC(PBUTN pbutn, HBRUSH *phBrush)
  836. {
  837. HDC hdc = NULL;
  838. if (IsWindowVisible(pbutn->ci.hwnd))
  839. {
  840. HBRUSH hBrush;
  841. hdc = GetDC(pbutn->ci.hwnd);
  842. hBrush = Button_InitDC(pbutn, hdc);
  843. if ((phBrush != NULL) && hBrush)
  844. {
  845. *phBrush = hBrush;
  846. }
  847. }
  848. return hdc;
  849. }
  850. //---------------------------------------------------------------------------//
  851. //
  852. VOID Button_ReleaseDC(PBUTN pbutn, HDC hdc, HBRUSH *phBrush)
  853. {
  854. ULONG ulStyleEx = GET_EXSTYLE(pbutn);
  855. if ((ulStyleEx & WS_EX_RTLREADING) != 0)
  856. {
  857. SetTextAlign(hdc, GetTextAlign(hdc) & ~TA_RTLREADING);
  858. }
  859. if (pbutn->hFont)
  860. {
  861. SelectObject(hdc, GetStockObject(SYSTEM_FONT));
  862. }
  863. ReleaseDC(pbutn->ci.hwnd, hdc);
  864. }
  865. //---------------------------------------------------------------------------//
  866. //
  867. VOID Button_OwnerDraw(PBUTN pbutn, HDC hdc, UINT itemAction)
  868. {
  869. DRAWITEMSTRUCT drawItemStruct;
  870. UINT itemState = 0;
  871. int iButtonId = GetWindowID(pbutn->ci.hwnd);
  872. if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN))
  873. {
  874. itemState |= ODS_NOFOCUSRECT;
  875. }
  876. if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
  877. {
  878. itemState |= ODS_NOACCEL;
  879. }
  880. if (TESTFLAG(BUTTONSTATE(pbutn), BST_FOCUS))
  881. {
  882. itemState |= ODS_FOCUS;
  883. }
  884. if (TESTFLAG(BUTTONSTATE(pbutn), BST_PUSHED))
  885. {
  886. itemState |= ODS_SELECTED;
  887. }
  888. if (!IsWindowEnabled(pbutn->ci.hwnd))
  889. {
  890. itemState |= ODS_DISABLED;
  891. }
  892. //
  893. // Populate the draw item struct
  894. //
  895. drawItemStruct.CtlType = ODT_BUTTON;
  896. drawItemStruct.CtlID = iButtonId;
  897. drawItemStruct.itemAction = itemAction;
  898. drawItemStruct.itemState = itemState;
  899. drawItemStruct.hwndItem = pbutn->ci.hwnd;
  900. drawItemStruct.hDC = hdc;
  901. GetClientRect(pbutn->ci.hwnd, &drawItemStruct.rcItem);
  902. drawItemStruct.itemData = 0L;
  903. //
  904. // Send a WM_DRAWITEM message to our parent
  905. //
  906. SendMessage(GetParent(pbutn->ci.hwnd),
  907. WM_DRAWITEM,
  908. (WPARAM)iButtonId,
  909. (LPARAM)&drawItemStruct);
  910. }
  911. //---------------------------------------------------------------------------//
  912. //
  913. VOID Button_CalcRect(PBUTN pbutn, HDC hdc, LPRECT lprc, int iCode, UINT uFlags)
  914. {
  915. CONST TCHAR szOneChar[] = TEXT("0");
  916. SIZE sizeExtent;
  917. int dy;
  918. LPTSTR lpName = NULL;
  919. WORD wAlign;
  920. int cxEdge, cyEdge;
  921. int cxBorder, cyBorder;
  922. ULONG ulStyle = GET_STYLE(pbutn);
  923. ULONG ulStyleEx = GET_EXSTYLE(pbutn);
  924. cxEdge = GetSystemMetrics(SM_CXEDGE);
  925. cyEdge = GetSystemMetrics(SM_CYEDGE);
  926. cxBorder = GetSystemMetrics(SM_CXBORDER);
  927. cyBorder = GetSystemMetrics(SM_CYBORDER);
  928. GetClientRect(pbutn->ci.hwnd, lprc);
  929. wAlign = GetAlignment(pbutn);
  930. switch (iCode)
  931. {
  932. case CBR_PUSHBUTTON:
  933. //
  934. // Subtract out raised edge all around
  935. //
  936. InflateRect(lprc, -cxEdge, -cyEdge);
  937. if (uFlags & PBF_DEFAULT)
  938. {
  939. InflateRect(lprc, -cxBorder, -cyBorder);
  940. }
  941. break;
  942. case CBR_CHECKBOX:
  943. case CBR_RADIOBUTTON:
  944. {
  945. SIZE sizeChk = {0};
  946. GetCheckBoxSize(hdc, pbutn, (iCode == CBR_CHECKBOX), &sizeChk);
  947. switch (wAlign & HIBYTE(BS_VERTMASK))
  948. {
  949. case HIBYTE(BS_VCENTER):
  950. lprc->top = (lprc->top + lprc->bottom - sizeChk.cy) / 2;
  951. break;
  952. case HIBYTE(BS_TOP):
  953. case HIBYTE(BS_BOTTOM):
  954. GetTextExtentPoint32(hdc, (LPTSTR)szOneChar, 1, &sizeExtent);
  955. dy = sizeExtent.cy + sizeExtent.cy/4;
  956. //
  957. // Save vertical extent
  958. //
  959. sizeExtent.cx = dy;
  960. //
  961. // Get centered amount
  962. //
  963. dy = (dy - sizeChk.cy) / 2;
  964. if ((wAlign & HIBYTE(BS_VERTMASK)) == HIBYTE(BS_TOP))
  965. {
  966. lprc->top += dy;
  967. }
  968. else
  969. {
  970. lprc->top = lprc->bottom - sizeExtent.cx + dy;
  971. }
  972. break;
  973. }
  974. if ((ulStyle & BS_RIGHTBUTTON) != 0)
  975. {
  976. lprc->left = lprc->right - sizeChk.cx;
  977. }
  978. else
  979. {
  980. lprc->right = lprc->left + sizeChk.cx;
  981. }
  982. break;
  983. }
  984. case CBR_CHECKTEXT:
  985. {
  986. SIZE sizeChk = {0};
  987. GetCheckBoxSize(hdc, pbutn, TRUE, &sizeChk);
  988. if ((ulStyle & BS_RIGHTBUTTON) != 0)
  989. {
  990. lprc->right -= sizeChk.cx;
  991. //
  992. // More spacing for 4.0 dudes
  993. //
  994. if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT))
  995. {
  996. GetTextExtentPoint32(hdc, szOneChar, 1, &sizeExtent);
  997. lprc->right -= sizeExtent.cx / 2;
  998. }
  999. }
  1000. else
  1001. {
  1002. lprc->left += sizeChk.cx;
  1003. //
  1004. // More spacing for 4.0 dudes
  1005. //
  1006. if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT))
  1007. {
  1008. GetTextExtentPoint32(hdc, szOneChar, 1, &sizeExtent);
  1009. lprc->left += sizeExtent.cx / 2;
  1010. }
  1011. }
  1012. break;
  1013. }
  1014. case CBR_GROUPTEXT:
  1015. {
  1016. int cch = GetWindowTextLength(pbutn->ci.hwnd);
  1017. if (cch != 0)
  1018. {
  1019. //
  1020. // Always use WCHAR for alloc because of MBCS
  1021. //
  1022. lpName = _alloca((cch+1)*sizeof(WCHAR));
  1023. }
  1024. if (cch == 0 || lpName == NULL || GetWindowText(pbutn->ci.hwnd, lpName, cch+1) == 0)
  1025. {
  1026. SetRectEmpty(lprc);
  1027. break;
  1028. }
  1029. //
  1030. // if not themed
  1031. //
  1032. if (!Button_IsThemed(pbutn))
  1033. {
  1034. GetTextExtentPoint32(hdc, lpName, cch, &sizeExtent);
  1035. }
  1036. else
  1037. {
  1038. DWORD dwTextFlags = Button_GetTextFlags(pbutn);
  1039. RECT rcExtent;
  1040. GetThemeTextExtent(pbutn->hTheme,
  1041. hdc,
  1042. BP_GROUPBOX,
  1043. 0,
  1044. lpName,
  1045. cch,
  1046. dwTextFlags,
  1047. lprc,
  1048. &rcExtent);
  1049. sizeExtent.cx = RECTWIDTH(rcExtent);
  1050. sizeExtent.cy = RECTHEIGHT(rcExtent);
  1051. }
  1052. sizeExtent.cx += GetSystemMetrics(SM_CXEDGE) * 2;
  1053. switch (wAlign & HIBYTE(BS_HORZMASK))
  1054. {
  1055. //
  1056. // BFLEFT, nothing
  1057. //
  1058. case HIBYTE(BS_LEFT):
  1059. lprc->left += (SYSFONT_CXCHAR - GetSystemMetrics(SM_CXBORDER));
  1060. lprc->right = lprc->left + (int)(sizeExtent.cx);
  1061. break;
  1062. case HIBYTE(BS_RIGHT):
  1063. lprc->right -= (SYSFONT_CXCHAR - GetSystemMetrics(SM_CXBORDER));
  1064. lprc->left = lprc->right - (int)(sizeExtent.cx);
  1065. break;
  1066. case HIBYTE(BS_CENTER):
  1067. lprc->left = (lprc->left + lprc->right - (int)(sizeExtent.cx)) / 2;
  1068. lprc->right = lprc->left + (int)(sizeExtent.cx);
  1069. break;
  1070. }
  1071. //
  1072. // Center aligned.
  1073. //
  1074. lprc->bottom = lprc->top + sizeExtent.cy + GetSystemMetrics(SM_CYEDGE);
  1075. break;
  1076. }
  1077. case CBR_GROUPFRAME:
  1078. GetTextExtentPoint32(hdc, (LPTSTR)szOneChar, 1, &sizeExtent);
  1079. lprc->top += sizeExtent.cy / 2;
  1080. break;
  1081. }
  1082. }
  1083. //---------------------------------------------------------------------------//
  1084. //
  1085. // Button_MultiExtent()
  1086. //
  1087. // Calculates button text extent, given alignment flags.
  1088. //
  1089. VOID Button_MultiExtent(WORD wFlags, HDC hdc, LPRECT lprcMax, LPTSTR lpsz, int cch, PINT pcx, PINT pcy)
  1090. {
  1091. RECT rc;
  1092. UINT dtFlags = DT_CALCRECT | DT_WORDBREAK | DT_EDITCONTROL;
  1093. CopyRect(&rc, lprcMax);
  1094. //
  1095. // Note that since we're just calculating the maximum dimensions,
  1096. // left-justification and top-justification are not important.
  1097. // Also, remember to leave margins horz and vert that follow our rules
  1098. // in DrawBtnText().
  1099. //
  1100. InflateRect(&rc, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYBORDER));
  1101. if ((wFlags & LOWORD(BS_HORZMASK)) == LOWORD(BS_CENTER))
  1102. {
  1103. dtFlags |= DT_CENTER;
  1104. }
  1105. if ((wFlags & LOWORD(BS_VERTMASK)) == LOWORD(BS_VCENTER))
  1106. {
  1107. dtFlags |= DT_VCENTER;
  1108. }
  1109. DrawTextEx(hdc, lpsz, cch, &rc, dtFlags, NULL);
  1110. if (pcx)
  1111. {
  1112. *pcx = rc.right-rc.left;
  1113. }
  1114. if (pcy)
  1115. {
  1116. *pcy = rc.bottom-rc.top;
  1117. }
  1118. }
  1119. //---------------------------------------------------------------------------//
  1120. //
  1121. // Button_MultiDraw()
  1122. //
  1123. // Draws multiline button text
  1124. //
  1125. BOOL Button_MultiDraw(HDC hdc, LPBTNDATA lpbd, int cch, int cx, int cy)
  1126. {
  1127. RECT rc;
  1128. UINT dtFlags = DT_WORDBREAK | DT_EDITCONTROL;
  1129. PBUTN pbutn = lpbd->pbutn;
  1130. if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
  1131. {
  1132. dtFlags |= DT_HIDEPREFIX;
  1133. }
  1134. else if (pbutn->fPaintKbdCuesOnly)
  1135. {
  1136. dtFlags |= DT_PREFIXONLY;
  1137. }
  1138. rc.left = 0;
  1139. rc.top = 0;
  1140. rc.right = cx;
  1141. rc.bottom = cy;
  1142. //
  1143. // Horizontal alignment
  1144. //
  1145. UserAssert(DT_LEFT == 0);
  1146. switch (lpbd->wFlags & LOWORD(BS_HORZMASK))
  1147. {
  1148. case LOWORD(BS_CENTER):
  1149. dtFlags |= DT_CENTER;
  1150. break;
  1151. case LOWORD(BS_RIGHT):
  1152. dtFlags |= DT_RIGHT;
  1153. break;
  1154. }
  1155. //
  1156. // Vertical alignment
  1157. //
  1158. UserAssert(DT_TOP == 0);
  1159. switch (lpbd->wFlags & LOWORD(BS_VERTMASK))
  1160. {
  1161. case LOWORD(BS_VCENTER):
  1162. dtFlags |= DT_VCENTER;
  1163. break;
  1164. case LOWORD(BS_BOTTOM):
  1165. dtFlags |= DT_BOTTOM;
  1166. break;
  1167. }
  1168. DrawTextEx(hdc, lpbd->lpsz, cch, &rc, dtFlags, NULL);
  1169. return TRUE;
  1170. }
  1171. //---------------------------------------------------------------------------//
  1172. //
  1173. BOOL Button_SetCapture(PBUTN pbutn, UINT uCodeMouse)
  1174. {
  1175. BUTTONSTATE(pbutn) |= uCodeMouse;
  1176. if (!(BUTTONSTATE(pbutn) & BST_CAPTURED))
  1177. {
  1178. SetCapture(pbutn->ci.hwnd);
  1179. BUTTONSTATE(pbutn) |= BST_CAPTURED;
  1180. //
  1181. // To prevent redundant CLICK messages, we set the INCLICK bit so
  1182. // the WM_SETFOCUS code will not do a Button_NotifyParent(BN_CLICKED).
  1183. //
  1184. BUTTONSTATE(pbutn) |= BST_INCLICK;
  1185. SetFocus(pbutn->ci.hwnd);
  1186. BUTTONSTATE(pbutn) &= ~BST_INCLICK;
  1187. }
  1188. return BUTTONSTATE(pbutn) & BST_CAPTURED;
  1189. }
  1190. //---------------------------------------------------------------------------//
  1191. //
  1192. VOID Button_NotifyParent(PBUTN pbutn, UINT uCode)
  1193. {
  1194. HWND hwndParent = GetParent(pbutn->ci.hwnd);
  1195. int iButtonId = GetWindowID(pbutn->ci.hwnd);
  1196. if ( !hwndParent )
  1197. {
  1198. hwndParent = pbutn->ci.hwnd;
  1199. }
  1200. SendMessage(hwndParent,
  1201. WM_COMMAND,
  1202. MAKELONG(iButtonId, uCode),
  1203. (LPARAM)pbutn->ci.hwnd);
  1204. }
  1205. //---------------------------------------------------------------------------//
  1206. //
  1207. VOID Button_ReleaseCapture(PBUTN pbutn, BOOL fCheck)
  1208. {
  1209. UINT uCheck;
  1210. BOOL fNotifyParent = FALSE;
  1211. ULONG ulStyle = GET_STYLE(pbutn);
  1212. if (BUTTONSTATE(pbutn) & BST_PUSHED)
  1213. {
  1214. SendMessage(pbutn->ci.hwnd, BM_SETSTATE, FALSE, 0);
  1215. if (fCheck)
  1216. {
  1217. switch (GetButtonType(ulStyle))
  1218. {
  1219. case BS_AUTOCHECKBOX:
  1220. case BS_AUTO3STATE:
  1221. uCheck = (UINT)((BUTTONSTATE(pbutn) & BST_CHECKMASK) + 1);
  1222. if (uCheck > (UINT)(GetButtonType(ulStyle) == BS_AUTO3STATE ? BST_INDETERMINATE : BST_CHECKED))
  1223. {
  1224. uCheck = BST_UNCHECKED;
  1225. }
  1226. SendMessage(pbutn->ci.hwnd, BM_SETCHECK, uCheck, 0);
  1227. break;
  1228. case BS_AUTORADIOBUTTON:
  1229. {
  1230. //
  1231. // Walk the radio buttons in the same group as us. Check ourself
  1232. // and uncheck everyone else.
  1233. //
  1234. HWND hwndNext = pbutn->ci.hwnd;
  1235. HWND hwndParent = GetParent(pbutn->ci.hwnd);
  1236. do
  1237. {
  1238. if ((UINT)SendMessage(hwndNext, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
  1239. {
  1240. SendMessage(hwndNext, BM_SETCHECK, hwndNext == pbutn->ci.hwnd, 0L);
  1241. }
  1242. hwndNext = GetNextDlgGroupItem(hwndParent, hwndNext, FALSE);
  1243. }
  1244. //
  1245. // Loop until we see ourself again
  1246. //
  1247. while (hwndNext != pbutn->ci.hwnd);
  1248. break;
  1249. }
  1250. }
  1251. fNotifyParent = TRUE;
  1252. }
  1253. }
  1254. if (BUTTONSTATE(pbutn) & BST_CAPTURED)
  1255. {
  1256. BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE);
  1257. ReleaseCapture();
  1258. }
  1259. if (fNotifyParent)
  1260. {
  1261. //
  1262. // We have to do the notification after setting the buttonstate bits.
  1263. //
  1264. Button_NotifyParent(pbutn, BN_CLICKED);
  1265. }
  1266. }
  1267. //---------------------------------------------------------------------------//
  1268. //
  1269. // Button_DrawText()
  1270. //
  1271. // Draws text of button.
  1272. //
  1273. VOID Button_DrawText(PBUTN pbutn, HDC hdc, BOOL dbt, BOOL fDepress)
  1274. {
  1275. RECT rc;
  1276. HBRUSH hbr;
  1277. LPTSTR lpName;
  1278. int x = 0, y = 0;
  1279. int cx = 0, cy = 0;
  1280. BYTE bStyle;
  1281. UINT dsFlags;
  1282. BTNDATA bdt;
  1283. UINT pbfPush;
  1284. ULONG ulStyle = GET_STYLE(pbutn);
  1285. int cch;
  1286. bStyle = GetButtonStyle(ulStyle);
  1287. if ((bStyle == LOBYTE(BS_GROUPBOX)) && (dbt == DBT_FOCUS))
  1288. {
  1289. return;
  1290. }
  1291. pbfPush = IsPushButton(pbutn);
  1292. cch = GetWindowTextLength(pbutn->ci.hwnd);
  1293. lpName = _alloca((cch + 1) * sizeof(wchar_t));
  1294. if (lpName == NULL)
  1295. {
  1296. return;
  1297. }
  1298. GetWindowText(pbutn->ci.hwnd, lpName, cch + 1);
  1299. //
  1300. // if not themed
  1301. //
  1302. if (!Button_IsThemed(pbutn))
  1303. {
  1304. if (pbfPush)
  1305. {
  1306. Button_CalcRect(pbutn, hdc, &rc, CBR_PUSHBUTTON, pbfPush);
  1307. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  1308. //
  1309. // This is because we didn't have WM_CTLCOLOR,
  1310. // CTLCOLOR_BTN actually set up the button colors. For
  1311. // old apps, CTLCOLOR_BTN needs to work like CTLCOLOR_STATIC.
  1312. //
  1313. SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
  1314. SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
  1315. hbr = GetSysColorBrush(COLOR_BTNTEXT);
  1316. }
  1317. else
  1318. {
  1319. Button_CalcRect(pbutn, hdc, &rc, mpStyleCbr[bStyle], pbfPush);
  1320. //
  1321. // Skip stuff for ownerdraw buttons, since we aren't going to
  1322. // draw text/image.
  1323. //
  1324. if (bStyle == LOBYTE(BS_OWNERDRAW))
  1325. {
  1326. goto DrawFocus;
  1327. }
  1328. else
  1329. {
  1330. hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
  1331. }
  1332. }
  1333. if (pbutn->himl)
  1334. {
  1335. int x, y;
  1336. Button_GetImagePosition(pbutn, &rc, &x, &y);
  1337. if (fDepress)
  1338. {
  1339. x += GetSystemMetrics(SM_CXBORDER);
  1340. y += GetSystemMetrics(SM_CYBORDER);
  1341. }
  1342. ImageList_Draw(pbutn->himl, 0, hdc, x, y, ILD_TRANSPARENT | (CCDPIScale(pbutn->ci)?ILD_DPISCALE:0));
  1343. }
  1344. //
  1345. // Alignment
  1346. //
  1347. bdt.wFlags = GetAlignment(pbutn);
  1348. bdt.pbutn = pbutn;
  1349. //
  1350. // Bail if we have nothing to draw
  1351. //
  1352. if ((ulStyle & BS_BITMAP) != 0)
  1353. {
  1354. BITMAP bmp;
  1355. //
  1356. // Bitmap button
  1357. //
  1358. if (!pbutn->hImage)
  1359. {
  1360. return;
  1361. }
  1362. GetObject(pbutn->hImage, sizeof(BITMAP), &bmp);
  1363. cx = bmp.bmWidth;
  1364. cy = bmp.bmHeight;
  1365. dsFlags = DST_BITMAP;
  1366. goto UseImageForName;
  1367. }
  1368. else if ((ulStyle & BS_ICON) != 0)
  1369. {
  1370. SIZE sizeIcon;
  1371. //
  1372. // Icon button
  1373. //
  1374. if (!pbutn->hImage)
  1375. {
  1376. return;
  1377. }
  1378. GetIconSize(pbutn->hImage, &sizeIcon);
  1379. cx = sizeIcon.cx;
  1380. cy = sizeIcon.cy;
  1381. dsFlags = DST_ICON;
  1382. UseImageForName:
  1383. lpName = (LPTSTR)pbutn->hImage;
  1384. cch = TRUE;
  1385. }
  1386. else
  1387. {
  1388. //
  1389. // Text button
  1390. //
  1391. if (cch == 0)
  1392. {
  1393. return;
  1394. }
  1395. if ((ulStyle & BS_MULTILINE) != 0)
  1396. {
  1397. bdt.lpsz = lpName;
  1398. Button_MultiExtent(bdt.wFlags, hdc, &rc, lpName, cch, &cx, &cy);
  1399. lpName = (LPTSTR)(LPBTNDATA)&bdt;
  1400. dsFlags = DST_COMPLEX;
  1401. }
  1402. else
  1403. {
  1404. SIZE size;
  1405. LPWSTR lpwstr = UserLocalAlloc(0, sizeof(WCHAR)*(cch+1));
  1406. //
  1407. // Try to get the text extent with mnemonics stripped.
  1408. //
  1409. if (lpwstr != NULL)
  1410. {
  1411. int iLen = StripAccelerators(lpName, lpwstr, TRUE);
  1412. GetTextExtentPoint32(hdc, lpwstr, iLen, &size);
  1413. UserLocalFree(lpwstr);
  1414. }
  1415. else
  1416. {
  1417. GetTextExtentPoint32(hdc, lpName, cch, &size);
  1418. }
  1419. cx = size.cx;
  1420. cy = size.cy;
  1421. //
  1422. // If the control doesn't need underlines, set DST_HIDEPREFIX and
  1423. // also do not show the focus indicator
  1424. //
  1425. dsFlags = DST_PREFIXTEXT;
  1426. if (TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIACCELHIDDEN))
  1427. {
  1428. dsFlags |= DSS_HIDEPREFIX;
  1429. }
  1430. else if (pbutn->fPaintKbdCuesOnly)
  1431. {
  1432. dsFlags |= DSS_PREFIXONLY;
  1433. }
  1434. }
  1435. //
  1436. // Add on a pixel or two of vertical space to make centering
  1437. // happier. That way underline won't abut focus rect unless
  1438. // spacing is really tight.
  1439. //
  1440. cy++;
  1441. }
  1442. //
  1443. // ALIGNMENT
  1444. //
  1445. //
  1446. // Horizontal
  1447. //
  1448. switch (bdt.wFlags & HIBYTE(BS_HORZMASK))
  1449. {
  1450. //
  1451. // For left & right justified, we leave a margin of CXEDGE on either
  1452. // side for eye-pleasing space.
  1453. //
  1454. case HIBYTE(BS_LEFT):
  1455. x = rc.left + GetSystemMetrics(SM_CXEDGE);
  1456. break;
  1457. case HIBYTE(BS_RIGHT):
  1458. x = rc.right - cx - GetSystemMetrics(SM_CXEDGE);
  1459. break;
  1460. default:
  1461. x = (rc.left + rc.right - cx) / 2;
  1462. break;
  1463. }
  1464. //
  1465. // Vertical
  1466. //
  1467. switch (bdt.wFlags & HIBYTE(BS_VERTMASK))
  1468. {
  1469. //
  1470. // For top & bottom justified, we leave a margin of CYBORDER on
  1471. // either side for more eye-pleasing space.
  1472. //
  1473. case HIBYTE(BS_TOP):
  1474. y = rc.top + GetSystemMetrics(SM_CYBORDER);
  1475. break;
  1476. case HIBYTE(BS_BOTTOM):
  1477. y = rc.bottom - cy - GetSystemMetrics(SM_CYBORDER);
  1478. break;
  1479. default:
  1480. y = (rc.top + rc.bottom - cy) / 2;
  1481. break;
  1482. }
  1483. //
  1484. // Draw the text
  1485. //
  1486. if (dbt & DBT_TEXT)
  1487. {
  1488. //
  1489. // This isn't called for USER buttons.
  1490. //
  1491. UserAssert(bStyle != LOBYTE(BS_USERBUTTON));
  1492. if (fDepress)
  1493. {
  1494. x += GetSystemMetrics(SM_CXBORDER);
  1495. y += GetSystemMetrics(SM_CYBORDER);
  1496. }
  1497. if ( !IsWindowEnabled(pbutn->ci.hwnd) )
  1498. {
  1499. UserAssert(HIBYTE(BS_ICON) == HIBYTE(BS_BITMAP));
  1500. if (GetSystemMetrics(SM_SLOWMACHINE) &&
  1501. ((ulStyle & (BS_ICON | BS_BITMAP)) != 0) &&
  1502. (GetBkColor(hdc) != GetSysColor(COLOR_GRAYTEXT)))
  1503. {
  1504. //
  1505. // Perf && consistency with menus, statics
  1506. //
  1507. SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
  1508. }
  1509. else
  1510. {
  1511. dsFlags |= DSS_DISABLED;
  1512. }
  1513. }
  1514. //
  1515. // Use transparent mode for checked push buttons since we're going to
  1516. // fill background with dither.
  1517. //
  1518. if (pbfPush)
  1519. {
  1520. switch (BUTTONSTATE(pbutn) & BST_CHECKMASK)
  1521. {
  1522. case BST_INDETERMINATE:
  1523. hbr = GetSysColorBrush(COLOR_GRAYTEXT);
  1524. dsFlags |= DSS_MONO;
  1525. //
  1526. // FALL THRU
  1527. //
  1528. case BST_CHECKED:
  1529. //
  1530. // Drawing on dithered background...
  1531. //
  1532. SetBkMode(hdc, TRANSPARENT);
  1533. break;
  1534. }
  1535. }
  1536. //
  1537. // Use brush and colors currently selected into hdc when we grabbed
  1538. // color
  1539. //
  1540. DrawState(hdc, hbr, (DRAWSTATEPROC)Button_MultiDraw, (LPARAM)lpName,
  1541. (WPARAM)cch, x, y, cx, cy,
  1542. dsFlags);
  1543. }
  1544. }
  1545. // Draw focus rect.
  1546. //
  1547. // This can get called for OWNERDRAW and USERDRAW buttons. However, only
  1548. // OWNERDRAW buttons let the owner change the drawing of the focus button.
  1549. DrawFocus:
  1550. if (dbt & DBT_FOCUS)
  1551. {
  1552. if (bStyle == LOBYTE(BS_OWNERDRAW))
  1553. {
  1554. //
  1555. // For ownerdraw buttons, this is only called in response to a
  1556. // WM_SETFOCUS or WM_KILL FOCUS message. So, we can check the
  1557. // new state of the focus by looking at the BUTTONSTATE bits
  1558. // which are set before this procedure is called.
  1559. //
  1560. Button_OwnerDraw(pbutn, hdc, ODA_FOCUS);
  1561. }
  1562. else
  1563. {
  1564. //
  1565. // Don't draw the focus if underlines are not turned on
  1566. //
  1567. if (!TESTFLAG(GET_EXSTYLE(pbutn), WS_EXP_UIFOCUSHIDDEN))
  1568. {
  1569. //
  1570. // Let focus rect always hug edge of push buttons. We already
  1571. // have the client area setup for push buttons, so we don't have
  1572. // to do anything.
  1573. //
  1574. if (!pbfPush)
  1575. {
  1576. RECT rcClient;
  1577. GetClientRect(pbutn->ci.hwnd, &rcClient);
  1578. if (bStyle == LOBYTE(BS_USERBUTTON))
  1579. {
  1580. CopyRect(&rc, &rcClient);
  1581. }
  1582. else if (Button_IsThemed(pbutn))
  1583. {
  1584. //
  1585. // if themed
  1586. //
  1587. int iPartId = 0;
  1588. int iStateId = 0;
  1589. Button_GetThemeIds(pbutn, &iPartId, &iStateId);
  1590. GetThemeBackgroundContentRect(pbutn->hTheme,
  1591. hdc,
  1592. iPartId,
  1593. iStateId,
  1594. &rcClient,
  1595. &rc);
  1596. GetThemeTextExtent(pbutn->hTheme,
  1597. hdc,
  1598. iPartId,
  1599. iStateId,
  1600. lpName,
  1601. -1,
  1602. Button_GetTextFlags(pbutn),
  1603. &rc,
  1604. &rc);
  1605. //
  1606. // Inflate the bounding rect a litte, but contrained to
  1607. // within the client area.
  1608. //
  1609. rc.top = max(rcClient.top, rc.top-1);
  1610. rc.bottom = min(rcClient.bottom, rc.bottom+1);
  1611. rc.left = max(rcClient.left, rc.left-1);
  1612. rc.right = min(rcClient.right, rc.right+1);
  1613. }
  1614. else
  1615. {
  1616. //
  1617. // Try to leave a border all around text. That causes
  1618. // focus to hug text.
  1619. //
  1620. rc.top = max(rcClient.top, y-GetSystemMetrics(SM_CYBORDER));
  1621. rc.bottom = min(rcClient.bottom, rc.top + GetSystemMetrics(SM_CYEDGE) + cy);
  1622. rc.left = max(rcClient.left, x-GetSystemMetrics(SM_CXBORDER));
  1623. rc.right = min(rcClient.right, rc.left + GetSystemMetrics(SM_CXEDGE) + cx);
  1624. }
  1625. }
  1626. else
  1627. {
  1628. InflateRect(&rc, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYBORDER));
  1629. }
  1630. //
  1631. // Are back & fore colors set properly?
  1632. //
  1633. DrawFocusRect(hdc, &rc);
  1634. }
  1635. }
  1636. }
  1637. }
  1638. //---------------------------------------------------------------------------//
  1639. //
  1640. // DrawCheck()
  1641. //
  1642. VOID Button_DrawCheck(PBUTN pbutn, HDC hdc, HBRUSH hBrush)
  1643. {
  1644. //
  1645. // if not themed
  1646. //
  1647. if (!Button_IsThemed(pbutn)) // Images don't have a mask so look ugly. Need to use old painting
  1648. {
  1649. RECT rc;
  1650. UINT uFlags;
  1651. BOOL fDoubleBlt = FALSE;
  1652. ULONG ulStyle = GET_STYLE(pbutn);
  1653. SIZE sizeChk = {0};
  1654. Button_CalcRect(pbutn, hdc, &rc, CBR_CHECKBOX, 0);
  1655. uFlags = 0;
  1656. if ( BUTTONSTATE(pbutn) & BST_CHECKMASK )
  1657. {
  1658. uFlags |= DFCS_CHECKED;
  1659. }
  1660. if ( BUTTONSTATE(pbutn) & BST_PUSHED )
  1661. {
  1662. uFlags |= DFCS_PUSHED;
  1663. }
  1664. if ( !IsWindowEnabled(pbutn->ci.hwnd) )
  1665. {
  1666. uFlags |= DFCS_INACTIVE;
  1667. }
  1668. switch (GetButtonType(ulStyle))
  1669. {
  1670. case BS_AUTORADIOBUTTON:
  1671. case BS_RADIOBUTTON:
  1672. fDoubleBlt = TRUE;
  1673. uFlags |= DFCS_BUTTONRADIO;
  1674. break;
  1675. case BS_3STATE:
  1676. case BS_AUTO3STATE:
  1677. if ((BUTTONSTATE(pbutn) & BST_CHECKMASK) == BST_INDETERMINATE)
  1678. {
  1679. uFlags |= DFCS_BUTTON3STATE;
  1680. break;
  1681. }
  1682. //
  1683. // FALL THRU
  1684. //
  1685. default:
  1686. uFlags |= DFCS_BUTTONCHECK;
  1687. break;
  1688. }
  1689. if ((ulStyle & BS_FLAT) != 0)
  1690. {
  1691. uFlags |= DFCS_FLAT | DFCS_MONO;
  1692. }
  1693. GetCheckBoxSize(hdc, pbutn, TRUE, &sizeChk);
  1694. rc.right = rc.left + sizeChk.cx;
  1695. rc.bottom = rc.top + sizeChk.cy;
  1696. FillRect(hdc, &rc, hBrush);
  1697. DrawFrameControl(hdc, &rc, DFC_BUTTON, uFlags);
  1698. }
  1699. else
  1700. {
  1701. int iStateId = 0;
  1702. int iPartId = 0;
  1703. Button_GetThemeIds(pbutn, &iPartId, &iStateId);
  1704. if ((iPartId != BP_RADIOBUTTON) && (iPartId != BP_CHECKBOX))
  1705. {
  1706. TraceMsg(TF_STANDARD, "Button_DrawCheck: Not a radio or check, iPartId = %d", iPartId);
  1707. return;
  1708. }
  1709. Button_DrawThemed(pbutn, hdc, iPartId, iStateId);
  1710. }
  1711. }
  1712. //---------------------------------------------------------------------------//
  1713. //
  1714. VOID Button_DrawNewState(PBUTN pbutn, HDC hdc, HBRUSH hbr, UINT sOld)
  1715. {
  1716. if (sOld != (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED))
  1717. {
  1718. UINT pbfPush;
  1719. ULONG ulStyle = GET_STYLE(pbutn);
  1720. pbfPush = IsPushButton(pbutn);
  1721. switch (GetButtonType(ulStyle))
  1722. {
  1723. case BS_GROUPBOX:
  1724. case BS_OWNERDRAW:
  1725. break;
  1726. default:
  1727. if (!pbfPush)
  1728. {
  1729. Button_DrawCheck(pbutn, hdc, hbr);
  1730. break;
  1731. }
  1732. case BS_PUSHBUTTON:
  1733. case BS_DEFPUSHBUTTON:
  1734. case BS_PUSHBOX:
  1735. Button_DrawPush(pbutn, hdc, pbfPush);
  1736. break;
  1737. }
  1738. }
  1739. }
  1740. //---------------------------------------------------------------------------//
  1741. //
  1742. // Button_DrawPush()
  1743. //
  1744. // Draws push-like button with text
  1745. //
  1746. VOID Button_DrawPush(PBUTN pbutn, HDC hdc, UINT pbfPush)
  1747. {
  1748. //
  1749. // if not themed
  1750. //
  1751. if (!Button_IsThemed(pbutn))
  1752. {
  1753. RECT rc;
  1754. UINT uFlags = 0;
  1755. UINT uState = 0;
  1756. ULONG ulStyle = GET_STYLE(pbutn);
  1757. NMCUSTOMDRAW nmcd = {0};
  1758. //
  1759. // Always a push button if calling this function
  1760. //
  1761. uState = DFCS_BUTTONPUSH;
  1762. GetClientRect(pbutn->ci.hwnd, &rc);
  1763. nmcd.hdc = hdc;
  1764. nmcd.rc = rc;
  1765. nmcd.dwItemSpec = GetWindowID(pbutn->ci.hwnd);
  1766. nmcd.uItemState = ButtonStateToCustomDrawState(pbutn);
  1767. if (BUTTONSTATE(pbutn) & BST_PUSHED)
  1768. {
  1769. uState |= DFCS_PUSHED;
  1770. }
  1771. pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREERASE, &nmcd);
  1772. if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
  1773. {
  1774. if (!pbutn->fPaintKbdCuesOnly)
  1775. {
  1776. if (BUTTONSTATE(pbutn) & BST_CHECKMASK)
  1777. {
  1778. uState |= DFCS_CHECKED;
  1779. }
  1780. if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT))
  1781. {
  1782. uFlags = BF_SOFT;
  1783. }
  1784. if ((ulStyle & BS_FLAT) != 0)
  1785. {
  1786. uFlags |= DFCS_FLAT | DFCS_MONO;
  1787. }
  1788. if (pbfPush & PBF_DEFAULT)
  1789. {
  1790. int cxBorder = GetSystemMetrics(SM_CXBORDER);
  1791. int cyBorder = GetSystemMetrics(SM_CYBORDER);
  1792. int clFrame = 1;
  1793. int x = rc.left;
  1794. int y = rc.top;
  1795. int cxWidth = cxBorder * clFrame;
  1796. int cyWidth = cyBorder * clFrame;
  1797. int cx = rc.right - x - cxWidth;
  1798. int cy = rc.bottom - y - cyWidth;
  1799. HBRUSH hbrFill = GetSysColorBrush(COLOR_WINDOWFRAME);
  1800. HBRUSH hbrSave = SelectObject(hdc, hbrFill);
  1801. PatBlt(hdc, x, y, cxWidth, cy, PATCOPY);
  1802. PatBlt(hdc, x + cxWidth, y, cx, cyWidth, PATCOPY);
  1803. PatBlt(hdc, x, y + cy, cx, cyWidth, PATCOPY);
  1804. PatBlt(hdc, x + cx, y + cyWidth, cxWidth, cy, PATCOPY);
  1805. SelectObject(hdc, hbrSave);
  1806. InflateRect(&rc, -cxBorder, -cyBorder);
  1807. if (uState & DFCS_PUSHED)
  1808. {
  1809. uFlags |= DFCS_FLAT;
  1810. }
  1811. }
  1812. DrawFrameControl(hdc, &rc, DFC_BUTTON, uState | uFlags);
  1813. }
  1814. if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
  1815. CICustomDrawNotify(&pbutn->ci, CDDS_POSTERASE, &nmcd);
  1816. pbutn->ci.dwCustom = CICustomDrawNotify(&pbutn->ci, CDDS_PREPAINT, &nmcd);
  1817. if (!(pbutn->ci.dwCustom & CDRF_SKIPDEFAULT))
  1818. {
  1819. Button_DrawText(pbutn, hdc, DBT_TEXT | (BUTTONSTATE(pbutn) &
  1820. BST_FOCUS ? DBT_FOCUS : 0), (uState & DFCS_PUSHED));
  1821. if (pbutn->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
  1822. CICustomDrawNotify(&pbutn->ci, CDDS_POSTPAINT, &nmcd);
  1823. }
  1824. }
  1825. }
  1826. else
  1827. {
  1828. int iStateId = 0;
  1829. int iPartId = 0;
  1830. Button_GetThemeIds(pbutn, &iPartId, &iStateId);
  1831. if (iPartId != BP_PUSHBUTTON)
  1832. {
  1833. TraceMsg(TF_STANDARD, "Not a Pushbutton");
  1834. return;
  1835. }
  1836. Button_DrawThemed(pbutn, hdc, iPartId, iStateId);
  1837. }
  1838. }
  1839. BOOL Button_OnSetImageList(PBUTN pbutn, BUTTON_IMAGELIST* biml)
  1840. {
  1841. BOOL fRet = FALSE;
  1842. if (biml)
  1843. {
  1844. if (biml->himl)
  1845. {
  1846. pbutn->rcIcon = biml->margin;
  1847. pbutn->himl = biml->himl;
  1848. pbutn->uAlign = biml->uAlign;
  1849. fRet = TRUE;
  1850. }
  1851. }
  1852. return fRet;
  1853. }
  1854. void ApplyMarginsToRect(RECT* prcm, RECT* prc)
  1855. {
  1856. prc->left -= prcm->left;
  1857. prc->top -= prcm->top;
  1858. prc->right += prcm->right;
  1859. prc->bottom += prcm->bottom;
  1860. }
  1861. BOOL Button_OnGetIdealSize(PBUTN pbutn, PSIZE psize)
  1862. {
  1863. UINT bsWnd;
  1864. RECT rc = {0};
  1865. HBRUSH hBrush;
  1866. HDC hdc;
  1867. if (psize == NULL)
  1868. return FALSE;
  1869. GetWindowRect(pbutn->ci.hwnd, &rc);
  1870. hdc = GetDC (pbutn->ci.hwnd);
  1871. if (hdc)
  1872. {
  1873. ULONG ulStyle = GET_STYLE(pbutn);
  1874. bsWnd = GetButtonType(ulStyle);
  1875. hBrush = Button_InitDC(pbutn, hdc);
  1876. switch (bsWnd)
  1877. {
  1878. case BS_PUSHBUTTON:
  1879. case BS_DEFPUSHBUTTON:
  1880. {
  1881. PWSTR pName;
  1882. int cch = GetWindowTextLength(pbutn->ci.hwnd);
  1883. pName = _alloca((cch + 1) * sizeof(wchar_t));
  1884. if (pName)
  1885. {
  1886. RECT rcText={0};
  1887. RECT rcIcon={0};
  1888. int cx = 0, cy = 0;
  1889. int iStateId = 0;
  1890. int iPartId = 0;
  1891. GetWindowText(pbutn->ci.hwnd, pName, cch + 1);
  1892. if (Button_IsThemed(pbutn))
  1893. {
  1894. Button_GetThemeIds(pbutn, &iPartId, &iStateId);
  1895. // First: Get the text rect
  1896. GetThemeTextExtent(pbutn->hTheme, hdc, iPartId, iStateId, pName, cch, 0, &rcText, &rcText);
  1897. ApplyMarginsToRect(&pbutn->rcText, &rcText);
  1898. rc = rcText;
  1899. // We should now have The button with text.
  1900. }
  1901. else
  1902. {
  1903. int cxWidth = 2 * GetSystemMetrics(SM_CXEDGE);
  1904. int cyWidth = 3 * GetSystemMetrics(SM_CYEDGE);
  1905. if (IsPushButton(pbutn) & PBF_DEFAULT)
  1906. {
  1907. cxWidth += 2 * GetSystemMetrics(SM_CXBORDER);
  1908. cyWidth += 2 * GetSystemMetrics(SM_CXBORDER);
  1909. }
  1910. DrawText(hdc, pName, cch, &rcText, DT_CALCRECT);
  1911. ApplyMarginsToRect(&pbutn->rcText, &rcText);
  1912. rcText.bottom += cyWidth + 1; // +1 because draw text adds a single pixel to the first char, but not the last...
  1913. rcText.right += cxWidth + 1;
  1914. }
  1915. if (pbutn->himl)
  1916. {
  1917. rc.top = rc.left = 0; // We turn this into a width not a position
  1918. CCGetIconSize(&pbutn->ci, pbutn->himl, &cx, &cy);
  1919. rcIcon.bottom = cy;
  1920. rcIcon.right = cx;
  1921. ApplyMarginsToRect(&pbutn->rcIcon, &rcIcon);
  1922. switch (pbutn->uAlign)
  1923. {
  1924. case BUTTON_IMAGELIST_ALIGN_TOP:
  1925. case BUTTON_IMAGELIST_ALIGN_BOTTOM:
  1926. rc.bottom = RECTHEIGHT(rcIcon) + RECTHEIGHT(rcText);
  1927. rc.right = max(RECTWIDTH(rcIcon), RECTWIDTH(rcText));
  1928. break;
  1929. case BUTTON_IMAGELIST_ALIGN_CENTER: // This means no text
  1930. rc.bottom = RECTHEIGHT(rcIcon);
  1931. rc.right = RECTWIDTH(rcIcon);
  1932. break;
  1933. case BUTTON_IMAGELIST_ALIGN_RIGHT:
  1934. case BUTTON_IMAGELIST_ALIGN_LEFT:
  1935. // Fall
  1936. default:
  1937. rc.right = RECTWIDTH(rcIcon) + RECTWIDTH(rcText);
  1938. rc.bottom = max(RECTHEIGHT(rcIcon), RECTHEIGHT(rcText));
  1939. break;
  1940. }
  1941. }
  1942. else
  1943. {
  1944. rc = rcText;
  1945. }
  1946. if (Button_IsThemed(pbutn))
  1947. {
  1948. GetThemeBackgroundExtent(pbutn->hTheme, hdc, iPartId, iStateId, &rc, &rc);
  1949. }
  1950. }
  1951. }
  1952. break;
  1953. }
  1954. //
  1955. // Release the font which may have been loaded by ButtonInitDC.
  1956. //
  1957. if (pbutn->hFont)
  1958. {
  1959. SelectObject(hdc, GetStockObject(SYSTEM_FONT));
  1960. }
  1961. ReleaseDC(pbutn->ci.hwnd, hdc);
  1962. }
  1963. psize->cx = RECTWIDTH(rc);
  1964. psize->cy = RECTHEIGHT(rc);
  1965. return TRUE;
  1966. }
  1967. //---------------------------------------------------------------------------//
  1968. //
  1969. VOID Button_Paint(PBUTN pbutn, HDC hdc)
  1970. {
  1971. RECT rc;
  1972. RECT rcText;
  1973. HBRUSH hBrush;
  1974. HBRUSH hBrushSave = NULL;
  1975. BOOL fDrawBackground = TRUE;
  1976. ULONG ulStyle = GET_STYLE(pbutn);
  1977. CCDBUFFER db = {0};
  1978. UINT bsWnd = GetButtonType(ulStyle);
  1979. UINT pbfPush = IsPushButton(pbutn);
  1980. BOOL fTransparent = FALSE;
  1981. int iPartId = 0;
  1982. int iStateId = 0;
  1983. GetClientRect(pbutn->ci.hwnd, &rc);
  1984. if (Button_IsThemed(pbutn) &&
  1985. (bsWnd != LOBYTE(BS_GROUPBOX)) &&
  1986. (bsWnd != LOBYTE(BS_OWNERDRAW)) &&
  1987. !pbutn->fPaintKbdCuesOnly)
  1988. {
  1989. hdc = CCBeginDoubleBuffer(hdc, &rc, &db);
  1990. Button_GetThemeIds(pbutn, &iPartId, &iStateId);
  1991. fTransparent = CCShouldAskForBits(&pbutn->ci, pbutn->hTheme, iPartId, iStateId);
  1992. if (fTransparent)
  1993. {
  1994. fDrawBackground = (TRUE != CCSendPrint(&pbutn->ci, hdc));
  1995. }
  1996. }
  1997. hBrush = Button_InitDC(pbutn, hdc);
  1998. if ((!pbfPush || fTransparent) && !pbutn->fPaintKbdCuesOnly &&
  1999. fDrawBackground)
  2000. {
  2001. if ((bsWnd != LOBYTE(BS_OWNERDRAW)) &&
  2002. (bsWnd != LOBYTE(BS_GROUPBOX)))
  2003. {
  2004. //
  2005. // Fill the client area with the background brush
  2006. // before we begin painting.
  2007. //
  2008. FillRect(hdc, &rc, hBrush);
  2009. }
  2010. hBrushSave = SelectObject(hdc, hBrush);
  2011. }
  2012. switch (bsWnd)
  2013. {
  2014. case BS_CHECKBOX:
  2015. case BS_RADIOBUTTON:
  2016. case BS_AUTORADIOBUTTON:
  2017. case BS_3STATE:
  2018. case BS_AUTOCHECKBOX:
  2019. case BS_AUTO3STATE:
  2020. if (!pbfPush)
  2021. {
  2022. if (!Button_IsThemed(pbutn))
  2023. {
  2024. Button_DrawText(pbutn, hdc,
  2025. DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
  2026. }
  2027. if (!pbutn->fPaintKbdCuesOnly || Button_IsThemed(pbutn))
  2028. {
  2029. Button_DrawCheck(pbutn, hdc, hBrush);
  2030. }
  2031. break;
  2032. }
  2033. //
  2034. // Fall through for PUSHLIKE buttons
  2035. //
  2036. case BS_PUSHBUTTON:
  2037. case BS_DEFPUSHBUTTON:
  2038. Button_DrawPush(pbutn, hdc, pbfPush);
  2039. break;
  2040. case BS_PUSHBOX:
  2041. Button_DrawText(pbutn, hdc,
  2042. DBT_TEXT | (BUTTONSTATE(pbutn) & BST_FOCUS ? DBT_FOCUS : 0), FALSE);
  2043. Button_DrawNewState(pbutn, hdc, hBrush, 0);
  2044. break;
  2045. case BS_USERBUTTON:
  2046. // Don't support USERBUTTON in v6. This has been superceded by OWNERDRAW in win32.
  2047. break;
  2048. case BS_OWNERDRAW:
  2049. Button_OwnerDraw(pbutn, hdc, ODA_DRAWENTIRE);
  2050. break;
  2051. case BS_GROUPBOX:
  2052. Button_CalcRect(pbutn, hdc, &rcText, CBR_GROUPTEXT, 0);
  2053. //----- get theme part, state for groupbox ----
  2054. if (Button_IsThemed(pbutn))
  2055. {
  2056. Button_GetThemeIds(pbutn, &iPartId, &iStateId);
  2057. }
  2058. if (!pbutn->fPaintKbdCuesOnly)
  2059. {
  2060. UINT uFlags;
  2061. BOOL fFillMyself = TRUE;
  2062. Button_CalcRect(pbutn, hdc, &rc, CBR_GROUPFRAME, 0);
  2063. uFlags = ((ulStyle & BS_FLAT) != 0) ? BF_FLAT | BF_MONO : 0;
  2064. if (!Button_IsThemed(pbutn))
  2065. {
  2066. DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT | uFlags);
  2067. }
  2068. else
  2069. {
  2070. DrawThemeBackground(pbutn->hTheme, hdc, iPartId, iStateId, &rc, 0);
  2071. fFillMyself = (FALSE == CCSendPrintRect(&pbutn->ci, hdc, &rcText));
  2072. }
  2073. if (fFillMyself)
  2074. {
  2075. FillRect(hdc, &rcText, hBrush);
  2076. }
  2077. }
  2078. // FillRect(hdc, &rc, hBrush);
  2079. if (!Button_IsThemed(pbutn))
  2080. {
  2081. Button_DrawText(pbutn, hdc, DBT_TEXT, FALSE);
  2082. }
  2083. else
  2084. {
  2085. DWORD dwTextFlags;
  2086. HRESULT hr;
  2087. int cch;
  2088. LPWSTR lpName;
  2089. cch = GetWindowTextLength(pbutn->ci.hwnd);
  2090. lpName = UserLocalAlloc(0, sizeof(WCHAR)*(cch+1));
  2091. if ( !lpName )
  2092. {
  2093. TraceMsg(TF_STANDARD, "Button_Paint couldn't allocate buffer");
  2094. return;
  2095. }
  2096. GetWindowTextW(pbutn->ci.hwnd, lpName, cch+1);
  2097. dwTextFlags = Button_GetTextFlags(pbutn);
  2098. //
  2099. // Button_CalcRect padded by a CXEDGE so the groupbox frame wouldn't
  2100. // be flush with the Group text
  2101. //
  2102. rcText.left += GetSystemMetrics(SM_CXEDGE);
  2103. hr = DrawThemeText(pbutn->hTheme,
  2104. hdc,
  2105. iPartId,
  2106. iStateId,
  2107. lpName,
  2108. cch,
  2109. dwTextFlags,
  2110. 0,
  2111. &rcText);
  2112. if (FAILED(hr))
  2113. {
  2114. TraceMsg(TF_STANDARD, "Button_Paint failed to render groupbox text");
  2115. }
  2116. UserLocalFree(lpName);
  2117. }
  2118. break;
  2119. }
  2120. if (!pbfPush && hBrushSave)
  2121. {
  2122. SelectObject(hdc, hBrushSave);
  2123. }
  2124. //
  2125. // Release the font which may have been loaded by ButtonInitDC.
  2126. //
  2127. if (pbutn->hFont)
  2128. {
  2129. SelectObject(hdc, GetStockObject(SYSTEM_FONT));
  2130. }
  2131. CCEndDoubleBuffer(&db);
  2132. }
  2133. //---------------------------------------------------------------------------//
  2134. //
  2135. VOID Button_Repaint(PBUTN pbutn)
  2136. {
  2137. HDC hdc = Button_GetDC(pbutn, NULL);
  2138. if (hdc != NULL)
  2139. {
  2140. Button_Paint(pbutn, hdc);
  2141. Button_ReleaseDC(pbutn, hdc, NULL);
  2142. }
  2143. }
  2144. VOID Button_SetHot(PBUTN pbutn, BOOL fHot, DWORD dwReason)
  2145. {
  2146. NMBCHOTITEM nmhot = {0};
  2147. // Send a notification about the hot item change
  2148. if (fHot)
  2149. {
  2150. nmhot.dwFlags = HICF_ENTERING;
  2151. pbutn->buttonState |= BST_HOT;
  2152. }
  2153. else
  2154. {
  2155. nmhot.dwFlags = HICF_LEAVING;
  2156. pbutn->buttonState &= ~BST_HOT;
  2157. }
  2158. nmhot.dwFlags |= dwReason;
  2159. CCSendNotify(&pbutn->ci, BCN_HOTITEMCHANGE, &nmhot.hdr);
  2160. }
  2161. void Button_EraseOwnerDraw(PBUTN pbutn, HDC hdc)
  2162. {
  2163. if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_OWNERDRAW))
  2164. {
  2165. RECT rc;
  2166. HBRUSH hbr;
  2167. //
  2168. // Handle erase background for owner draw buttons.
  2169. //
  2170. GetClientRect(pbutn->ci.hwnd, &rc);
  2171. hbr = (HBRUSH)SendMessage(GetParent(pbutn->ci.hwnd), WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)pbutn->ci.hwnd);
  2172. FillRect(hdc, &rc, hbr);
  2173. }
  2174. }
  2175. //---------------------------------------------------------------------------//
  2176. //
  2177. // Button_WndProc
  2178. //
  2179. // WndProc for buttons, check boxes, etc.
  2180. //
  2181. LRESULT APIENTRY Button_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2182. {
  2183. UINT wOldState;
  2184. RECT rc;
  2185. HDC hdc;
  2186. HBRUSH hbr;
  2187. PAINTSTRUCT ps;
  2188. PBUTN pbutn;
  2189. LRESULT lResult = FALSE;
  2190. //
  2191. // Get the instance data for this button control
  2192. //
  2193. pbutn = Button_GetPtr(hwnd);
  2194. if (!pbutn && uMsg != WM_NCCREATE)
  2195. {
  2196. goto CallDWP;
  2197. }
  2198. switch (uMsg)
  2199. {
  2200. case WM_NCHITTEST:
  2201. if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_GROUPBOX))
  2202. {
  2203. lResult = (LONG)HTTRANSPARENT;
  2204. }
  2205. else
  2206. {
  2207. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  2208. if ( lResult == HTCLIENT && Button_IsThemed(pbutn))
  2209. {
  2210. HRESULT hr;
  2211. int iPartId = 0;
  2212. int iStateId = 0;
  2213. POINT pt;
  2214. WORD wHitTestCode;
  2215. hr = Button_GetThemeIds(pbutn, &iPartId, &iStateId);
  2216. if ( SUCCEEDED(hr) )
  2217. GetWindowRect(pbutn->ci.hwnd, &rc);
  2218. pt.x = GET_X_LPARAM(lParam);
  2219. pt.y = GET_Y_LPARAM(lParam);
  2220. hr = HitTestThemeBackground(pbutn->hTheme,
  2221. NULL,
  2222. iPartId,
  2223. iStateId,
  2224. 0,
  2225. &rc,
  2226. NULL,
  2227. pt,
  2228. &wHitTestCode);
  2229. if ( SUCCEEDED(hr) && wHitTestCode == HTTRANSPARENT)
  2230. {
  2231. lResult = (LRESULT)HTTRANSPARENT;
  2232. }
  2233. }
  2234. }
  2235. break;
  2236. case WM_ERASEBKGND:
  2237. Button_EraseOwnerDraw(pbutn, (HDC)wParam);
  2238. //
  2239. // Do nothing for other buttons, but don't let DefWndProc() do it
  2240. // either. It will be erased in Button_Paint().
  2241. //
  2242. lResult = (LONG)TRUE;
  2243. break;
  2244. case WM_PRINTCLIENT:
  2245. Button_EraseOwnerDraw(pbutn, (HDC)wParam);
  2246. Button_Paint(pbutn, (HDC)wParam);
  2247. break;
  2248. case WM_CREATE:
  2249. pbutn->hTheme = Button_GetTheme(pbutn);
  2250. CIInitialize(&pbutn->ci, hwnd, (LPCREATESTRUCT)lParam);
  2251. SendMessage(hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
  2252. break;
  2253. case WM_PAINT:
  2254. {
  2255. //
  2256. // If wParam != NULL, then this is a subclassed paint.
  2257. //
  2258. if (wParam)
  2259. {
  2260. hdc = (HDC)wParam;
  2261. }
  2262. else
  2263. {
  2264. hdc = BeginPaint(hwnd, &ps);
  2265. }
  2266. if (IsWindowVisible(pbutn->ci.hwnd))
  2267. {
  2268. Button_Paint(pbutn, hdc);
  2269. }
  2270. if (!wParam)
  2271. {
  2272. EndPaint(hwnd, &ps);
  2273. }
  2274. }
  2275. break;
  2276. case WM_SETFOCUS:
  2277. BUTTONSTATE(pbutn) |= BST_FOCUS;
  2278. if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_OWNERDRAW))
  2279. {
  2280. HDC hdc = Button_GetDC(pbutn, NULL);
  2281. if (hdc)
  2282. {
  2283. Button_DrawText(pbutn, hdc, DBT_FOCUS, FALSE);
  2284. Button_ReleaseDC(pbutn, hdc, NULL);
  2285. }
  2286. }
  2287. else
  2288. {
  2289. InvalidateRect(pbutn->ci.hwnd, NULL, FALSE);
  2290. }
  2291. if ((GET_STYLE(pbutn)& BS_NOTIFY) != 0)
  2292. {
  2293. Button_NotifyParent(pbutn, BN_SETFOCUS);
  2294. }
  2295. if (!(BUTTONSTATE(pbutn) & BST_INCLICK))
  2296. {
  2297. switch (GetButtonType(GET_STYLE(pbutn)))
  2298. {
  2299. case LOBYTE(BS_RADIOBUTTON):
  2300. case LOBYTE(BS_AUTORADIOBUTTON):
  2301. if (!(BUTTONSTATE(pbutn) & BST_DONTCLICK))
  2302. {
  2303. if (!(BUTTONSTATE(pbutn) & BST_CHECKMASK))
  2304. {
  2305. Button_NotifyParent(pbutn, BN_CLICKED);
  2306. }
  2307. }
  2308. break;
  2309. }
  2310. }
  2311. break;
  2312. case WM_GETDLGCODE:
  2313. lResult = DLGC_BUTTON;
  2314. switch (GetButtonType(GET_STYLE(pbutn)))
  2315. {
  2316. case LOBYTE(BS_DEFPUSHBUTTON):
  2317. lResult |= DLGC_DEFPUSHBUTTON;
  2318. break;
  2319. case LOBYTE(BS_PUSHBUTTON):
  2320. case LOBYTE(BS_PUSHBOX):
  2321. lResult |= DLGC_UNDEFPUSHBUTTON;
  2322. break;
  2323. case LOBYTE(BS_AUTORADIOBUTTON):
  2324. case LOBYTE(BS_RADIOBUTTON):
  2325. lResult |= DLGC_RADIOBUTTON;
  2326. break;
  2327. case LOBYTE(BS_GROUPBOX):
  2328. //
  2329. // remove DLGC_BUTTON
  2330. //
  2331. lResult = DLGC_STATIC;
  2332. break;
  2333. case LOBYTE(BS_CHECKBOX):
  2334. case LOBYTE(BS_AUTOCHECKBOX):
  2335. //
  2336. // If this is a char that is a '=/+', or '-', we want it
  2337. //
  2338. if (lParam && ((LPMSG)lParam)->message == WM_CHAR)
  2339. {
  2340. switch (wParam)
  2341. {
  2342. case TEXT('='):
  2343. case TEXT('+'):
  2344. case TEXT('-'):
  2345. lResult |= DLGC_WANTCHARS;
  2346. break;
  2347. }
  2348. }
  2349. break;
  2350. }
  2351. break;
  2352. case WM_CAPTURECHANGED:
  2353. if (BUTTONSTATE(pbutn) & BST_CAPTURED)
  2354. {
  2355. //
  2356. // Unwittingly, we've been kicked out of capture,
  2357. // so undepress etc.
  2358. //
  2359. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  2360. {
  2361. SendMessage(pbutn->ci.hwnd, BM_SETSTATE, FALSE, 0);
  2362. }
  2363. BUTTONSTATE(pbutn) &= ~(BST_CAPTURED | BST_MOUSE);
  2364. }
  2365. break;
  2366. case WM_KILLFOCUS:
  2367. //
  2368. // If we are losing the focus and we are in "capture mode", click
  2369. // the button. This allows tab and space keys to overlap for
  2370. // fast toggle of a series of buttons.
  2371. //
  2372. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  2373. {
  2374. //
  2375. // If for some reason we are killing the focus, and we have the
  2376. // mouse captured, don't notify the parent we got clicked. This
  2377. // breaks Omnis Quartz otherwise.
  2378. //
  2379. SendMessage(pbutn->ci.hwnd, BM_SETSTATE, FALSE, 0);
  2380. }
  2381. Button_ReleaseCapture(pbutn, TRUE);
  2382. BUTTONSTATE(pbutn) &= ~BST_FOCUS;
  2383. if ((GET_STYLE(pbutn) & BS_NOTIFY) != 0)
  2384. {
  2385. Button_NotifyParent(pbutn, BN_KILLFOCUS);
  2386. }
  2387. //
  2388. // Since the bold border around the defpushbutton is done by
  2389. // someone else, we need to invalidate the rect so that the
  2390. // focus rect is repainted properly.
  2391. //
  2392. InvalidateRect(hwnd, NULL, FALSE);
  2393. break;
  2394. case WM_LBUTTONDBLCLK:
  2395. //
  2396. // Double click messages are recognized for BS_RADIOBUTTON,
  2397. // BS_USERBUTTON, and BS_OWNERDRAW styles. For all other buttons,
  2398. // double click is handled like a normal button down.
  2399. //
  2400. switch (GetButtonType(GET_STYLE(pbutn)))
  2401. {
  2402. default:
  2403. if ((GET_STYLE(pbutn) & BS_NOTIFY) == 0)
  2404. goto btnclick;
  2405. case LOBYTE(BS_USERBUTTON):
  2406. case LOBYTE(BS_RADIOBUTTON):
  2407. case LOBYTE(BS_OWNERDRAW):
  2408. Button_NotifyParent(pbutn, BN_DOUBLECLICKED);
  2409. break;
  2410. }
  2411. break;
  2412. case WM_LBUTTONUP:
  2413. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  2414. {
  2415. Button_ReleaseCapture(pbutn, TRUE);
  2416. }
  2417. break;
  2418. case WM_MOUSELEAVE:
  2419. {
  2420. //
  2421. // We should only be requesting mouseleave messages
  2422. // if we are themed but check anyway
  2423. //
  2424. if (pbutn->buttonState & BST_HOT)
  2425. {
  2426. Button_SetHot(pbutn, FALSE, HICF_MOUSE);
  2427. InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
  2428. }
  2429. }
  2430. break;
  2431. case WM_MOUSEMOVE:
  2432. {
  2433. //
  2434. // If the hot bit is not already set
  2435. //
  2436. // 300925: Can't hottrack ownerdraw buttons for app compat reasons
  2437. //
  2438. if (!TESTFLAG(pbutn->buttonState, BST_HOT) &&
  2439. GetButtonType(GET_STYLE(pbutn)) != LOBYTE(BS_OWNERDRAW))
  2440. {
  2441. TRACKMOUSEEVENT tme;
  2442. //
  2443. // Set the hot bit and request that
  2444. // we be notified when the mouse leaves
  2445. //
  2446. Button_SetHot(pbutn, TRUE, HICF_MOUSE);
  2447. tme.cbSize = sizeof(tme);
  2448. tme.dwFlags = TME_LEAVE;
  2449. tme.hwndTrack = pbutn->ci.hwnd;
  2450. tme.dwHoverTime = 0;
  2451. TrackMouseEvent(&tme);
  2452. InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
  2453. }
  2454. if (!(BUTTONSTATE(pbutn) & BST_MOUSE))
  2455. {
  2456. break;
  2457. }
  2458. }
  2459. //
  2460. // FALL THRU
  2461. //
  2462. case WM_LBUTTONDOWN:
  2463. btnclick:
  2464. if (Button_SetCapture(pbutn, BST_MOUSE))
  2465. {
  2466. POINT pt;
  2467. GetClientRect(pbutn->ci.hwnd, &rc);
  2468. POINTSTOPOINT(pt, lParam);
  2469. SendMessage(pbutn->ci.hwnd, BM_SETSTATE, PtInRect(&rc, pt), 0);
  2470. }
  2471. break;
  2472. case WM_CHAR:
  2473. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  2474. {
  2475. goto CallDWP;
  2476. }
  2477. if (GetButtonType(GET_STYLE(pbutn)) != LOBYTE(BS_CHECKBOX) &&
  2478. GetButtonType(GET_STYLE(pbutn)) != LOBYTE(BS_AUTOCHECKBOX))
  2479. {
  2480. goto CallDWP;
  2481. }
  2482. switch (wParam)
  2483. {
  2484. case TEXT('+'):
  2485. case TEXT('='):
  2486. //
  2487. // we must Set the check mark on.
  2488. //
  2489. wParam = 1;
  2490. goto SetCheck;
  2491. case TEXT('-'):
  2492. //
  2493. // Set the check mark off.
  2494. //
  2495. wParam = 0;
  2496. SetCheck:
  2497. //
  2498. // Must notify only if the check status changes
  2499. //
  2500. if ((WORD)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (WORD)wParam)
  2501. {
  2502. //
  2503. // We must check/uncheck only if it is AUTO
  2504. //
  2505. if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_AUTOCHECKBOX))
  2506. {
  2507. if (Button_SetCapture(pbutn, 0))
  2508. {
  2509. SendMessage(pbutn->ci.hwnd, BM_SETCHECK, wParam, 0);
  2510. Button_ReleaseCapture(pbutn, TRUE);
  2511. }
  2512. }
  2513. Button_NotifyParent(pbutn, BN_CLICKED);
  2514. }
  2515. break;
  2516. default:
  2517. goto CallDWP;
  2518. }
  2519. break;
  2520. case BCM_GETIDEALSIZE:
  2521. return Button_OnGetIdealSize(pbutn, (PSIZE)lParam);
  2522. case BCM_SETIMAGELIST:
  2523. return Button_OnSetImageList(pbutn, (BUTTON_IMAGELIST*)lParam);
  2524. case BCM_GETIMAGELIST:
  2525. {
  2526. BUTTON_IMAGELIST* biml = (BUTTON_IMAGELIST*)lParam;
  2527. if (biml)
  2528. {
  2529. biml->himl = pbutn->himl;
  2530. biml->uAlign = pbutn->uAlign;
  2531. biml->margin = pbutn->rcIcon;
  2532. return TRUE;
  2533. }
  2534. }
  2535. break;
  2536. case BCM_SETTEXTMARGIN:
  2537. {
  2538. RECT* prc = (RECT*)lParam;
  2539. if (prc)
  2540. {
  2541. pbutn->rcText = *prc;
  2542. return TRUE;
  2543. }
  2544. }
  2545. break;
  2546. case BCM_GETTEXTMARGIN:
  2547. {
  2548. RECT* prc = (RECT*)lParam;
  2549. if (prc)
  2550. {
  2551. *prc = pbutn->rcText;
  2552. return TRUE;
  2553. }
  2554. }
  2555. break;
  2556. case BM_CLICK:
  2557. //
  2558. // Don't recurse into this code!
  2559. //
  2560. if (BUTTONSTATE(pbutn) & BST_INBMCLICK)
  2561. {
  2562. break;
  2563. }
  2564. BUTTONSTATE(pbutn) |= BST_INBMCLICK;
  2565. SendMessage(pbutn->ci.hwnd, WM_LBUTTONDOWN, 0, 0);
  2566. SendMessage(pbutn->ci.hwnd, WM_LBUTTONUP, 0, 0);
  2567. BUTTONSTATE(pbutn) &= ~BST_INBMCLICK;
  2568. //
  2569. // FALL THRU
  2570. //
  2571. case WM_KEYDOWN:
  2572. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  2573. {
  2574. break;
  2575. }
  2576. if (wParam == VK_SPACE)
  2577. {
  2578. if (Button_SetCapture(pbutn, 0))
  2579. {
  2580. SendMessage(pbutn->ci.hwnd, BM_SETSTATE, TRUE, 0);
  2581. }
  2582. }
  2583. else
  2584. {
  2585. Button_ReleaseCapture(pbutn, FALSE);
  2586. }
  2587. break;
  2588. case WM_KEYUP:
  2589. case WM_SYSKEYUP:
  2590. if (BUTTONSTATE(pbutn) & BST_MOUSE)
  2591. {
  2592. goto CallDWP;
  2593. }
  2594. //
  2595. // Don't cancel the capture mode on the up of the tab in case the
  2596. // guy is overlapping tab and space keys.
  2597. //
  2598. if (wParam == VK_TAB)
  2599. {
  2600. goto CallDWP;
  2601. }
  2602. //
  2603. // WARNING: pbutn->ci.hwnd is history after this call!
  2604. //
  2605. Button_ReleaseCapture(pbutn, (wParam == VK_SPACE));
  2606. if (uMsg == WM_SYSKEYUP)
  2607. {
  2608. goto CallDWP;
  2609. }
  2610. break;
  2611. case BM_GETSTATE:
  2612. lResult = (LONG)BUTTONSTATE(pbutn);
  2613. break;
  2614. case BM_SETSTATE:
  2615. wOldState = (UINT)(BUTTONSTATE(pbutn) & BST_PUSHED);
  2616. if (wParam)
  2617. {
  2618. BUTTONSTATE(pbutn) |= BST_PUSHED;
  2619. }
  2620. else
  2621. {
  2622. BUTTONSTATE(pbutn) &= ~BST_PUSHED;
  2623. }
  2624. if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_USERBUTTON))
  2625. {
  2626. Button_NotifyParent(pbutn, (UINT)(wParam ? BN_PUSHED : BN_UNPUSHED));
  2627. }
  2628. if (wOldState != (BOOL)(BUTTONSTATE(pbutn) & BST_PUSHED))
  2629. {
  2630. // Only invalidate if the state changed.
  2631. InvalidateRect(pbutn->ci.hwnd, NULL, FALSE);
  2632. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  2633. }
  2634. break;
  2635. case BM_GETCHECK:
  2636. lResult = (LONG)(BUTTONSTATE(pbutn) & BST_CHECKMASK);
  2637. break;
  2638. case BM_SETCHECK:
  2639. switch (GetButtonType(GET_STYLE(pbutn)))
  2640. {
  2641. case LOBYTE(BS_RADIOBUTTON):
  2642. case LOBYTE(BS_AUTORADIOBUTTON):
  2643. if (wParam)
  2644. {
  2645. SetWindowState(pbutn->ci.hwnd, WS_TABSTOP);
  2646. }
  2647. else
  2648. {
  2649. ClearWindowState(pbutn->ci.hwnd, WS_TABSTOP);
  2650. }
  2651. //
  2652. // FALL THRU
  2653. //
  2654. case LOBYTE(BS_CHECKBOX):
  2655. case LOBYTE(BS_AUTOCHECKBOX):
  2656. if (wParam)
  2657. {
  2658. wParam = 1;
  2659. }
  2660. goto CheckIt;
  2661. case LOBYTE(BS_3STATE):
  2662. case LOBYTE(BS_AUTO3STATE):
  2663. if (wParam > BST_INDETERMINATE)
  2664. {
  2665. wParam = BST_INDETERMINATE;
  2666. }
  2667. CheckIt:
  2668. if ((UINT)(BUTTONSTATE(pbutn) & BST_CHECKMASK) != (UINT)wParam)
  2669. {
  2670. BUTTONSTATE(pbutn) &= ~BST_CHECKMASK;
  2671. BUTTONSTATE(pbutn) |= (UINT)wParam;
  2672. if (!IsWindowVisible(pbutn->ci.hwnd))
  2673. {
  2674. break;
  2675. }
  2676. InvalidateRect(pbutn->ci.hwnd, NULL, FALSE);
  2677. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  2678. }
  2679. break;
  2680. }
  2681. break;
  2682. case BM_SETSTYLE:
  2683. AlterWindowStyle(hwnd, BS_TYPEMASK, (DWORD)wParam);
  2684. if (lParam)
  2685. {
  2686. InvalidateRect(hwnd, NULL, TRUE);
  2687. }
  2688. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
  2689. break;
  2690. case WM_SETTEXT:
  2691. //
  2692. // In case the new group name is longer than the old name,
  2693. // this paints over the old name before repainting the group
  2694. // box with the new name.
  2695. //
  2696. if (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_GROUPBOX))
  2697. {
  2698. hdc = Button_GetDC(pbutn, &hbr);
  2699. if (hdc != NULL)
  2700. {
  2701. Button_CalcRect(pbutn, hdc, &rc, CBR_GROUPTEXT, 0);
  2702. InvalidateRect(hwnd, &rc, TRUE);
  2703. FillRect(hdc, &rc, hbr);
  2704. Button_ReleaseDC(pbutn, hdc, &hbr);
  2705. }
  2706. }
  2707. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  2708. NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_WINDOW, INDEXID_CONTAINER);
  2709. goto DoEnable;
  2710. case WM_ENABLE:
  2711. lResult = 0L;
  2712. DoEnable:
  2713. Button_Repaint(pbutn);
  2714. break;
  2715. case WM_SETFONT:
  2716. //
  2717. // wParam - handle to the font
  2718. // lParam - if true, redraw else don't
  2719. //
  2720. Button_SetFont(pbutn, (HFONT)wParam, (BOOL)(lParam != 0));
  2721. break;
  2722. case WM_GETFONT:
  2723. lResult = (LRESULT)pbutn->hFont;
  2724. break;
  2725. case BM_GETIMAGE:
  2726. case BM_SETIMAGE:
  2727. if (!IsValidImage(wParam, (GET_STYLE(pbutn) & BS_IMAGEMASK) != 0, IMAGE_BMMAX))
  2728. {
  2729. TraceMsg(TF_STANDARD, "UxButton: Invalid button image type");
  2730. SetLastError(ERROR_INVALID_PARAMETER);
  2731. }
  2732. else
  2733. {
  2734. HANDLE hOld = pbutn->hImage;
  2735. if (uMsg == BM_SETIMAGE)
  2736. {
  2737. pbutn->hImage = (HANDLE)lParam;
  2738. if (IsWindowVisible(pbutn->ci.hwnd))
  2739. {
  2740. InvalidateRect(hwnd, NULL, TRUE);
  2741. }
  2742. }
  2743. lResult = (LRESULT)hOld;
  2744. }
  2745. break;
  2746. case WM_NCDESTROY:
  2747. if (pbutn->hTheme)
  2748. {
  2749. CloseThemeData(pbutn->hTheme);
  2750. }
  2751. UserLocalFree(pbutn);
  2752. TraceMsg(TF_STANDARD, "BUTTON: Clearing button instance pointer.");
  2753. Button_SetPtr(hwnd, NULL);
  2754. break;
  2755. case WM_NCCREATE:
  2756. pbutn = (PBUTN)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(BUTN));
  2757. if (pbutn)
  2758. {
  2759. //
  2760. // Success... store the instance pointer.
  2761. //
  2762. TraceMsg(TF_STANDARD, "BUTTON: Setting button instance pointer.");
  2763. Button_SetPtr(hwnd, pbutn);
  2764. pbutn->ci.hwnd = hwnd;
  2765. pbutn->pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);
  2766. SetRect(&pbutn->rcText, GetSystemMetrics(SM_CXEDGE) / 2, GetSystemMetrics(SM_CYEDGE) / 2,
  2767. GetSystemMetrics(SM_CXEDGE) / 2, GetSystemMetrics(SM_CYEDGE) / 2);
  2768. //
  2769. // Borland's OBEX has a button with style 0x98; We didn't strip
  2770. // these bits in win3.1 because we checked for 0x08.
  2771. // Stripping these bits cause a GP Fault in OBEX.
  2772. // For win3.1 guys, I use the old code to strip the style bits.
  2773. //
  2774. if (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN31COMPAT))
  2775. {
  2776. if ((!TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT) &&
  2777. (((LOBYTE(GET_STYLE(pbutn))) & (LOBYTE(~BS_LEFTTEXT))) == LOBYTE(BS_USERBUTTON))) ||
  2778. (TESTFLAG(GET_STATE2(pbutn), WS_S2_WIN40COMPAT) &&
  2779. (GetButtonType(GET_STYLE(pbutn)) == LOBYTE(BS_USERBUTTON))))
  2780. {
  2781. //
  2782. // BS_USERBUTTON is no longer allowed for 3.1 and beyond.
  2783. // Just turn to normal push button.
  2784. //
  2785. AlterWindowStyle(hwnd, BS_TYPEMASK, 0);
  2786. TraceMsg(TF_STANDARD, "UxButton: BS_USERBUTTON no longer supported");
  2787. }
  2788. }
  2789. if ((GET_EXSTYLE(pbutn) & WS_EX_RIGHT) != 0)
  2790. {
  2791. AlterWindowStyle(hwnd, BS_RIGHT | BS_RIGHTBUTTON, BS_RIGHT | BS_RIGHTBUTTON);
  2792. }
  2793. goto CallDWP;
  2794. }
  2795. else
  2796. {
  2797. //
  2798. // Failed... return FALSE.
  2799. //
  2800. // From a WM_NCCREATE msg, this will cause the
  2801. // CreateWindow call to fail.
  2802. //
  2803. TraceMsg(TF_STANDARD, "BUTTON: Unable to allocate button instance structure.");
  2804. lResult = FALSE;
  2805. }
  2806. break;
  2807. case WM_INPUTLANGCHANGEREQUEST:
  2808. //
  2809. // #115190
  2810. // If the window is one of controls on top of dialogbox,
  2811. // let the parent dialog handle it.
  2812. //
  2813. #if 0 // Need to expose TestwndChild()
  2814. if (TestwndChild(pbutn->ci.hwnd) && pbutn->ci.hwnd->spbutn->ci.hwndParent)
  2815. {
  2816. PWND pbutn->ci.hwndParent = REBASEPWND(pbutn->ci.hwnd, spbutn->ci.hwndParent);
  2817. if (pbutn->ci.hwndParent)
  2818. {
  2819. PCLS pclsParent = REBASEALWAYS(pbutn->ci.hwndParent, pcls);
  2820. UserAssert(pclsParent != NULL);
  2821. if (pclsParent->atomClassName == gpsi->atomSysClass[ICLS_DIALOG])
  2822. {
  2823. TraceMsg(TF_STANDARD, "UxButton: WM_INPUTLANGCHANGEREQUEST is sent to parent.");
  2824. return SendMessage(pbutn->ci.hwndParent, uMsg, wParam, lParam);
  2825. }
  2826. }
  2827. }
  2828. #endif
  2829. goto CallDWP;
  2830. case WM_UPDATEUISTATE:
  2831. DefWindowProc(hwnd, uMsg, wParam, lParam);
  2832. if (ISBSTEXTOROD(GET_STYLE(pbutn)))
  2833. {
  2834. pbutn->fPaintKbdCuesOnly = !IsUsingCleartype();
  2835. Button_Repaint(pbutn);
  2836. pbutn->fPaintKbdCuesOnly = FALSE;
  2837. }
  2838. break;
  2839. case WM_GETOBJECT:
  2840. if(lParam == OBJID_QUERYCLASSNAMEIDX)
  2841. {
  2842. lResult = MSAA_CLASSNAMEIDX_BUTTON;
  2843. }
  2844. else
  2845. {
  2846. lResult = FALSE;
  2847. }
  2848. break;
  2849. case WM_THEMECHANGED:
  2850. if ( pbutn->hTheme )
  2851. {
  2852. CloseThemeData(pbutn->hTheme);
  2853. }
  2854. //---- reset cached sizes that may change with a theme change ----
  2855. sizeCheckBox.cx = 0;
  2856. sizeCheckBox.cy = 0;
  2857. sizeRadioBox.cx = 0;
  2858. sizeRadioBox.cy = 0;
  2859. pbutn->hTheme = Button_GetTheme(pbutn);
  2860. InvalidateRect(pbutn->ci.hwnd, NULL, TRUE);
  2861. CCSendNotify(&pbutn->ci, NM_THEMECHANGED, NULL);
  2862. lResult = TRUE;
  2863. break;
  2864. default:
  2865. if (CCWndProc(&pbutn->ci, uMsg, wParam, lParam, &lResult))
  2866. return lResult;
  2867. CallDWP:
  2868. lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
  2869. }
  2870. return lResult;
  2871. }