1 /* File: button.c -- Button type widgets 2 * 3 * Copyright (C) 1993 Johannes Ruscheinski 4 * Copyright (C) 1993 David Metcalfe 5 * Copyright (C) 1994 Alexandre Julliard 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 * 21 * NOTES 22 * 23 * This code was audited for completeness against the documented features 24 * of Comctl32.dll version 6.0 on Oct. 3, 2004, by Dimitrie O. Paun. 25 * 26 * Unless otherwise noted, we believe this code to be complete, as per 27 * the specification mentioned above. 28 * If you discover missing features, or bugs, please note them below. 29 * 30 * TODO 31 * Styles 32 * - BS_NOTIFY: is it complete? 33 * - BS_RIGHTBUTTON: same as BS_LEFTTEXT 34 * 35 * Messages 36 * - WM_CHAR: Checks a (manual or automatic) check box on '+' or '=', clears it on '-' key. 37 * - WM_SETFOCUS: For (manual or automatic) radio buttons, send the parent window BN_CLICKED 38 * - WM_NCCREATE: Turns any BS_OWNERDRAW button into a BS_PUSHBUTTON button. 39 * - WM_SYSKEYUP 40 * - BCM_GETIDEALSIZE 41 * - BCM_GETIMAGELIST 42 * - BCM_GETTEXTMARGIN 43 * - BCM_SETIMAGELIST 44 * - BCM_SETTEXTMARGIN 45 * 46 * Notifications 47 * - BCN_HOTITEMCHANGE 48 * - BN_DISABLE 49 * - BN_PUSHED/BN_HILITE 50 * + BN_KILLFOCUS: is it OK? 51 * - BN_PAINT 52 * + BN_SETFOCUS: is it OK? 53 * - BN_UNPUSHED/BN_UNHILITE 54 * - NM_CUSTOMDRAW 55 * 56 * Structures/Macros/Definitions 57 * - BUTTON_IMAGELIST 58 * - NMBCHOTITEM 59 * - Button_GetIdealSize 60 * - Button_GetImageList 61 * - Button_GetTextMargin 62 * - Button_SetImageList 63 * - Button_SetTextMargin 64 */ 65 #include "comctl32.h" 66 67 #include <wine/debug.h> 68 WINE_DEFAULT_DEBUG_CHANNEL(button); 69 70 /* GetWindowLong offsets for window extra information */ 71 #define STATE_GWL_OFFSET 0 72 #define HFONT_GWL_OFFSET (sizeof(LONG)) 73 #define HIMAGE_GWL_OFFSET (HFONT_GWL_OFFSET+sizeof(HFONT)) 74 #define UISTATE_GWL_OFFSET (HIMAGE_GWL_OFFSET+sizeof(HFONT)) 75 #define NB_EXTRA_BYTES (UISTATE_GWL_OFFSET+sizeof(LONG)) 76 77 /* undocumented flags */ 78 #define BUTTON_NSTATES 0x0F 79 #define BUTTON_BTNPRESSED 0x40 80 #define BUTTON_UNKNOWN2 0x20 81 #define BUTTON_UNKNOWN3 0x10 82 #ifdef __REACTOS__ 83 #define BUTTON_BMCLICK 0x100 // ReactOS Need to up to wine! 84 #endif 85 86 #define BUTTON_NOTIFY_PARENT(hWnd, code) \ 87 do { /* Notify parent which has created this button control */ \ 88 TRACE("notification " #code " sent to hwnd=%p\n", GetParent(hWnd)); \ 89 SendMessageW(GetParent(hWnd), WM_COMMAND, \ 90 MAKEWPARAM(GetWindowLongPtrW((hWnd),GWLP_ID), (code)), \ 91 (LPARAM)(hWnd)); \ 92 } while(0) 93 94 static UINT BUTTON_CalcLabelRect( HWND hwnd, HDC hdc, RECT *rc ); 95 static void PB_Paint( HWND hwnd, HDC hDC, UINT action ); 96 static void CB_Paint( HWND hwnd, HDC hDC, UINT action ); 97 static void GB_Paint( HWND hwnd, HDC hDC, UINT action ); 98 static void UB_Paint( HWND hwnd, HDC hDC, UINT action ); 99 static void OB_Paint( HWND hwnd, HDC hDC, UINT action ); 100 static void BUTTON_CheckAutoRadioButton( HWND hwnd ); 101 102 #define MAX_BTN_TYPE 16 103 104 static const WORD maxCheckState[MAX_BTN_TYPE] = 105 { 106 BST_UNCHECKED, /* BS_PUSHBUTTON */ 107 BST_UNCHECKED, /* BS_DEFPUSHBUTTON */ 108 BST_CHECKED, /* BS_CHECKBOX */ 109 BST_CHECKED, /* BS_AUTOCHECKBOX */ 110 BST_CHECKED, /* BS_RADIOBUTTON */ 111 BST_INDETERMINATE, /* BS_3STATE */ 112 BST_INDETERMINATE, /* BS_AUTO3STATE */ 113 BST_UNCHECKED, /* BS_GROUPBOX */ 114 BST_UNCHECKED, /* BS_USERBUTTON */ 115 BST_CHECKED, /* BS_AUTORADIOBUTTON */ 116 BST_UNCHECKED, /* BS_PUSHBOX */ 117 BST_UNCHECKED /* BS_OWNERDRAW */ 118 }; 119 120 typedef void (*pfPaint)( HWND hwnd, HDC hdc, UINT action ); 121 122 static const pfPaint btnPaintFunc[MAX_BTN_TYPE] = 123 { 124 PB_Paint, /* BS_PUSHBUTTON */ 125 PB_Paint, /* BS_DEFPUSHBUTTON */ 126 CB_Paint, /* BS_CHECKBOX */ 127 CB_Paint, /* BS_AUTOCHECKBOX */ 128 CB_Paint, /* BS_RADIOBUTTON */ 129 CB_Paint, /* BS_3STATE */ 130 CB_Paint, /* BS_AUTO3STATE */ 131 GB_Paint, /* BS_GROUPBOX */ 132 UB_Paint, /* BS_USERBUTTON */ 133 CB_Paint, /* BS_AUTORADIOBUTTON */ 134 NULL, /* BS_PUSHBOX */ 135 OB_Paint /* BS_OWNERDRAW */ 136 }; 137 138 /* The original code from user32 was kept in order to make it easier to bring changes from user32 */ 139 #ifdef _USER32_ 140 /********************************************************************* 141 * button class descriptor 142 */ 143 static const WCHAR buttonW[] = {'B','u','t','t','o','n',0}; 144 const struct builtin_class_descr BUTTON_builtin_class = 145 { 146 buttonW, /* name */ 147 CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */ 148 #ifdef __REACTOS__ 149 ButtonWndProcA, /* procA */ 150 ButtonWndProcW, /* procW */ 151 #else 152 WINPROC_BUTTON, /* proc */ 153 #endif 154 NB_EXTRA_BYTES, /* extra */ 155 IDC_ARROW, /* cursor */ 156 0 /* brush */ 157 }; 158 159 160 static inline LONG get_button_state( HWND hwnd ) 161 { 162 return GetWindowLongPtrW( hwnd, STATE_GWL_OFFSET ); 163 } 164 165 static inline void set_button_state( HWND hwnd, LONG state ) 166 { 167 SetWindowLongPtrW( hwnd, STATE_GWL_OFFSET, state ); 168 } 169 170 #ifdef __REACTOS__ 171 172 static __inline void set_ui_state( HWND hwnd, LONG flags ) 173 { 174 SetWindowLongPtrW( hwnd, UISTATE_GWL_OFFSET, flags ); 175 } 176 177 static __inline LONG get_ui_state( HWND hwnd ) 178 { 179 return GetWindowLongPtrW( hwnd, UISTATE_GWL_OFFSET ); 180 } 181 182 #endif /* __REACTOS__ */ 183 184 static inline HFONT get_button_font( HWND hwnd ) 185 { 186 return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ); 187 } 188 189 static inline void set_button_font( HWND hwnd, HFONT font ) 190 { 191 SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font ); 192 } 193 194 static inline UINT get_button_type( LONG window_style ) 195 { 196 return (window_style & BS_TYPEMASK); 197 } 198 199 /* paint a button of any type */ 200 static inline void paint_button( HWND hwnd, LONG style, UINT action ) 201 { 202 if (btnPaintFunc[style] && IsWindowVisible(hwnd)) 203 { 204 HDC hdc = GetDC( hwnd ); 205 btnPaintFunc[style]( hwnd, hdc, action ); 206 ReleaseDC( hwnd, hdc ); 207 } 208 } 209 210 #else 211 212 #define NtUserAlterWindowStyle SetWindowLongPtrW 213 214 static inline void _SetButtonData(HWND hwnd, PBUTTON_DATA data) 215 { 216 SetWindowLongPtrW( hwnd, 0, (LONG)data ); 217 } 218 219 HRGN set_control_clipping( HDC hdc, const RECT *rect ) 220 { 221 RECT rc = *rect; 222 HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 ); 223 224 if (GetClipRgn( hdc, hrgn ) != 1) 225 { 226 DeleteObject( hrgn ); 227 hrgn = 0; 228 } 229 DPtoLP( hdc, (POINT *)&rc, 2 ); 230 if (GetLayout( hdc ) & LAYOUT_RTL) /* compensate for the shifting done by IntersectClipRect */ 231 { 232 rc.left++; 233 rc.right++; 234 } 235 IntersectClipRect( hdc, rc.left, rc.top, rc.right, rc.bottom ); 236 return hrgn; 237 } 238 239 BOOL BUTTON_PaintWithTheme(HTHEME theme, HWND hwnd, HDC hParamDC, LPARAM prfFlag); 240 WCHAR *get_button_text( HWND hwnd ); 241 242 static inline LONG_PTR get_button_image(HWND hwnd) 243 { 244 return _GetButtonData(hwnd)->image; 245 } 246 247 static inline LONG_PTR set_button_image(HWND hwnd, LONG_PTR image) 248 { 249 PBUTTON_DATA data = _GetButtonData(hwnd); 250 LONG_PTR ret = data->image; 251 data->image = image; 252 return ret; 253 } 254 255 static inline LONG get_button_state( HWND hwnd ) 256 { 257 return _GetButtonData(hwnd)->state; 258 } 259 260 static inline void set_button_state( HWND hwnd, LONG state ) 261 { 262 _GetButtonData(hwnd)->state = state; 263 } 264 265 static __inline void set_ui_state( HWND hwnd, LONG flags ) 266 { 267 _GetButtonData(hwnd)->ui_state = flags; 268 } 269 270 static __inline LONG get_ui_state( HWND hwnd ) 271 { 272 return _GetButtonData(hwnd)->ui_state; 273 } 274 275 static inline HFONT get_button_font( HWND hwnd ) 276 { 277 return (HFONT)_GetButtonData(hwnd)->font; 278 } 279 280 static inline void set_button_font( HWND hwnd, HFONT font ) 281 { 282 _GetButtonData(hwnd)->font = font; 283 } 284 285 static inline UINT get_button_type( LONG window_style ) 286 { 287 return (window_style & BS_TYPEMASK); 288 } 289 290 /* paint a button of any type */ 291 static inline void paint_button( HWND hwnd, LONG style, UINT action ) 292 { 293 HTHEME theme = GetWindowTheme(hwnd); 294 RECT rc; 295 HDC hdc = GetDC( hwnd ); 296 /* GetDC appears to give a dc with a clip rect that includes the whoe parent, not sure if it is correct or not. */ 297 GetClientRect(hwnd, &rc); 298 IntersectClipRect (hdc, rc.left, rc. top, rc.right, rc.bottom); 299 if (theme && BUTTON_PaintWithTheme(theme, hwnd, hdc, 0)) 300 { 301 ReleaseDC( hwnd, hdc ); 302 return; 303 } 304 if (btnPaintFunc[style] && IsWindowVisible(hwnd)) 305 { 306 btnPaintFunc[style]( hwnd, hdc, action ); 307 } 308 ReleaseDC( hwnd, hdc ); 309 } 310 311 BOOL BUTTON_GetIdealSize(HTHEME theme, HWND hwnd, SIZE* psize) 312 { 313 PBUTTON_DATA pdata; 314 HDC hdc; 315 WCHAR *text; 316 HFONT hFont = 0, hPrevFont = 0; 317 SIZE TextSize, ImageSize, ButtonSize; 318 BOOL ret = FALSE; 319 LOGFONTW logfont = {0}; 320 321 pdata = _GetButtonData(hwnd); 322 text = get_button_text( hwnd ); 323 hdc = GetDC(hwnd); 324 if (!pdata || !text || !hdc || !text[0]) 325 goto cleanup; 326 327 /* FIXME : Should use GetThemeTextExtent but unfortunately uses DrawTextW which is broken */ 328 if (theme) 329 { 330 HRESULT hr = GetThemeFont(theme, hdc, BP_PUSHBUTTON, PBS_NORMAL, TMT_FONT, &logfont); 331 if(SUCCEEDED(hr)) 332 { 333 hFont = CreateFontIndirectW(&logfont); 334 if(hFont) 335 hPrevFont = SelectObject( hdc, hFont ); 336 } 337 } 338 else 339 { 340 if (pdata->font) 341 hPrevFont = SelectObject( hdc, pdata->font ); 342 } 343 344 GetTextExtentPoint32W(hdc, text, wcslen(text), &TextSize); 345 346 if (logfont.lfHeight == -1 && logfont.lfWidth == 0 && wcscmp(logfont.lfFaceName, L"Arial") == 0 && wcsicmp(text, L"Start") == 0) 347 { 348 TextSize.cx = 5; 349 TextSize.cy = 4; 350 } 351 352 if (hPrevFont) 353 SelectObject( hdc, hPrevFont ); 354 355 TextSize.cy += pdata->rcTextMargin.top + pdata->rcTextMargin.bottom; 356 TextSize.cx += pdata->rcTextMargin.left + pdata->rcTextMargin.right; 357 358 if (pdata->imlData.himl && ImageList_GetIconSize(pdata->imlData.himl, &ImageSize.cx, &ImageSize.cy)) 359 { 360 ImageSize.cx += pdata->imlData.margin.left + pdata->imlData.margin.right; 361 ImageSize.cy += pdata->imlData.margin.top + pdata->imlData.margin.bottom; 362 } 363 else 364 { 365 ImageSize.cx = ImageSize.cy = 0; 366 } 367 368 if (theme) 369 { 370 RECT rcContents = {0}; 371 RECT rcButtonExtent = {0}; 372 rcContents.right = ImageSize.cx + TextSize.cx; 373 rcContents.bottom = max(ImageSize.cy, TextSize.cy); 374 GetThemeBackgroundExtent(theme, hdc, BP_PUSHBUTTON, PBS_NORMAL, &rcContents, &rcButtonExtent); 375 ButtonSize.cx = rcButtonExtent.right - rcButtonExtent.left; 376 ButtonSize.cy = rcButtonExtent.bottom - rcButtonExtent.top; 377 } 378 else 379 { 380 ButtonSize.cx = ImageSize.cx + TextSize.cx + 5; 381 ButtonSize.cy = max(ImageSize.cy, TextSize.cy + 7); 382 } 383 384 *psize = ButtonSize; 385 ret = TRUE; 386 387 cleanup: 388 if (hFont) 389 DeleteObject(hFont); 390 if (text) 391 HeapFree( GetProcessHeap(), 0, text ); 392 if (hdc) 393 ReleaseDC(hwnd, hdc); 394 395 return ret; 396 } 397 398 BOOL BUTTON_DrawIml(HDC hDC, BUTTON_IMAGELIST *pimlData, RECT *prc, BOOL bOnlyCalc) 399 { 400 SIZE ImageSize; 401 int left, top; 402 403 if (!pimlData->himl) 404 return FALSE; 405 406 if (!ImageList_GetIconSize(pimlData->himl, &ImageSize.cx, &ImageSize.cy)) 407 return FALSE; 408 409 if (pimlData->uAlign == BUTTON_IMAGELIST_ALIGN_LEFT) 410 { 411 left = prc->left + pimlData->margin.left; 412 top = prc->top + (prc->bottom - prc->top - ImageSize.cy) / 2; 413 prc->left = left + pimlData->margin.right + ImageSize.cx; 414 } 415 else if (pimlData->uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) 416 { 417 left = prc->right - pimlData->margin.right - ImageSize.cx; 418 top = prc->top + (prc->bottom - prc->top - ImageSize.cy) / 2; 419 prc->right = left - pimlData->margin.left; 420 } 421 else if (pimlData->uAlign == BUTTON_IMAGELIST_ALIGN_TOP) 422 { 423 left = prc->left + (prc->right - prc->left - ImageSize.cy) / 2; 424 top = prc->top + pimlData->margin.top; 425 prc->top = top + ImageSize.cy + pimlData->margin.bottom; 426 } 427 else if (pimlData->uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) 428 { 429 left = prc->left + (prc->right - prc->left - ImageSize.cy) / 2; 430 top = prc->bottom - pimlData->margin.bottom - ImageSize.cy; 431 prc->bottom = top - pimlData->margin.top; 432 } 433 434 if (!bOnlyCalc) 435 ImageList_Draw(pimlData->himl, 0, hDC, left, top, 0); 436 437 return TRUE; 438 } 439 #endif 440 441 442 /* retrieve the button text; returned buffer must be freed by caller */ 443 inline WCHAR *get_button_text( HWND hwnd ) 444 { 445 INT len = 512; 446 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ); 447 if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 ); 448 return buffer; 449 } 450 451 #ifdef __REACTOS__ 452 /* Retrieve the UI state for the control */ 453 static BOOL button_update_uistate(HWND hwnd, BOOL unicode) 454 { 455 LONG flags, prevflags; 456 457 if (unicode) 458 flags = DefWindowProcW(hwnd, WM_QUERYUISTATE, 0, 0); 459 else 460 flags = DefWindowProcA(hwnd, WM_QUERYUISTATE, 0, 0); 461 462 prevflags = get_ui_state(hwnd); 463 464 if (prevflags != flags) 465 { 466 set_ui_state(hwnd, flags); 467 return TRUE; 468 } 469 470 return FALSE; 471 } 472 #endif 473 474 /*********************************************************************** 475 * ButtonWndProc_common 476 */ 477 LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, 478 WPARAM wParam, LPARAM lParam, BOOL unicode ) 479 { 480 RECT rect; 481 POINT pt; 482 LONG style = GetWindowLongPtrW( hWnd, GWL_STYLE ); 483 UINT btn_type = get_button_type( style ); 484 LONG state; 485 HANDLE oldHbitmap; 486 #if defined(__REACTOS__) && defined(_USER32_) 487 PWND pWnd; 488 489 pWnd = ValidateHwnd(hWnd); 490 if (pWnd) 491 { 492 if (!pWnd->fnid) 493 { 494 NtUserSetWindowFNID(hWnd, FNID_BUTTON); 495 } 496 else 497 { 498 if (pWnd->fnid != FNID_BUTTON) 499 { 500 ERR("Wrong window class for Button! fnId 0x%x\n",pWnd->fnid); 501 return 0; 502 } 503 } 504 } 505 else 506 return 0; 507 #else 508 if (!IsWindow( hWnd )) return 0; 509 #endif 510 511 pt.x = (short)LOWORD(lParam); 512 pt.y = (short)HIWORD(lParam); 513 514 #ifndef _USER32_ 515 switch (uMsg) 516 { 517 case WM_NCCREATE: 518 { 519 PBUTTON_DATA data = HeapAlloc( GetProcessHeap(), 0, sizeof(BUTTON_DATA) ); 520 if (!data) 521 { 522 ERR("Failed to alloc internal button data\n"); 523 return -1; 524 } 525 526 memset(data, 0, sizeof(BUTTON_DATA)); 527 SetRect(&data->rcTextMargin, 1,1,1,1); 528 529 _SetButtonData(hWnd, data); 530 break; 531 } 532 case WM_NCDESTROY: 533 { 534 PBUTTON_DATA data = _GetButtonData(hWnd); 535 if (!data) 536 { 537 ERR("No data"); 538 return 0; 539 } 540 HeapFree( GetProcessHeap(), 0, data ); 541 _SetButtonData(hWnd, NULL); 542 } 543 case WM_CREATE: 544 OpenThemeData(hWnd, WC_BUTTONW); 545 break; 546 case WM_DESTROY: 547 CloseThemeData (GetWindowTheme(hWnd)); 548 break; 549 case WM_THEMECHANGED: 550 CloseThemeData (GetWindowTheme(hWnd)); 551 OpenThemeData(hWnd, WC_BUTTONW); 552 InvalidateRect(hWnd, NULL, TRUE); 553 break; 554 case WM_MOUSELEAVE: 555 { 556 state = get_button_state( hWnd ); 557 if (state & BST_HOT) 558 { 559 NMBCHOTITEM nmhotitem; 560 561 state &= ~BST_HOT; 562 set_button_state(hWnd, state); 563 564 nmhotitem.hdr.hwndFrom = hWnd; 565 nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); 566 nmhotitem.hdr.code = BCN_HOTITEMCHANGE; 567 nmhotitem.dwFlags = HICF_LEAVING; 568 SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); 569 570 InvalidateRect(hWnd, NULL, TRUE); 571 } 572 break; 573 } 574 case WM_MOUSEMOVE: 575 { 576 TRACKMOUSEEVENT mouse_event; 577 state = get_button_state( hWnd ); 578 if ((state & BST_HOT) == 0) 579 { 580 NMBCHOTITEM nmhotitem; 581 582 state |= BST_HOT; 583 set_button_state(hWnd, state); 584 585 nmhotitem.hdr.hwndFrom = hWnd; 586 nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); 587 nmhotitem.hdr.code = BCN_HOTITEMCHANGE; 588 nmhotitem.dwFlags = HICF_ENTERING; 589 SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); 590 591 InvalidateRect(hWnd, NULL, TRUE); 592 } 593 594 mouse_event.cbSize = sizeof(TRACKMOUSEEVENT); 595 mouse_event.dwFlags = TME_QUERY; 596 if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&TME_LEAVE)) 597 { 598 mouse_event.dwFlags = TME_LEAVE; 599 mouse_event.hwndTrack = hWnd; 600 mouse_event.dwHoverTime = 1; 601 TrackMouseEvent(&mouse_event); 602 } 603 break; 604 } 605 case BCM_GETTEXTMARGIN: 606 { 607 RECT* prc = (RECT*)lParam; 608 PBUTTON_DATA data = _GetButtonData(hWnd); 609 if (!prc || !data) 610 return FALSE; 611 *prc = data->rcTextMargin; 612 return TRUE; 613 } 614 case BCM_SETTEXTMARGIN: 615 { 616 RECT* prc = (RECT*)lParam; 617 PBUTTON_DATA data = _GetButtonData(hWnd); 618 if (!prc || !data) 619 return FALSE; 620 data->rcTextMargin = *prc; 621 return TRUE; 622 } 623 case BCM_SETIMAGELIST: 624 { 625 BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; 626 PBUTTON_DATA data = _GetButtonData(hWnd); 627 if (!data || !pimldata || !pimldata->himl) 628 return FALSE; 629 data->imlData = *pimldata; 630 return TRUE; 631 } 632 case BCM_GETIMAGELIST: 633 { 634 BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; 635 PBUTTON_DATA data = _GetButtonData(hWnd); 636 if (!data|| !pimldata) 637 return FALSE; 638 *pimldata = data->imlData; 639 return TRUE; 640 } 641 case BCM_GETIDEALSIZE: 642 { 643 HTHEME theme = GetWindowTheme(hWnd); 644 BOOL ret = FALSE; 645 SIZE* pSize = (SIZE*)lParam; 646 647 if (btn_type == BS_PUSHBUTTON || 648 btn_type == BS_DEFPUSHBUTTON || 649 btn_type == BS_USERBUTTON) 650 { 651 ret = BUTTON_GetIdealSize(theme, hWnd, pSize); 652 } 653 654 if (!ret) 655 { 656 GetClientRect(hWnd, &rect); 657 pSize->cx = rect.right; 658 pSize->cy = rect.bottom; 659 } 660 661 return TRUE; 662 } 663 } 664 665 if (!_GetButtonData(hWnd)) 666 { 667 ERR("no data!\n"); 668 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) : 669 DefWindowProcA(hWnd, uMsg, wParam, lParam); 670 } 671 672 #endif 673 674 switch (uMsg) 675 { 676 case WM_GETDLGCODE: 677 switch(btn_type) 678 { 679 case BS_USERBUTTON: 680 case BS_PUSHBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON; 681 case BS_DEFPUSHBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON; 682 case BS_RADIOBUTTON: 683 case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON; 684 case BS_GROUPBOX: return DLGC_STATIC; 685 default: return DLGC_BUTTON; 686 } 687 688 case WM_ENABLE: 689 paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); 690 break; 691 692 case WM_CREATE: 693 if (btn_type >= MAX_BTN_TYPE) 694 return -1; /* abort */ 695 696 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */ 697 if (btn_type == BS_USERBUTTON ) 698 { 699 style = (style & ~BS_TYPEMASK) | BS_PUSHBUTTON; 700 #ifdef __REACTOS__ 701 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style ); 702 #else 703 WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style ); 704 #endif 705 } 706 set_button_state( hWnd, BST_UNCHECKED ); 707 #ifdef __REACTOS__ 708 button_update_uistate( hWnd, unicode ); 709 #endif 710 return 0; 711 712 #if defined(__REACTOS__) && defined(_USER32_) 713 case WM_NCDESTROY: 714 NtUserSetWindowFNID(hWnd, FNID_DESTROY); 715 case WM_DESTROY: 716 break; 717 #endif 718 case WM_ERASEBKGND: 719 if (btn_type == BS_OWNERDRAW) 720 { 721 HDC hdc = (HDC)wParam; 722 RECT rc; 723 HBRUSH hBrush; 724 HWND parent = GetParent(hWnd); 725 if (!parent) parent = hWnd; 726 #if defined(__REACTOS__) && defined(_USER32_) 727 hBrush = GetControlColor( parent, hWnd, hdc, WM_CTLCOLORBTN); 728 #else 729 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd); 730 if (!hBrush) /* did the app forget to call defwindowproc ? */ 731 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, 732 (WPARAM)hdc, (LPARAM)hWnd); 733 #endif 734 GetClientRect(hWnd, &rc); 735 FillRect(hdc, &rc, hBrush); 736 } 737 return 1; 738 739 case WM_PRINTCLIENT: 740 case WM_PAINT: 741 { 742 PAINTSTRUCT ps; 743 HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps ); 744 #ifndef _USER32_ 745 HTHEME theme = GetWindowTheme(hWnd); 746 if (theme && BUTTON_PaintWithTheme(theme, hWnd, hdc, uMsg == WM_PRINTCLIENT ? lParam : 0)) 747 { 748 if ( !wParam ) EndPaint( hWnd, &ps ); 749 return 0; 750 } 751 #endif 752 if (btnPaintFunc[btn_type]) 753 { 754 int nOldMode = SetBkMode( hdc, OPAQUE ); 755 (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE ); 756 SetBkMode(hdc, nOldMode); /* reset painting mode */ 757 } 758 if ( !wParam ) EndPaint( hWnd, &ps ); 759 break; 760 } 761 762 case WM_KEYDOWN: 763 if (wParam == VK_SPACE) 764 { 765 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); 766 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED ); 767 SetCapture( hWnd ); 768 } 769 break; 770 771 case WM_LBUTTONDBLCLK: 772 if(style & BS_NOTIFY || 773 btn_type == BS_RADIOBUTTON || 774 btn_type == BS_USERBUTTON || 775 btn_type == BS_OWNERDRAW) 776 { 777 BUTTON_NOTIFY_PARENT(hWnd, BN_DOUBLECLICKED); 778 break; 779 } 780 /* fall through */ 781 case WM_LBUTTONDOWN: 782 SetCapture( hWnd ); 783 SetFocus( hWnd ); 784 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED ); 785 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); 786 break; 787 788 case WM_KEYUP: 789 if (wParam != VK_SPACE) 790 break; 791 /* fall through */ 792 case WM_LBUTTONUP: 793 #ifdef _REACTOS_ 794 BOOL TellParent = FALSE; //// ReactOS see note below. 795 #endif 796 state = get_button_state( hWnd ); 797 if (!(state & BUTTON_BTNPRESSED)) break; 798 state &= BUTTON_NSTATES; 799 set_button_state( hWnd, state ); 800 if (!(state & BST_PUSHED)) 801 { 802 ReleaseCapture(); 803 break; 804 } 805 SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 ); 806 GetClientRect( hWnd, &rect ); 807 if (uMsg == WM_KEYUP || PtInRect( &rect, pt )) 808 { 809 state = get_button_state( hWnd ); 810 switch(btn_type) 811 { 812 case BS_AUTOCHECKBOX: 813 SendMessageW( hWnd, BM_SETCHECK, !(state & BST_CHECKED), 0 ); 814 break; 815 case BS_AUTORADIOBUTTON: 816 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 ); 817 break; 818 case BS_AUTO3STATE: 819 SendMessageW( hWnd, BM_SETCHECK, 820 (state & BST_INDETERMINATE) ? 0 : ((state & 3) + 1), 0 ); 821 break; 822 } 823 #ifdef _REACTOS_ 824 TellParent = TRUE; // <---- Fix CORE-10194, Notify parent after capture is released. 825 #else 826 ReleaseCapture(); 827 BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED); 828 #endif 829 } 830 #ifdef _REACTOS_ 831 ReleaseCapture(); 832 if (TellParent) BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED); 833 #else 834 else 835 { 836 ReleaseCapture(); 837 } 838 #endif 839 break; 840 841 case WM_CAPTURECHANGED: 842 TRACE("WM_CAPTURECHANGED %p\n", hWnd); 843 if (hWnd == (HWND)lParam) break; 844 state = get_button_state( hWnd ); 845 if (state & BUTTON_BTNPRESSED) 846 { 847 state &= BUTTON_NSTATES; 848 set_button_state( hWnd, state ); 849 if (state & BST_PUSHED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 ); 850 } 851 break; 852 853 case WM_MOUSEMOVE: 854 if ((wParam & MK_LBUTTON) && GetCapture() == hWnd) 855 { 856 GetClientRect( hWnd, &rect ); 857 SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 ); 858 } 859 break; 860 861 case WM_SETTEXT: 862 { 863 /* Clear an old text here as Windows does */ 864 // 865 // ReactOS Note : 866 // wine Bug: http://bugs.winehq.org/show_bug.cgi?id=25790 867 // Patch: http://source.winehq.org/patches/data/70889 868 // By: Alexander LAW, Replicate Windows behavior of WM_SETTEXT handler regarding WM_CTLCOLOR* 869 // 870 #ifdef __REACTOS__ 871 if (style & WS_VISIBLE) 872 #else 873 if (IsWindowVisible(hWnd)) 874 #endif 875 { 876 HDC hdc = GetDC(hWnd); 877 HBRUSH hbrush; 878 RECT client, rc; 879 HWND parent = GetParent(hWnd); 880 UINT message = (btn_type == BS_PUSHBUTTON || 881 btn_type == BS_DEFPUSHBUTTON || 882 btn_type == BS_PUSHLIKE || 883 btn_type == BS_USERBUTTON || 884 btn_type == BS_OWNERDRAW) ? 885 WM_CTLCOLORBTN : WM_CTLCOLORSTATIC; 886 887 if (!parent) parent = hWnd; 888 #if defined(__REACTOS__) && defined(_USER32_) 889 hbrush = GetControlColor(parent, hWnd, hdc, message); 890 #else 891 hbrush = (HBRUSH)SendMessageW(parent, message, 892 (WPARAM)hdc, (LPARAM)hWnd); 893 if (!hbrush) /* did the app forget to call DefWindowProc ? */ 894 hbrush = (HBRUSH)DefWindowProcW(parent, message, 895 (WPARAM)hdc, (LPARAM)hWnd); 896 #endif 897 898 GetClientRect(hWnd, &client); 899 rc = client; 900 /* FIXME: check other BS_* handlers */ 901 if (btn_type == BS_GROUPBOX) 902 InflateRect(&rc, -7, 1); /* GB_Paint does this */ 903 BUTTON_CalcLabelRect(hWnd, hdc, &rc); 904 /* Clip by client rect bounds */ 905 if (rc.right > client.right) rc.right = client.right; 906 if (rc.bottom > client.bottom) rc.bottom = client.bottom; 907 FillRect(hdc, &rc, hbrush); 908 ReleaseDC(hWnd, hdc); 909 } 910 911 if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam ); 912 else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam ); 913 if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */ 914 InvalidateRect( hWnd, NULL, TRUE ); 915 else 916 paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); 917 return 1; /* success. FIXME: check text length */ 918 } 919 920 case WM_SETFONT: 921 set_button_font( hWnd, (HFONT)wParam ); 922 if (lParam) InvalidateRect(hWnd, NULL, TRUE); 923 break; 924 925 case WM_GETFONT: 926 return (LRESULT)get_button_font( hWnd ); 927 928 case WM_SETFOCUS: 929 TRACE("WM_SETFOCUS %p\n",hWnd); 930 set_button_state( hWnd, get_button_state(hWnd) | BST_FOCUS ); 931 #ifndef _USER32_ 932 if (btn_type != BS_OWNERDRAW) 933 InvalidateRect(hWnd, NULL, FALSE); 934 else 935 #endif 936 paint_button( hWnd, btn_type, ODA_FOCUS ); 937 if (style & BS_NOTIFY) 938 BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS); 939 break; 940 941 case WM_KILLFOCUS: 942 TRACE("WM_KILLFOCUS %p\n",hWnd); 943 state = get_button_state( hWnd ); 944 set_button_state( hWnd, state & ~BST_FOCUS ); 945 #ifdef _USER32_ 946 paint_button( hWnd, btn_type, ODA_FOCUS ); 947 #endif 948 949 if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) 950 ReleaseCapture(); 951 if (style & BS_NOTIFY) 952 BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS); 953 954 InvalidateRect( hWnd, NULL, FALSE ); 955 break; 956 957 case WM_SYSCOLORCHANGE: 958 InvalidateRect( hWnd, NULL, FALSE ); 959 break; 960 961 case BM_SETSTYLE: 962 btn_type = wParam & BS_TYPEMASK; 963 style = (style & ~BS_TYPEMASK) | btn_type; 964 #ifdef __REACTOS__ 965 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style); 966 #else 967 WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style ); 968 #endif 969 970 /* Only redraw if lParam flag is set.*/ 971 if (lParam) 972 InvalidateRect( hWnd, NULL, TRUE ); 973 974 break; 975 976 case BM_CLICK: 977 #ifdef __REACTOS__ 978 state = get_button_state(hWnd); 979 if (state & BUTTON_BMCLICK) 980 break; 981 set_button_state(hWnd, state | BUTTON_BMCLICK); // Tracked in STATE_GWL_OFFSET. 982 #endif 983 SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 ); 984 SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 ); 985 #ifdef __REACTOS__ 986 state = get_button_state(hWnd); 987 if (!(state & BUTTON_BMCLICK)) break; 988 state &= ~BUTTON_BMCLICK; 989 set_button_state(hWnd, state); 990 #endif 991 break; 992 993 case BM_SETIMAGE: 994 /* Check that image format matches button style */ 995 switch (style & (BS_BITMAP|BS_ICON)) 996 { 997 case BS_BITMAP: 998 if (wParam != IMAGE_BITMAP) return 0; 999 break; 1000 case BS_ICON: 1001 if (wParam != IMAGE_ICON) return 0; 1002 break; 1003 default: 1004 return 0; 1005 } 1006 #ifdef _USER32_ 1007 oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam ); 1008 #else 1009 oldHbitmap = (HBITMAP)set_button_image(hWnd, lParam ); 1010 #endif 1011 InvalidateRect( hWnd, NULL, FALSE ); 1012 return (LRESULT)oldHbitmap; 1013 1014 case BM_GETIMAGE: 1015 #ifdef _USER32_ 1016 return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET ); 1017 #else 1018 return get_button_image(hWnd); 1019 #endif 1020 1021 case BM_GETCHECK: 1022 return get_button_state( hWnd ) & 3; 1023 1024 case BM_SETCHECK: 1025 if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type]; 1026 state = get_button_state( hWnd ); 1027 if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON)) 1028 { 1029 #ifdef __REACTOS__ 1030 if (wParam) style |= WS_TABSTOP; 1031 else style &= ~WS_TABSTOP; 1032 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style); 1033 #else 1034 if (wParam) WIN_SetStyle( hWnd, WS_TABSTOP, 0 ); 1035 else WIN_SetStyle( hWnd, 0, WS_TABSTOP ); 1036 #endif 1037 } 1038 if ((state & 3) != wParam) 1039 { 1040 set_button_state( hWnd, (state & ~3) | wParam ); 1041 #ifdef _USER32 1042 paint_button( hWnd, btn_type, ODA_SELECT ); 1043 #else 1044 InvalidateRect(hWnd, NULL, FALSE); 1045 #endif 1046 } 1047 if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD)) 1048 BUTTON_CheckAutoRadioButton( hWnd ); 1049 break; 1050 1051 case BM_GETSTATE: 1052 return get_button_state( hWnd ); 1053 1054 case BM_SETSTATE: 1055 state = get_button_state( hWnd ); 1056 if (wParam) 1057 set_button_state( hWnd, state | BST_PUSHED ); 1058 else 1059 set_button_state( hWnd, state & ~BST_PUSHED ); 1060 1061 #ifdef _USER32_ 1062 paint_button( hWnd, btn_type, ODA_SELECT ); 1063 #else 1064 InvalidateRect(hWnd, NULL, FALSE); 1065 #endif 1066 break; 1067 1068 #ifdef __REACTOS__ 1069 case WM_UPDATEUISTATE: 1070 if (unicode) 1071 DefWindowProcW(hWnd, uMsg, wParam, lParam); 1072 else 1073 DefWindowProcA(hWnd, uMsg, wParam, lParam); 1074 1075 if (button_update_uistate(hWnd, unicode)) 1076 paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); 1077 break; 1078 #endif 1079 1080 case WM_NCHITTEST: 1081 if(btn_type == BS_GROUPBOX) return HTTRANSPARENT; 1082 /* fall through */ 1083 default: 1084 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) : 1085 DefWindowProcA(hWnd, uMsg, wParam, lParam); 1086 } 1087 return 0; 1088 } 1089 1090 #ifdef __REACTOS__ 1091 1092 /*********************************************************************** 1093 * ButtonWndProcW 1094 * The button window procedure. This is just a wrapper which locks 1095 * the passed HWND and calls the real window procedure (with a WND* 1096 * pointer pointing to the locked windowstructure). 1097 */ 1098 LRESULT WINAPI ButtonWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1099 { 1100 if (!IsWindow(hWnd)) return 0; 1101 return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, TRUE); 1102 } 1103 1104 /*********************************************************************** 1105 * ButtonWndProcA 1106 */ 1107 LRESULT WINAPI ButtonWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1108 { 1109 if (!IsWindow(hWnd)) return 0; 1110 return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, FALSE); 1111 } 1112 1113 #endif /* __REACTOS__ */ 1114 1115 /********************************************************************** 1116 * Convert button styles to flags used by DrawText. 1117 */ 1118 static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style ) 1119 { 1120 UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */ 1121 1122 /* "Convert" pushlike buttons to pushbuttons */ 1123 if (style & BS_PUSHLIKE) 1124 style &= ~BS_TYPEMASK; 1125 1126 if (!(style & BS_MULTILINE)) 1127 dtStyle |= DT_SINGLELINE; 1128 else 1129 dtStyle |= DT_WORDBREAK; 1130 1131 switch (style & BS_CENTER) 1132 { 1133 case BS_LEFT: /* DT_LEFT is 0 */ break; 1134 case BS_RIGHT: dtStyle |= DT_RIGHT; break; 1135 case BS_CENTER: dtStyle |= DT_CENTER; break; 1136 default: 1137 /* Pushbutton's text is centered by default */ 1138 if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER; 1139 /* all other flavours have left aligned text */ 1140 } 1141 1142 if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER)); 1143 1144 /* DrawText ignores vertical alignment for multiline text, 1145 * but we use these flags to align label manually. 1146 */ 1147 if (get_button_type(style) != BS_GROUPBOX) 1148 { 1149 switch (style & BS_VCENTER) 1150 { 1151 case BS_TOP: /* DT_TOP is 0 */ break; 1152 case BS_BOTTOM: dtStyle |= DT_BOTTOM; break; 1153 case BS_VCENTER: /* fall through */ 1154 default: dtStyle |= DT_VCENTER; break; 1155 } 1156 } 1157 else 1158 /* GroupBox's text is always single line and is top aligned. */ 1159 dtStyle |= DT_SINGLELINE; 1160 1161 return dtStyle; 1162 } 1163 1164 /********************************************************************** 1165 * BUTTON_CalcLabelRect 1166 * 1167 * Calculates label's rectangle depending on button style. 1168 * 1169 * Returns flags to be passed to DrawText. 1170 * Calculated rectangle doesn't take into account button state 1171 * (pushed, etc.). If there is nothing to draw (no text/image) output 1172 * rectangle is empty, and return value is (UINT)-1. 1173 */ 1174 static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc) 1175 { 1176 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1177 LONG ex_style = GetWindowLongPtrW( hwnd, GWL_EXSTYLE ); 1178 WCHAR *text; 1179 ICONINFO iconInfo; 1180 BITMAP bm; 1181 UINT dtStyle = BUTTON_BStoDT( style, ex_style ); 1182 RECT r = *rc; 1183 INT n; 1184 #ifdef __REACTOS__ 1185 PBUTTON_DATA pdata = _GetButtonData(hwnd); 1186 #endif 1187 1188 /* Calculate label rectangle according to label type */ 1189 switch (style & (BS_ICON|BS_BITMAP)) 1190 { 1191 case BS_TEXT: 1192 { 1193 HFONT hFont, hPrevFont = 0; 1194 #ifdef __REACTOS__ 1195 BOOL bHasIml; 1196 1197 bHasIml = BUTTON_DrawIml(hdc, &pdata->imlData, &r, TRUE); 1198 #endif 1199 1200 if (!(text = get_button_text( hwnd ))) goto empty_rect; 1201 if (!text[0]) 1202 { 1203 HeapFree( GetProcessHeap(), 0, text ); 1204 goto empty_rect; 1205 } 1206 1207 if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hdc, hFont ); 1208 DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT); 1209 if (hPrevFont) SelectObject( hdc, hPrevFont ); 1210 HeapFree( GetProcessHeap(), 0, text ); 1211 #ifdef __REACTOS__ 1212 if (get_ui_state(hwnd) & UISF_HIDEACCEL) 1213 dtStyle |= DT_HIDEPREFIX; 1214 1215 if (bHasIml) 1216 { 1217 if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT) 1218 r.left = pdata->imlData.margin.left; 1219 else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) 1220 r.right = pdata->imlData.margin.right; 1221 else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_TOP) 1222 r.top = pdata->imlData.margin.top; 1223 else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) 1224 r.bottom = pdata->imlData.margin.bottom; 1225 } 1226 #endif 1227 break; 1228 } 1229 1230 case BS_ICON: 1231 #ifdef _USER32_ 1232 if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo)) 1233 #else 1234 if (!GetIconInfo((HICON)get_button_image(hwnd), &iconInfo)) 1235 #endif 1236 goto empty_rect; 1237 1238 GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm); 1239 1240 r.right = r.left + bm.bmWidth; 1241 r.bottom = r.top + bm.bmHeight; 1242 1243 DeleteObject(iconInfo.hbmColor); 1244 DeleteObject(iconInfo.hbmMask); 1245 break; 1246 1247 case BS_BITMAP: 1248 #ifdef _USER32_ 1249 if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm)) 1250 #else 1251 if (!GetObjectW( (HANDLE)get_button_image(hwnd), sizeof(BITMAP), &bm)) 1252 #endif 1253 goto empty_rect; 1254 1255 r.right = r.left + bm.bmWidth; 1256 r.bottom = r.top + bm.bmHeight; 1257 break; 1258 1259 default: 1260 empty_rect: 1261 rc->right = r.left; 1262 rc->bottom = r.top; 1263 return (UINT)-1; 1264 } 1265 1266 /* Position label inside bounding rectangle according to 1267 * alignment flags. (calculated rect is always left-top aligned). 1268 * If label is aligned to any side - shift label in opposite 1269 * direction to leave extra space for focus rectangle. 1270 */ 1271 switch (dtStyle & (DT_CENTER|DT_RIGHT)) 1272 { 1273 case DT_LEFT: r.left++; r.right++; break; 1274 case DT_CENTER: n = r.right - r.left; 1275 r.left = rc->left + ((rc->right - rc->left) - n) / 2; 1276 r.right = r.left + n; break; 1277 case DT_RIGHT: n = r.right - r.left; 1278 r.right = rc->right - 1; 1279 r.left = r.right - n; 1280 break; 1281 } 1282 1283 switch (dtStyle & (DT_VCENTER|DT_BOTTOM)) 1284 { 1285 case DT_TOP: r.top++; r.bottom++; break; 1286 case DT_VCENTER: n = r.bottom - r.top; 1287 r.top = rc->top + ((rc->bottom - rc->top) - n) / 2; 1288 r.bottom = r.top + n; break; 1289 case DT_BOTTOM: n = r.bottom - r.top; 1290 r.bottom = rc->bottom - 1; 1291 r.top = r.bottom - n; 1292 break; 1293 } 1294 1295 *rc = r; 1296 return dtStyle; 1297 } 1298 1299 1300 /********************************************************************** 1301 * BUTTON_DrawTextCallback 1302 * 1303 * Callback function used by DrawStateW function. 1304 */ 1305 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy) 1306 { 1307 #ifdef _USER32_ 1308 RECT rc; 1309 1310 SetRect(&rc, 0, 0, cx, cy); 1311 DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp); 1312 return TRUE; 1313 #else 1314 HWND hwnd = (HWND)lp; 1315 RECT rc; 1316 PBUTTON_DATA pdata = _GetButtonData(hwnd); 1317 WCHAR *text = NULL; 1318 1319 if (!(text = get_button_text( hwnd ))) return TRUE; 1320 1321 SetRect(&rc, 0, 0, cx, cy); 1322 1323 BUTTON_DrawIml(hdc, &pdata->imlData, &rc, FALSE); 1324 1325 DrawTextW(hdc, text, -1, &rc, (UINT)wp); 1326 HeapFree(GetProcessHeap(), 0, text); 1327 return TRUE; 1328 #endif 1329 } 1330 1331 1332 /********************************************************************** 1333 * BUTTON_DrawLabel 1334 * 1335 * Common function for drawing button label. 1336 */ 1337 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc) 1338 { 1339 DRAWSTATEPROC lpOutputProc = NULL; 1340 LPARAM lp; 1341 WPARAM wp = 0; 1342 HBRUSH hbr = 0; 1343 UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED; 1344 LONG state = get_button_state( hwnd ); 1345 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1346 WCHAR *text = NULL; 1347 1348 /* FIXME: To draw disabled label in Win31 look-and-feel, we probably 1349 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION). 1350 * I don't have Win31 on hand to verify that, so I leave it as is. 1351 */ 1352 1353 if ((style & BS_PUSHLIKE) && (state & BST_INDETERMINATE)) 1354 { 1355 hbr = GetSysColorBrush(COLOR_GRAYTEXT); 1356 flags |= DSS_MONO; 1357 } 1358 1359 switch (style & (BS_ICON|BS_BITMAP)) 1360 { 1361 case BS_TEXT: 1362 /* DST_COMPLEX -- is 0 */ 1363 lpOutputProc = BUTTON_DrawTextCallback; 1364 #ifdef _USER32_ 1365 if (!(text = get_button_text( hwnd ))) return; 1366 lp = (LPARAM)text; 1367 #else 1368 lp = (LPARAM)hwnd; 1369 #endif 1370 1371 wp = (WPARAM)dtFlags; 1372 1373 #ifdef __REACTOS__ 1374 if (dtFlags & DT_HIDEPREFIX) 1375 flags |= DSS_HIDEPREFIX; 1376 #endif 1377 break; 1378 1379 case BS_ICON: 1380 flags |= DST_ICON; 1381 #ifdef _USER32_ 1382 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ); 1383 #else 1384 lp = get_button_image(hwnd); 1385 #endif 1386 break; 1387 1388 case BS_BITMAP: 1389 flags |= DST_BITMAP; 1390 #ifdef _USER32_ 1391 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ); 1392 #else 1393 lp = get_button_image(hwnd); 1394 #endif 1395 break; 1396 1397 default: 1398 return; 1399 } 1400 1401 DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top, 1402 rc->right - rc->left, rc->bottom - rc->top, flags); 1403 HeapFree( GetProcessHeap(), 0, text ); 1404 } 1405 1406 /********************************************************************** 1407 * Push Button Functions 1408 */ 1409 static void PB_Paint( HWND hwnd, HDC hDC, UINT action ) 1410 { 1411 RECT rc, r; 1412 UINT dtFlags, uState; 1413 HPEN hOldPen; 1414 HBRUSH hOldBrush; 1415 INT oldBkMode; 1416 COLORREF oldTxtColor; 1417 HFONT hFont; 1418 LONG state = get_button_state( hwnd ); 1419 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1420 BOOL pushedState = (state & BST_PUSHED); 1421 HWND parent; 1422 HRGN hrgn; 1423 1424 GetClientRect( hwnd, &rc ); 1425 1426 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */ 1427 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1428 parent = GetParent(hwnd); 1429 if (!parent) parent = hwnd; 1430 #if defined(__REACTOS__) && defined(_USER32_) 1431 GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); 1432 #else 1433 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); 1434 #endif 1435 1436 hrgn = set_control_clipping( hDC, &rc ); 1437 #ifdef __REACTOS__ 1438 hOldPen = SelectObject(hDC, GetStockObject(DC_PEN)); 1439 SetDCPenColor(hDC, GetSysColor(COLOR_WINDOWFRAME)); 1440 #else 1441 hOldPen = SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME)); 1442 #endif 1443 hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE)); 1444 oldBkMode = SetBkMode(hDC, TRANSPARENT); 1445 1446 if (get_button_type(style) == BS_DEFPUSHBUTTON) 1447 { 1448 if (action != ODA_FOCUS) 1449 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); 1450 InflateRect( &rc, -1, -1 ); 1451 } 1452 1453 /* completely skip the drawing if only focus has changed */ 1454 if (action == ODA_FOCUS) goto draw_focus; 1455 1456 uState = DFCS_BUTTONPUSH; 1457 1458 if (style & BS_FLAT) 1459 uState |= DFCS_MONO; 1460 else if (pushedState) 1461 { 1462 if (get_button_type(style) == BS_DEFPUSHBUTTON ) 1463 uState |= DFCS_FLAT; 1464 else 1465 uState |= DFCS_PUSHED; 1466 } 1467 1468 if (state & (BST_CHECKED | BST_INDETERMINATE)) 1469 uState |= DFCS_CHECKED; 1470 1471 DrawFrameControl( hDC, &rc, DFC_BUTTON, uState ); 1472 1473 /* draw button label */ 1474 r = rc; 1475 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r); 1476 1477 if (dtFlags == (UINT)-1L) 1478 goto cleanup; 1479 1480 if (pushedState) 1481 OffsetRect(&r, 1, 1); 1482 1483 oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) ); 1484 1485 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r); 1486 1487 SetTextColor( hDC, oldTxtColor ); 1488 1489 draw_focus: 1490 if (action == ODA_FOCUS || (state & BST_FOCUS)) 1491 { 1492 #ifdef __REACTOS__ 1493 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) 1494 { 1495 #endif 1496 InflateRect( &rc, -2, -2 ); 1497 DrawFocusRect( hDC, &rc ); 1498 #ifdef __REACTOS__ 1499 } 1500 #endif 1501 } 1502 1503 cleanup: 1504 SelectObject( hDC, hOldPen ); 1505 SelectObject( hDC, hOldBrush ); 1506 SetBkMode(hDC, oldBkMode); 1507 SelectClipRgn( hDC, hrgn ); 1508 if (hrgn) DeleteObject( hrgn ); 1509 } 1510 1511 /********************************************************************** 1512 * Check Box & Radio Button Functions 1513 */ 1514 1515 static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) 1516 { 1517 RECT rbox, rtext, client; 1518 HBRUSH hBrush; 1519 int delta, text_offset, checkBoxWidth, checkBoxHeight; 1520 UINT dtFlags; 1521 HFONT hFont; 1522 LONG state = get_button_state( hwnd ); 1523 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1524 LONG ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE ); 1525 HWND parent; 1526 HRGN hrgn; 1527 1528 if (style & BS_PUSHLIKE) 1529 { 1530 PB_Paint( hwnd, hDC, action ); 1531 return; 1532 } 1533 1534 GetClientRect(hwnd, &client); 1535 rbox = rtext = client; 1536 1537 checkBoxWidth = 12 * GetDeviceCaps( hDC, LOGPIXELSX ) / 96 + 1; 1538 checkBoxHeight = 12 * GetDeviceCaps( hDC, LOGPIXELSY ) / 96 + 1; 1539 1540 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1541 GetCharWidthW( hDC, '0', '0', &text_offset ); 1542 text_offset /= 2; 1543 1544 parent = GetParent(hwnd); 1545 if (!parent) parent = hwnd; 1546 #if defined(__REACTOS__) && defined(_USER32_) 1547 hBrush = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC); 1548 #else 1549 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, 1550 (WPARAM)hDC, (LPARAM)hwnd); 1551 if (!hBrush) /* did the app forget to call defwindowproc ? */ 1552 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, 1553 (WPARAM)hDC, (LPARAM)hwnd ); 1554 #endif 1555 hrgn = set_control_clipping( hDC, &client ); 1556 1557 if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT) 1558 { 1559 /* magic +4 is what CTL3D expects */ 1560 1561 rtext.right -= checkBoxWidth + text_offset;; 1562 rbox.left = rbox.right - checkBoxWidth; 1563 } 1564 else 1565 { 1566 rtext.left += checkBoxWidth + text_offset;; 1567 rbox.right = checkBoxWidth; 1568 } 1569 1570 /* Since WM_ERASEBKGND does nothing, first prepare background */ 1571 if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush ); 1572 if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush ); 1573 1574 /* Draw label */ 1575 client = rtext; 1576 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext); 1577 1578 /* Only adjust rbox when rtext is valid */ 1579 if (dtFlags != (UINT)-1L) 1580 { 1581 rbox.top = rtext.top; 1582 rbox.bottom = rtext.bottom; 1583 } 1584 1585 /* Draw the check-box bitmap */ 1586 if (action == ODA_DRAWENTIRE || action == ODA_SELECT) 1587 { 1588 UINT flags; 1589 1590 if ((get_button_type(style) == BS_RADIOBUTTON) || 1591 (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO; 1592 else if (state & BST_INDETERMINATE) flags = DFCS_BUTTON3STATE; 1593 else flags = DFCS_BUTTONCHECK; 1594 1595 if (state & (BST_CHECKED | BST_INDETERMINATE)) flags |= DFCS_CHECKED; 1596 if (state & BST_PUSHED) flags |= DFCS_PUSHED; 1597 1598 if (style & WS_DISABLED) flags |= DFCS_INACTIVE; 1599 1600 /* rbox must have the correct height */ 1601 delta = rbox.bottom - rbox.top - checkBoxHeight; 1602 1603 if (style & BS_TOP) { 1604 if (delta > 0) { 1605 rbox.bottom = rbox.top + checkBoxHeight; 1606 } else { 1607 rbox.top -= -delta/2 + 1; 1608 rbox.bottom = rbox.top + checkBoxHeight; 1609 } 1610 } else if (style & BS_BOTTOM) { 1611 if (delta > 0) { 1612 rbox.top = rbox.bottom - checkBoxHeight; 1613 } else { 1614 rbox.bottom += -delta/2 + 1; 1615 rbox.top = rbox.bottom - checkBoxHeight; 1616 } 1617 } else { /* Default */ 1618 if (delta > 0) { 1619 int ofs = (delta / 2); 1620 rbox.bottom -= ofs + 1; 1621 rbox.top = rbox.bottom - checkBoxHeight; 1622 } else if (delta < 0) { 1623 int ofs = (-delta / 2); 1624 rbox.top -= ofs + 1; 1625 rbox.bottom = rbox.top + checkBoxHeight; 1626 } 1627 } 1628 1629 DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags ); 1630 } 1631 1632 if (dtFlags == (UINT)-1L) /* Noting to draw */ 1633 return; 1634 1635 if (action == ODA_DRAWENTIRE) 1636 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext); 1637 1638 /* ... and focus */ 1639 if (action == ODA_FOCUS || (state & BST_FOCUS)) 1640 { 1641 #ifdef __REACTOS__ 1642 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) 1643 { 1644 #endif 1645 rtext.left--; 1646 rtext.right++; 1647 IntersectRect(&rtext, &rtext, &client); 1648 DrawFocusRect( hDC, &rtext ); 1649 #ifdef __REACTOS__ 1650 } 1651 #endif 1652 } 1653 SelectClipRgn( hDC, hrgn ); 1654 if (hrgn) DeleteObject( hrgn ); 1655 } 1656 1657 1658 /********************************************************************** 1659 * BUTTON_CheckAutoRadioButton 1660 * 1661 * hwnd is checked, uncheck every other auto radio button in group 1662 */ 1663 static void BUTTON_CheckAutoRadioButton( HWND hwnd ) 1664 { 1665 HWND parent, sibling, start; 1666 1667 parent = GetParent(hwnd); 1668 /* make sure that starting control is not disabled or invisible */ 1669 start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE ); 1670 do 1671 { 1672 if (!sibling) break; 1673 if ((hwnd != sibling) && 1674 ((GetWindowLongPtrW( sibling, GWL_STYLE) & BS_TYPEMASK) == BS_AUTORADIOBUTTON)) 1675 SendMessageW( sibling, BM_SETCHECK, BST_UNCHECKED, 0 ); 1676 sibling = GetNextDlgGroupItem( parent, sibling, FALSE ); 1677 } while (sibling != start); 1678 } 1679 1680 1681 /********************************************************************** 1682 * Group Box Functions 1683 */ 1684 1685 static void GB_Paint( HWND hwnd, HDC hDC, UINT action ) 1686 { 1687 RECT rc, rcFrame; 1688 HBRUSH hbr; 1689 HFONT hFont; 1690 UINT dtFlags; 1691 TEXTMETRICW tm; 1692 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1693 HWND parent; 1694 HRGN hrgn; 1695 1696 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1697 /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */ 1698 parent = GetParent(hwnd); 1699 if (!parent) parent = hwnd; 1700 #if defined(__REACTOS__) && defined(_USER32_) 1701 hbr = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC); 1702 #else 1703 hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd); 1704 if (!hbr) /* did the app forget to call defwindowproc ? */ 1705 hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, 1706 (WPARAM)hDC, (LPARAM)hwnd); 1707 #endif 1708 GetClientRect( hwnd, &rc); 1709 rcFrame = rc; 1710 hrgn = set_control_clipping( hDC, &rc ); 1711 1712 GetTextMetricsW (hDC, &tm); 1713 rcFrame.top += (tm.tmHeight / 2) - 1; 1714 DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0)); 1715 1716 InflateRect(&rc, -7, 1); 1717 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc); 1718 1719 if (dtFlags != (UINT)-1L) 1720 { 1721 /* Because buttons have CS_PARENTDC class style, there is a chance 1722 * that label will be drawn out of client rect. 1723 * But Windows doesn't clip label's rect, so do I. 1724 */ 1725 1726 /* There is 1-pixel margin at the left, right, and bottom */ 1727 rc.left--; rc.right++; rc.bottom++; 1728 FillRect(hDC, &rc, hbr); 1729 rc.left++; rc.right--; rc.bottom--; 1730 1731 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc); 1732 } 1733 SelectClipRgn( hDC, hrgn ); 1734 if (hrgn) DeleteObject( hrgn ); 1735 } 1736 1737 1738 /********************************************************************** 1739 * User Button Functions 1740 */ 1741 1742 static void UB_Paint( HWND hwnd, HDC hDC, UINT action ) 1743 { 1744 RECT rc; 1745 HBRUSH hBrush; 1746 HFONT hFont; 1747 LONG state = get_button_state( hwnd ); 1748 HWND parent; 1749 1750 GetClientRect( hwnd, &rc); 1751 1752 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1753 1754 parent = GetParent(hwnd); 1755 if (!parent) parent = hwnd; 1756 #if defined(__REACTOS__) && defined(_USER32_) 1757 hBrush = GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); 1758 #else 1759 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd); 1760 if (!hBrush) /* did the app forget to call defwindowproc ? */ 1761 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, 1762 (WPARAM)hDC, (LPARAM)hwnd); 1763 #endif 1764 1765 FillRect( hDC, &rc, hBrush ); 1766 if (action == ODA_FOCUS || (state & BST_FOCUS)) 1767 #ifdef __REACTOS__ 1768 { 1769 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) 1770 #endif 1771 DrawFocusRect( hDC, &rc ); 1772 #ifdef __REACTOS__ 1773 } 1774 #endif 1775 1776 switch (action) 1777 { 1778 case ODA_FOCUS: 1779 BUTTON_NOTIFY_PARENT( hwnd, (state & BST_FOCUS) ? BN_SETFOCUS : BN_KILLFOCUS ); 1780 break; 1781 1782 case ODA_SELECT: 1783 BUTTON_NOTIFY_PARENT( hwnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE ); 1784 break; 1785 1786 default: 1787 BUTTON_NOTIFY_PARENT( hwnd, BN_PAINT ); 1788 break; 1789 } 1790 } 1791 1792 1793 /********************************************************************** 1794 * Ownerdrawn Button Functions 1795 */ 1796 1797 static void OB_Paint( HWND hwnd, HDC hDC, UINT action ) 1798 { 1799 LONG state = get_button_state( hwnd ); 1800 DRAWITEMSTRUCT dis; 1801 LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID ); 1802 HWND parent; 1803 HFONT hFont, hPrevFont = 0; 1804 HRGN hrgn; 1805 1806 dis.CtlType = ODT_BUTTON; 1807 dis.CtlID = id; 1808 dis.itemID = 0; 1809 dis.itemAction = action; 1810 dis.itemState = ((state & BST_FOCUS) ? ODS_FOCUS : 0) | 1811 ((state & BST_PUSHED) ? ODS_SELECTED : 0) | 1812 (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED); 1813 dis.hwndItem = hwnd; 1814 dis.hDC = hDC; 1815 dis.itemData = 0; 1816 GetClientRect( hwnd, &dis.rcItem ); 1817 1818 if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont ); 1819 parent = GetParent(hwnd); 1820 if (!parent) parent = hwnd; 1821 #if defined(__REACTOS__) && defined(_USER32_) 1822 GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); 1823 #else 1824 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); 1825 #endif 1826 1827 hrgn = set_control_clipping( hDC, &dis.rcItem ); 1828 1829 SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis ); 1830 if (hPrevFont) SelectObject(hDC, hPrevFont); 1831 SelectClipRgn( hDC, hrgn ); 1832 if (hrgn) DeleteObject( hrgn ); 1833 } 1834 1835 #ifndef _USER32_ 1836 void BUTTON_Register() 1837 { 1838 WNDCLASSW wndClass; 1839 1840 ZeroMemory(&wndClass, sizeof(WNDCLASSW)); 1841 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC; 1842 wndClass.lpfnWndProc = ButtonWndProcW; 1843 wndClass.cbClsExtra = 0; 1844 wndClass.cbWndExtra = sizeof(PBUTTON_DATA); 1845 wndClass.hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); 1846 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); 1847 wndClass.lpszClassName = WC_BUTTONW; 1848 1849 RegisterClassW(&wndClass); 1850 } 1851 1852 void BUTTON_Unregister() 1853 { 1854 UnregisterClassW(WC_BUTTONW, NULL); 1855 } 1856 #endif