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.

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