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, int index) 399 { 400 SIZE ImageSize; 401 int left, top, count; 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.cx) / 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.cx) / 2; 430 top = prc->bottom - pimlData->margin.bottom - ImageSize.cy; 431 prc->bottom = top - pimlData->margin.top; 432 } 433 else if (pimlData->uAlign == BUTTON_IMAGELIST_ALIGN_CENTER) 434 { 435 left = prc->left + (prc->right - prc->left - ImageSize.cx) / 2; 436 top = prc->top + (prc->bottom - prc->top - ImageSize.cy) / 2; 437 } 438 439 if (bOnlyCalc) 440 return TRUE; 441 442 count = ImageList_GetImageCount(pimlData->himl); 443 444 if (count == 1) 445 index = 0; 446 else if (index >= count) 447 return TRUE; 448 449 ImageList_Draw(pimlData->himl, index, hDC, left, top, 0); 450 451 return TRUE; 452 } 453 454 DWORD BUTTON_SendCustomDraw(HWND hwnd, HDC hDC, DWORD dwDrawStage, RECT* prc) 455 { 456 NMCUSTOMDRAW nmcs; 457 LONG state = get_button_state( hwnd ); 458 459 nmcs.hdr.hwndFrom = hwnd; 460 nmcs.hdr.idFrom = GetWindowLongPtrW (hwnd, GWLP_ID); 461 nmcs.hdr.code = NM_CUSTOMDRAW ; 462 nmcs.dwDrawStage = dwDrawStage; 463 nmcs.hdc = hDC; 464 nmcs.rc = *prc; 465 nmcs.dwItemSpec = 0; 466 nmcs.uItemState = 0; 467 nmcs.lItemlParam = 0; 468 if(!IsWindowEnabled(hwnd)) 469 nmcs.uItemState |= CDIS_DISABLED; 470 if (state & (BST_CHECKED | BST_INDETERMINATE)) 471 nmcs.uItemState |= CDIS_CHECKED; 472 if (state & BST_FOCUS) 473 nmcs.uItemState |= CDIS_FOCUS; 474 if (state & BST_PUSHED) 475 nmcs.uItemState |= CDIS_SELECTED; 476 if (!(get_ui_state(hwnd) & UISF_HIDEACCEL)) 477 nmcs.uItemState |= CDIS_SHOWKEYBOARDCUES; 478 479 return SendMessageW(GetParent(hwnd), WM_NOTIFY, nmcs.hdr.idFrom, (LPARAM)&nmcs); 480 } 481 482 #endif 483 484 485 /* retrieve the button text; returned buffer must be freed by caller */ 486 inline WCHAR *get_button_text( HWND hwnd ) 487 { 488 INT len = 512; 489 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ); 490 if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 ); 491 return buffer; 492 } 493 494 #ifdef __REACTOS__ 495 /* Retrieve the UI state for the control */ 496 static BOOL button_update_uistate(HWND hwnd, BOOL unicode) 497 { 498 LONG flags, prevflags; 499 500 if (unicode) 501 flags = DefWindowProcW(hwnd, WM_QUERYUISTATE, 0, 0); 502 else 503 flags = DefWindowProcA(hwnd, WM_QUERYUISTATE, 0, 0); 504 505 prevflags = get_ui_state(hwnd); 506 507 if (prevflags != flags) 508 { 509 set_ui_state(hwnd, flags); 510 return TRUE; 511 } 512 513 return FALSE; 514 } 515 #endif 516 517 /*********************************************************************** 518 * ButtonWndProc_common 519 */ 520 LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, 521 WPARAM wParam, LPARAM lParam, BOOL unicode ) 522 { 523 RECT rect; 524 POINT pt; 525 LONG style = GetWindowLongPtrW( hWnd, GWL_STYLE ); 526 UINT btn_type = get_button_type( style ); 527 LONG state; 528 HANDLE oldHbitmap; 529 #if defined(__REACTOS__) && defined(_USER32_) 530 PWND pWnd; 531 532 pWnd = ValidateHwnd(hWnd); 533 if (pWnd) 534 { 535 if (!pWnd->fnid) 536 { 537 NtUserSetWindowFNID(hWnd, FNID_BUTTON); 538 } 539 else 540 { 541 if (pWnd->fnid != FNID_BUTTON) 542 { 543 ERR("Wrong window class for Button! fnId 0x%x\n",pWnd->fnid); 544 return 0; 545 } 546 } 547 } 548 else 549 return 0; 550 #else 551 if (!IsWindow( hWnd )) return 0; 552 #endif 553 554 pt.x = (short)LOWORD(lParam); 555 pt.y = (short)HIWORD(lParam); 556 557 #ifndef _USER32_ 558 switch (uMsg) 559 { 560 case WM_NCCREATE: 561 { 562 PBUTTON_DATA data = HeapAlloc( GetProcessHeap(), 0, sizeof(BUTTON_DATA) ); 563 if (!data) 564 { 565 ERR("Failed to alloc internal button data\n"); 566 return -1; 567 } 568 569 memset(data, 0, sizeof(BUTTON_DATA)); 570 SetRect(&data->rcTextMargin, 1,1,1,1); 571 572 _SetButtonData(hWnd, data); 573 break; 574 } 575 case WM_NCDESTROY: 576 { 577 PBUTTON_DATA data = _GetButtonData(hWnd); 578 if (!data) 579 { 580 ERR("No data"); 581 return 0; 582 } 583 HeapFree( GetProcessHeap(), 0, data ); 584 _SetButtonData(hWnd, NULL); 585 } 586 case WM_CREATE: 587 OpenThemeData(hWnd, WC_BUTTONW); 588 break; 589 case WM_DESTROY: 590 CloseThemeData (GetWindowTheme(hWnd)); 591 break; 592 case WM_THEMECHANGED: 593 CloseThemeData (GetWindowTheme(hWnd)); 594 OpenThemeData(hWnd, WC_BUTTONW); 595 InvalidateRect(hWnd, NULL, TRUE); 596 break; 597 case WM_MOUSELEAVE: 598 { 599 state = get_button_state( hWnd ); 600 if (state & BST_HOT) 601 { 602 NMBCHOTITEM nmhotitem; 603 604 state &= ~BST_HOT; 605 set_button_state(hWnd, state); 606 607 nmhotitem.hdr.hwndFrom = hWnd; 608 nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); 609 nmhotitem.hdr.code = BCN_HOTITEMCHANGE; 610 nmhotitem.dwFlags = HICF_LEAVING; 611 SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); 612 613 InvalidateRect(hWnd, NULL, TRUE); 614 } 615 break; 616 } 617 case WM_MOUSEMOVE: 618 { 619 TRACKMOUSEEVENT mouse_event; 620 state = get_button_state( hWnd ); 621 if ((state & BST_HOT) == 0) 622 { 623 NMBCHOTITEM nmhotitem; 624 625 state |= BST_HOT; 626 set_button_state(hWnd, state); 627 628 nmhotitem.hdr.hwndFrom = hWnd; 629 nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); 630 nmhotitem.hdr.code = BCN_HOTITEMCHANGE; 631 nmhotitem.dwFlags = HICF_ENTERING; 632 SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); 633 634 InvalidateRect(hWnd, NULL, TRUE); 635 } 636 637 mouse_event.cbSize = sizeof(TRACKMOUSEEVENT); 638 mouse_event.dwFlags = TME_QUERY; 639 if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&TME_LEAVE)) 640 { 641 mouse_event.dwFlags = TME_LEAVE; 642 mouse_event.hwndTrack = hWnd; 643 mouse_event.dwHoverTime = 1; 644 TrackMouseEvent(&mouse_event); 645 } 646 break; 647 } 648 case BCM_GETTEXTMARGIN: 649 { 650 RECT* prc = (RECT*)lParam; 651 PBUTTON_DATA data = _GetButtonData(hWnd); 652 if (!prc || !data) 653 return FALSE; 654 *prc = data->rcTextMargin; 655 return TRUE; 656 } 657 case BCM_SETTEXTMARGIN: 658 { 659 RECT* prc = (RECT*)lParam; 660 PBUTTON_DATA data = _GetButtonData(hWnd); 661 if (!prc || !data) 662 return FALSE; 663 data->rcTextMargin = *prc; 664 return TRUE; 665 } 666 case BCM_SETIMAGELIST: 667 { 668 BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; 669 PBUTTON_DATA data = _GetButtonData(hWnd); 670 if (!data || !pimldata || !pimldata->himl) 671 return FALSE; 672 data->imlData = *pimldata; 673 return TRUE; 674 } 675 case BCM_GETIMAGELIST: 676 { 677 BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; 678 PBUTTON_DATA data = _GetButtonData(hWnd); 679 if (!data|| !pimldata) 680 return FALSE; 681 *pimldata = data->imlData; 682 return TRUE; 683 } 684 case BCM_GETIDEALSIZE: 685 { 686 HTHEME theme = GetWindowTheme(hWnd); 687 BOOL ret = FALSE; 688 SIZE* pSize = (SIZE*)lParam; 689 690 if (btn_type == BS_PUSHBUTTON || 691 btn_type == BS_DEFPUSHBUTTON || 692 btn_type == BS_USERBUTTON) 693 { 694 ret = BUTTON_GetIdealSize(theme, hWnd, pSize); 695 } 696 697 if (!ret) 698 { 699 GetClientRect(hWnd, &rect); 700 pSize->cx = rect.right; 701 pSize->cy = rect.bottom; 702 } 703 704 return TRUE; 705 } 706 } 707 708 if (!_GetButtonData(hWnd)) 709 { 710 ERR("no data!\n"); 711 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) : 712 DefWindowProcA(hWnd, uMsg, wParam, lParam); 713 } 714 715 #endif 716 717 switch (uMsg) 718 { 719 case WM_GETDLGCODE: 720 switch(btn_type) 721 { 722 case BS_USERBUTTON: 723 case BS_PUSHBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON; 724 case BS_DEFPUSHBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON; 725 case BS_RADIOBUTTON: 726 case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON; 727 case BS_GROUPBOX: return DLGC_STATIC; 728 default: return DLGC_BUTTON; 729 } 730 731 case WM_ENABLE: 732 paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); 733 break; 734 735 case WM_CREATE: 736 if (btn_type >= MAX_BTN_TYPE) 737 return -1; /* abort */ 738 739 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */ 740 if (btn_type == BS_USERBUTTON ) 741 { 742 style = (style & ~BS_TYPEMASK) | BS_PUSHBUTTON; 743 #ifdef __REACTOS__ 744 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style ); 745 #else 746 WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style ); 747 #endif 748 } 749 set_button_state( hWnd, BST_UNCHECKED ); 750 #ifdef __REACTOS__ 751 button_update_uistate( hWnd, unicode ); 752 #endif 753 return 0; 754 755 #if defined(__REACTOS__) && defined(_USER32_) 756 case WM_NCDESTROY: 757 NtUserSetWindowFNID(hWnd, FNID_DESTROY); 758 case WM_DESTROY: 759 break; 760 #endif 761 case WM_ERASEBKGND: 762 if (btn_type == BS_OWNERDRAW) 763 { 764 HDC hdc = (HDC)wParam; 765 RECT rc; 766 HBRUSH hBrush; 767 HWND parent = GetParent(hWnd); 768 if (!parent) parent = hWnd; 769 #if defined(__REACTOS__) && defined(_USER32_) 770 hBrush = GetControlColor( parent, hWnd, hdc, WM_CTLCOLORBTN); 771 #else 772 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd); 773 if (!hBrush) /* did the app forget to call defwindowproc ? */ 774 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, 775 (WPARAM)hdc, (LPARAM)hWnd); 776 #endif 777 GetClientRect(hWnd, &rc); 778 FillRect(hdc, &rc, hBrush); 779 } 780 return 1; 781 782 case WM_PRINTCLIENT: 783 case WM_PAINT: 784 { 785 PAINTSTRUCT ps; 786 HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps ); 787 #ifndef _USER32_ 788 HTHEME theme = GetWindowTheme(hWnd); 789 if (theme && BUTTON_PaintWithTheme(theme, hWnd, hdc, uMsg == WM_PRINTCLIENT ? lParam : 0)) 790 { 791 if ( !wParam ) EndPaint( hWnd, &ps ); 792 return 0; 793 } 794 #endif 795 if (btnPaintFunc[btn_type]) 796 { 797 int nOldMode = SetBkMode( hdc, OPAQUE ); 798 (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE ); 799 SetBkMode(hdc, nOldMode); /* reset painting mode */ 800 } 801 if ( !wParam ) EndPaint( hWnd, &ps ); 802 break; 803 } 804 805 case WM_KEYDOWN: 806 if (wParam == VK_SPACE) 807 { 808 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); 809 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED ); 810 SetCapture( hWnd ); 811 } 812 break; 813 814 case WM_LBUTTONDBLCLK: 815 if(style & BS_NOTIFY || 816 btn_type == BS_RADIOBUTTON || 817 btn_type == BS_USERBUTTON || 818 btn_type == BS_OWNERDRAW) 819 { 820 BUTTON_NOTIFY_PARENT(hWnd, BN_DOUBLECLICKED); 821 break; 822 } 823 /* fall through */ 824 case WM_LBUTTONDOWN: 825 SetCapture( hWnd ); 826 SetFocus( hWnd ); 827 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED ); 828 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); 829 break; 830 831 case WM_KEYUP: 832 if (wParam != VK_SPACE) 833 break; 834 /* fall through */ 835 case WM_LBUTTONUP: 836 #ifdef _REACTOS_ 837 BOOL TellParent = FALSE; //// ReactOS see note below. 838 #endif 839 state = get_button_state( hWnd ); 840 if (!(state & BUTTON_BTNPRESSED)) break; 841 state &= BUTTON_NSTATES; 842 set_button_state( hWnd, state ); 843 if (!(state & BST_PUSHED)) 844 { 845 ReleaseCapture(); 846 break; 847 } 848 SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 ); 849 GetClientRect( hWnd, &rect ); 850 if (uMsg == WM_KEYUP || PtInRect( &rect, pt )) 851 { 852 state = get_button_state( hWnd ); 853 switch(btn_type) 854 { 855 case BS_AUTOCHECKBOX: 856 SendMessageW( hWnd, BM_SETCHECK, !(state & BST_CHECKED), 0 ); 857 break; 858 case BS_AUTORADIOBUTTON: 859 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 ); 860 break; 861 case BS_AUTO3STATE: 862 SendMessageW( hWnd, BM_SETCHECK, 863 (state & BST_INDETERMINATE) ? 0 : ((state & 3) + 1), 0 ); 864 break; 865 } 866 #ifdef _REACTOS_ 867 TellParent = TRUE; // <---- Fix CORE-10194, Notify parent after capture is released. 868 #else 869 ReleaseCapture(); 870 BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED); 871 #endif 872 } 873 #ifdef _REACTOS_ 874 ReleaseCapture(); 875 if (TellParent) BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED); 876 #else 877 else 878 { 879 ReleaseCapture(); 880 } 881 #endif 882 break; 883 884 case WM_CAPTURECHANGED: 885 TRACE("WM_CAPTURECHANGED %p\n", hWnd); 886 if (hWnd == (HWND)lParam) break; 887 state = get_button_state( hWnd ); 888 if (state & BUTTON_BTNPRESSED) 889 { 890 state &= BUTTON_NSTATES; 891 set_button_state( hWnd, state ); 892 if (state & BST_PUSHED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 ); 893 } 894 break; 895 896 case WM_MOUSEMOVE: 897 if ((wParam & MK_LBUTTON) && GetCapture() == hWnd) 898 { 899 GetClientRect( hWnd, &rect ); 900 SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 ); 901 } 902 break; 903 904 case WM_SETTEXT: 905 { 906 /* Clear an old text here as Windows does */ 907 // 908 // ReactOS Note : 909 // wine Bug: http://bugs.winehq.org/show_bug.cgi?id=25790 910 // Patch: http://source.winehq.org/patches/data/70889 911 // By: Alexander LAW, Replicate Windows behavior of WM_SETTEXT handler regarding WM_CTLCOLOR* 912 // 913 #ifdef __REACTOS__ 914 if (style & WS_VISIBLE) 915 #else 916 if (IsWindowVisible(hWnd)) 917 #endif 918 { 919 HDC hdc = GetDC(hWnd); 920 HBRUSH hbrush; 921 RECT client, rc; 922 HWND parent = GetParent(hWnd); 923 UINT message = (btn_type == BS_PUSHBUTTON || 924 btn_type == BS_DEFPUSHBUTTON || 925 btn_type == BS_PUSHLIKE || 926 btn_type == BS_USERBUTTON || 927 btn_type == BS_OWNERDRAW) ? 928 WM_CTLCOLORBTN : WM_CTLCOLORSTATIC; 929 930 if (!parent) parent = hWnd; 931 #if defined(__REACTOS__) && defined(_USER32_) 932 hbrush = GetControlColor(parent, hWnd, hdc, message); 933 #else 934 hbrush = (HBRUSH)SendMessageW(parent, message, 935 (WPARAM)hdc, (LPARAM)hWnd); 936 if (!hbrush) /* did the app forget to call DefWindowProc ? */ 937 hbrush = (HBRUSH)DefWindowProcW(parent, message, 938 (WPARAM)hdc, (LPARAM)hWnd); 939 #endif 940 941 GetClientRect(hWnd, &client); 942 rc = client; 943 /* FIXME: check other BS_* handlers */ 944 if (btn_type == BS_GROUPBOX) 945 InflateRect(&rc, -7, 1); /* GB_Paint does this */ 946 BUTTON_CalcLabelRect(hWnd, hdc, &rc); 947 /* Clip by client rect bounds */ 948 if (rc.right > client.right) rc.right = client.right; 949 if (rc.bottom > client.bottom) rc.bottom = client.bottom; 950 FillRect(hdc, &rc, hbrush); 951 ReleaseDC(hWnd, hdc); 952 } 953 954 if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam ); 955 else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam ); 956 if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */ 957 InvalidateRect( hWnd, NULL, TRUE ); 958 else 959 paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); 960 return 1; /* success. FIXME: check text length */ 961 } 962 963 case WM_SETFONT: 964 set_button_font( hWnd, (HFONT)wParam ); 965 if (lParam) InvalidateRect(hWnd, NULL, TRUE); 966 break; 967 968 case WM_GETFONT: 969 return (LRESULT)get_button_font( hWnd ); 970 971 case WM_SETFOCUS: 972 TRACE("WM_SETFOCUS %p\n",hWnd); 973 set_button_state( hWnd, get_button_state(hWnd) | BST_FOCUS ); 974 #ifndef _USER32_ 975 if (btn_type != BS_OWNERDRAW) 976 InvalidateRect(hWnd, NULL, FALSE); 977 else 978 #endif 979 paint_button( hWnd, btn_type, ODA_FOCUS ); 980 if (style & BS_NOTIFY) 981 BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS); 982 break; 983 984 case WM_KILLFOCUS: 985 TRACE("WM_KILLFOCUS %p\n",hWnd); 986 state = get_button_state( hWnd ); 987 set_button_state( hWnd, state & ~BST_FOCUS ); 988 #ifdef _USER32_ 989 paint_button( hWnd, btn_type, ODA_FOCUS ); 990 #endif 991 992 if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) 993 ReleaseCapture(); 994 if (style & BS_NOTIFY) 995 BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS); 996 997 InvalidateRect( hWnd, NULL, FALSE ); 998 break; 999 1000 case WM_SYSCOLORCHANGE: 1001 InvalidateRect( hWnd, NULL, FALSE ); 1002 break; 1003 1004 case BM_SETSTYLE: 1005 btn_type = wParam & BS_TYPEMASK; 1006 style = (style & ~BS_TYPEMASK) | btn_type; 1007 #ifdef __REACTOS__ 1008 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style); 1009 #else 1010 WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style ); 1011 #endif 1012 1013 /* Only redraw if lParam flag is set.*/ 1014 if (lParam) 1015 InvalidateRect( hWnd, NULL, TRUE ); 1016 1017 break; 1018 1019 case BM_CLICK: 1020 #ifdef __REACTOS__ 1021 state = get_button_state(hWnd); 1022 if (state & BUTTON_BMCLICK) 1023 break; 1024 set_button_state(hWnd, state | BUTTON_BMCLICK); // Tracked in STATE_GWL_OFFSET. 1025 #endif 1026 SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 ); 1027 SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 ); 1028 #ifdef __REACTOS__ 1029 state = get_button_state(hWnd); 1030 if (!(state & BUTTON_BMCLICK)) break; 1031 state &= ~BUTTON_BMCLICK; 1032 set_button_state(hWnd, state); 1033 #endif 1034 break; 1035 1036 case BM_SETIMAGE: 1037 /* Check that image format matches button style */ 1038 switch (style & (BS_BITMAP|BS_ICON)) 1039 { 1040 case BS_BITMAP: 1041 if (wParam != IMAGE_BITMAP) return 0; 1042 break; 1043 case BS_ICON: 1044 if (wParam != IMAGE_ICON) return 0; 1045 break; 1046 default: 1047 return 0; 1048 } 1049 #ifdef _USER32_ 1050 oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam ); 1051 #else 1052 oldHbitmap = (HBITMAP)set_button_image(hWnd, lParam ); 1053 #endif 1054 InvalidateRect( hWnd, NULL, FALSE ); 1055 return (LRESULT)oldHbitmap; 1056 1057 case BM_GETIMAGE: 1058 #ifdef _USER32_ 1059 return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET ); 1060 #else 1061 return get_button_image(hWnd); 1062 #endif 1063 1064 case BM_GETCHECK: 1065 return get_button_state( hWnd ) & 3; 1066 1067 case BM_SETCHECK: 1068 if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type]; 1069 state = get_button_state( hWnd ); 1070 if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON)) 1071 { 1072 #ifdef __REACTOS__ 1073 if (wParam) style |= WS_TABSTOP; 1074 else style &= ~WS_TABSTOP; 1075 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style); 1076 #else 1077 if (wParam) WIN_SetStyle( hWnd, WS_TABSTOP, 0 ); 1078 else WIN_SetStyle( hWnd, 0, WS_TABSTOP ); 1079 #endif 1080 } 1081 if ((state & 3) != wParam) 1082 { 1083 set_button_state( hWnd, (state & ~3) | wParam ); 1084 #ifdef _USER32 1085 paint_button( hWnd, btn_type, ODA_SELECT ); 1086 #else 1087 InvalidateRect(hWnd, NULL, FALSE); 1088 #endif 1089 } 1090 if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD)) 1091 BUTTON_CheckAutoRadioButton( hWnd ); 1092 break; 1093 1094 case BM_GETSTATE: 1095 return get_button_state( hWnd ); 1096 1097 case BM_SETSTATE: 1098 state = get_button_state( hWnd ); 1099 if (wParam) 1100 set_button_state( hWnd, state | BST_PUSHED ); 1101 else 1102 set_button_state( hWnd, state & ~BST_PUSHED ); 1103 1104 #ifdef _USER32_ 1105 paint_button( hWnd, btn_type, ODA_SELECT ); 1106 #else 1107 InvalidateRect(hWnd, NULL, FALSE); 1108 #endif 1109 break; 1110 1111 #ifdef __REACTOS__ 1112 case WM_UPDATEUISTATE: 1113 if (unicode) 1114 DefWindowProcW(hWnd, uMsg, wParam, lParam); 1115 else 1116 DefWindowProcA(hWnd, uMsg, wParam, lParam); 1117 1118 if (button_update_uistate(hWnd, unicode)) 1119 paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); 1120 break; 1121 #endif 1122 1123 case WM_NCHITTEST: 1124 if(btn_type == BS_GROUPBOX) return HTTRANSPARENT; 1125 /* fall through */ 1126 default: 1127 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) : 1128 DefWindowProcA(hWnd, uMsg, wParam, lParam); 1129 } 1130 return 0; 1131 } 1132 1133 #ifdef __REACTOS__ 1134 1135 /*********************************************************************** 1136 * ButtonWndProcW 1137 * The button window procedure. This is just a wrapper which locks 1138 * the passed HWND and calls the real window procedure (with a WND* 1139 * pointer pointing to the locked windowstructure). 1140 */ 1141 LRESULT WINAPI ButtonWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1142 { 1143 if (!IsWindow(hWnd)) return 0; 1144 return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, TRUE); 1145 } 1146 1147 /*********************************************************************** 1148 * ButtonWndProcA 1149 */ 1150 LRESULT WINAPI ButtonWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1151 { 1152 if (!IsWindow(hWnd)) return 0; 1153 return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, FALSE); 1154 } 1155 1156 #endif /* __REACTOS__ */ 1157 1158 /********************************************************************** 1159 * Convert button styles to flags used by DrawText. 1160 */ 1161 static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style ) 1162 { 1163 UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */ 1164 1165 /* "Convert" pushlike buttons to pushbuttons */ 1166 if (style & BS_PUSHLIKE) 1167 style &= ~BS_TYPEMASK; 1168 1169 if (!(style & BS_MULTILINE)) 1170 dtStyle |= DT_SINGLELINE; 1171 else 1172 dtStyle |= DT_WORDBREAK; 1173 1174 switch (style & BS_CENTER) 1175 { 1176 case BS_LEFT: /* DT_LEFT is 0 */ break; 1177 case BS_RIGHT: dtStyle |= DT_RIGHT; break; 1178 case BS_CENTER: dtStyle |= DT_CENTER; break; 1179 default: 1180 /* Pushbutton's text is centered by default */ 1181 if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER; 1182 /* all other flavours have left aligned text */ 1183 } 1184 1185 if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER)); 1186 1187 /* DrawText ignores vertical alignment for multiline text, 1188 * but we use these flags to align label manually. 1189 */ 1190 if (get_button_type(style) != BS_GROUPBOX) 1191 { 1192 switch (style & BS_VCENTER) 1193 { 1194 case BS_TOP: /* DT_TOP is 0 */ break; 1195 case BS_BOTTOM: dtStyle |= DT_BOTTOM; break; 1196 case BS_VCENTER: /* fall through */ 1197 default: dtStyle |= DT_VCENTER; break; 1198 } 1199 } 1200 else 1201 /* GroupBox's text is always single line and is top aligned. */ 1202 dtStyle |= DT_SINGLELINE; 1203 1204 return dtStyle; 1205 } 1206 1207 /********************************************************************** 1208 * BUTTON_CalcLabelRect 1209 * 1210 * Calculates label's rectangle depending on button style. 1211 * 1212 * Returns flags to be passed to DrawText. 1213 * Calculated rectangle doesn't take into account button state 1214 * (pushed, etc.). If there is nothing to draw (no text/image) output 1215 * rectangle is empty, and return value is (UINT)-1. 1216 */ 1217 static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc) 1218 { 1219 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1220 LONG ex_style = GetWindowLongPtrW( hwnd, GWL_EXSTYLE ); 1221 WCHAR *text; 1222 ICONINFO iconInfo; 1223 BITMAP bm; 1224 UINT dtStyle = BUTTON_BStoDT( style, ex_style ); 1225 RECT r = *rc; 1226 INT n; 1227 #ifdef __REACTOS__ 1228 PBUTTON_DATA pdata = _GetButtonData(hwnd); 1229 #endif 1230 1231 #ifndef _USER32_ 1232 BOOL bHasIml = BUTTON_DrawIml(hdc, &pdata->imlData, &r, TRUE, 0); 1233 #endif 1234 1235 /* Calculate label rectangle according to label type */ 1236 switch (style & (BS_ICON|BS_BITMAP)) 1237 { 1238 case BS_TEXT: 1239 { 1240 HFONT hFont, hPrevFont = 0; 1241 1242 if (!(text = get_button_text( hwnd ))) goto empty_rect; 1243 if (!text[0]) 1244 { 1245 HeapFree( GetProcessHeap(), 0, text ); 1246 goto empty_rect; 1247 } 1248 1249 if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hdc, hFont ); 1250 DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT); 1251 if (hPrevFont) SelectObject( hdc, hPrevFont ); 1252 HeapFree( GetProcessHeap(), 0, text ); 1253 #ifdef __REACTOS__ 1254 if (get_ui_state(hwnd) & UISF_HIDEACCEL) 1255 dtStyle |= DT_HIDEPREFIX; 1256 #endif 1257 break; 1258 } 1259 1260 case BS_ICON: 1261 #ifdef _USER32_ 1262 if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo)) 1263 #else 1264 if (!GetIconInfo((HICON)get_button_image(hwnd), &iconInfo)) 1265 #endif 1266 goto empty_rect; 1267 1268 GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm); 1269 1270 r.right = r.left + bm.bmWidth; 1271 r.bottom = r.top + bm.bmHeight; 1272 1273 DeleteObject(iconInfo.hbmColor); 1274 DeleteObject(iconInfo.hbmMask); 1275 break; 1276 1277 case BS_BITMAP: 1278 #ifdef _USER32_ 1279 if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm)) 1280 #else 1281 if (!GetObjectW( (HANDLE)get_button_image(hwnd), sizeof(BITMAP), &bm)) 1282 #endif 1283 goto empty_rect; 1284 1285 r.right = r.left + bm.bmWidth; 1286 r.bottom = r.top + bm.bmHeight; 1287 break; 1288 1289 default: 1290 empty_rect: 1291 #ifndef _USER32_ 1292 if (bHasIml) 1293 break; 1294 #endif 1295 rc->right = r.left; 1296 rc->bottom = r.top; 1297 return (UINT)-1; 1298 } 1299 1300 #ifndef _USER32_ 1301 if (bHasIml) 1302 { 1303 if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT) 1304 r.left = pdata->imlData.margin.left; 1305 else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) 1306 r.right = pdata->imlData.margin.right; 1307 else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_TOP) 1308 r.top = pdata->imlData.margin.top; 1309 else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) 1310 r.bottom = pdata->imlData.margin.bottom; 1311 } 1312 #endif 1313 1314 /* Position label inside bounding rectangle according to 1315 * alignment flags. (calculated rect is always left-top aligned). 1316 * If label is aligned to any side - shift label in opposite 1317 * direction to leave extra space for focus rectangle. 1318 */ 1319 switch (dtStyle & (DT_CENTER|DT_RIGHT)) 1320 { 1321 case DT_LEFT: r.left++; r.right++; break; 1322 case DT_CENTER: n = r.right - r.left; 1323 r.left = rc->left + ((rc->right - rc->left) - n) / 2; 1324 r.right = r.left + n; break; 1325 case DT_RIGHT: n = r.right - r.left; 1326 r.right = rc->right - 1; 1327 r.left = r.right - n; 1328 break; 1329 } 1330 1331 switch (dtStyle & (DT_VCENTER|DT_BOTTOM)) 1332 { 1333 case DT_TOP: r.top++; r.bottom++; break; 1334 case DT_VCENTER: n = r.bottom - r.top; 1335 r.top = rc->top + ((rc->bottom - rc->top) - n) / 2; 1336 r.bottom = r.top + n; break; 1337 case DT_BOTTOM: n = r.bottom - r.top; 1338 r.bottom = rc->bottom - 1; 1339 r.top = r.bottom - n; 1340 break; 1341 } 1342 1343 *rc = r; 1344 return dtStyle; 1345 } 1346 1347 1348 /********************************************************************** 1349 * BUTTON_DrawTextCallback 1350 * 1351 * Callback function used by DrawStateW function. 1352 */ 1353 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy) 1354 { 1355 RECT rc; 1356 1357 SetRect(&rc, 0, 0, cx, cy); 1358 DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp); 1359 return TRUE; 1360 } 1361 1362 1363 /********************************************************************** 1364 * BUTTON_DrawLabel 1365 * 1366 * Common function for drawing button label. 1367 */ 1368 #if defined(_USER32_) 1369 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc) 1370 #else 1371 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc) 1372 #endif 1373 { 1374 DRAWSTATEPROC lpOutputProc = NULL; 1375 LPARAM lp; 1376 WPARAM wp = 0; 1377 HBRUSH hbr = 0; 1378 UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED; 1379 LONG state = get_button_state( hwnd ); 1380 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1381 WCHAR *text = NULL; 1382 1383 /* FIXME: To draw disabled label in Win31 look-and-feel, we probably 1384 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION). 1385 * I don't have Win31 on hand to verify that, so I leave it as is. 1386 */ 1387 1388 #ifndef _USER32_ 1389 PBUTTON_DATA pdata = _GetButtonData(hwnd); 1390 BUTTON_DrawIml(hdc, &pdata->imlData, rc, FALSE, 0); 1391 #endif 1392 1393 if ((style & BS_PUSHLIKE) && (state & BST_INDETERMINATE)) 1394 { 1395 hbr = GetSysColorBrush(COLOR_GRAYTEXT); 1396 flags |= DSS_MONO; 1397 } 1398 1399 switch (style & (BS_ICON|BS_BITMAP)) 1400 { 1401 case BS_TEXT: 1402 /* DST_COMPLEX -- is 0 */ 1403 lpOutputProc = BUTTON_DrawTextCallback; 1404 if (!(text = get_button_text( hwnd ))) return; 1405 lp = (LPARAM)text; 1406 wp = (WPARAM)dtFlags; 1407 1408 #ifdef __REACTOS__ 1409 if (dtFlags & DT_HIDEPREFIX) 1410 flags |= DSS_HIDEPREFIX; 1411 #endif 1412 break; 1413 1414 case BS_ICON: 1415 flags |= DST_ICON; 1416 #ifdef _USER32_ 1417 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ); 1418 #else 1419 lp = get_button_image(hwnd); 1420 #endif 1421 break; 1422 1423 case BS_BITMAP: 1424 flags |= DST_BITMAP; 1425 #ifdef _USER32_ 1426 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ); 1427 #else 1428 lp = get_button_image(hwnd); 1429 #endif 1430 break; 1431 1432 default: 1433 return; 1434 } 1435 1436 DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top, 1437 rc->right - rc->left, rc->bottom - rc->top, flags); 1438 HeapFree( GetProcessHeap(), 0, text ); 1439 } 1440 1441 /********************************************************************** 1442 * Push Button Functions 1443 */ 1444 static void PB_Paint( HWND hwnd, HDC hDC, UINT action ) 1445 { 1446 RECT rc, r; 1447 UINT dtFlags, uState; 1448 HPEN hOldPen; 1449 HBRUSH hOldBrush; 1450 INT oldBkMode; 1451 COLORREF oldTxtColor; 1452 HFONT hFont; 1453 LONG state = get_button_state( hwnd ); 1454 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1455 BOOL pushedState = (state & BST_PUSHED); 1456 HWND parent; 1457 HRGN hrgn; 1458 #ifndef _USER32_ 1459 DWORD cdrf; 1460 #endif 1461 1462 GetClientRect( hwnd, &rc ); 1463 1464 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */ 1465 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1466 parent = GetParent(hwnd); 1467 if (!parent) parent = hwnd; 1468 #if defined(__REACTOS__) && defined(_USER32_) 1469 GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); 1470 #else 1471 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); 1472 #endif 1473 1474 hrgn = set_control_clipping( hDC, &rc ); 1475 #ifdef __REACTOS__ 1476 hOldPen = SelectObject(hDC, GetStockObject(DC_PEN)); 1477 SetDCPenColor(hDC, GetSysColor(COLOR_WINDOWFRAME)); 1478 #else 1479 hOldPen = SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME)); 1480 #endif 1481 hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE)); 1482 oldBkMode = SetBkMode(hDC, TRANSPARENT); 1483 1484 /* completely skip the drawing if only focus has changed */ 1485 if (action == ODA_FOCUS) goto draw_focus; 1486 1487 #ifndef _USER32_ 1488 cdrf = BUTTON_SendCustomDraw(hwnd, hDC, CDDS_PREERASE, &rc); 1489 if (cdrf == CDRF_SKIPDEFAULT) 1490 goto cleanup; 1491 #endif 1492 1493 if (get_button_type(style) == BS_DEFPUSHBUTTON) 1494 { 1495 if (action != ODA_FOCUS) 1496 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); 1497 InflateRect( &rc, -1, -1 ); 1498 } 1499 1500 uState = DFCS_BUTTONPUSH; 1501 1502 if (style & BS_FLAT) 1503 uState |= DFCS_MONO; 1504 else if (pushedState) 1505 { 1506 if (get_button_type(style) == BS_DEFPUSHBUTTON ) 1507 uState |= DFCS_FLAT; 1508 else 1509 uState |= DFCS_PUSHED; 1510 } 1511 1512 if (state & (BST_CHECKED | BST_INDETERMINATE)) 1513 uState |= DFCS_CHECKED; 1514 1515 DrawFrameControl( hDC, &rc, DFC_BUTTON, uState ); 1516 1517 #ifndef _USER32_ 1518 if (cdrf == CDRF_NOTIFYPOSTERASE) 1519 BUTTON_SendCustomDraw(hwnd, hDC, CDDS_POSTERASE, &rc); 1520 1521 cdrf = BUTTON_SendCustomDraw(hwnd, hDC, CDDS_PREPAINT, &rc); 1522 if (cdrf == CDRF_SKIPDEFAULT) 1523 goto cleanup; 1524 #endif 1525 1526 /* draw button label */ 1527 r = rc; 1528 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r); 1529 1530 if (dtFlags == (UINT)-1L) 1531 goto cleanup; 1532 1533 if (pushedState) 1534 OffsetRect(&r, 1, 1); 1535 1536 oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) ); 1537 1538 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r); 1539 1540 SetTextColor( hDC, oldTxtColor ); 1541 1542 #ifndef _USER32_ 1543 if (cdrf == CDRF_NOTIFYPOSTPAINT) 1544 BUTTON_SendCustomDraw(hwnd, hDC, CDDS_POSTPAINT, &rc); 1545 #endif 1546 1547 draw_focus: 1548 if (action == ODA_FOCUS || (state & BST_FOCUS)) 1549 { 1550 #ifdef __REACTOS__ 1551 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) 1552 { 1553 #endif 1554 InflateRect( &rc, -2, -2 ); 1555 DrawFocusRect( hDC, &rc ); 1556 #ifdef __REACTOS__ 1557 } 1558 #endif 1559 } 1560 1561 cleanup: 1562 SelectObject( hDC, hOldPen ); 1563 SelectObject( hDC, hOldBrush ); 1564 SetBkMode(hDC, oldBkMode); 1565 SelectClipRgn( hDC, hrgn ); 1566 if (hrgn) DeleteObject( hrgn ); 1567 } 1568 1569 /********************************************************************** 1570 * Check Box & Radio Button Functions 1571 */ 1572 1573 static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) 1574 { 1575 RECT rbox, rtext, client; 1576 HBRUSH hBrush; 1577 int delta, text_offset, checkBoxWidth, checkBoxHeight; 1578 UINT dtFlags; 1579 HFONT hFont; 1580 LONG state = get_button_state( hwnd ); 1581 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1582 LONG ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE ); 1583 HWND parent; 1584 HRGN hrgn; 1585 1586 if (style & BS_PUSHLIKE) 1587 { 1588 PB_Paint( hwnd, hDC, action ); 1589 return; 1590 } 1591 1592 GetClientRect(hwnd, &client); 1593 rbox = rtext = client; 1594 1595 checkBoxWidth = 12 * GetDeviceCaps( hDC, LOGPIXELSX ) / 96 + 1; 1596 checkBoxHeight = 12 * GetDeviceCaps( hDC, LOGPIXELSY ) / 96 + 1; 1597 1598 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1599 GetCharWidthW( hDC, '0', '0', &text_offset ); 1600 text_offset /= 2; 1601 1602 parent = GetParent(hwnd); 1603 if (!parent) parent = hwnd; 1604 #if defined(__REACTOS__) && defined(_USER32_) 1605 hBrush = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC); 1606 #else 1607 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, 1608 (WPARAM)hDC, (LPARAM)hwnd); 1609 if (!hBrush) /* did the app forget to call defwindowproc ? */ 1610 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, 1611 (WPARAM)hDC, (LPARAM)hwnd ); 1612 #endif 1613 hrgn = set_control_clipping( hDC, &client ); 1614 1615 if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT) 1616 { 1617 /* magic +4 is what CTL3D expects */ 1618 1619 rtext.right -= checkBoxWidth + text_offset;; 1620 rbox.left = rbox.right - checkBoxWidth; 1621 } 1622 else 1623 { 1624 rtext.left += checkBoxWidth + text_offset;; 1625 rbox.right = checkBoxWidth; 1626 } 1627 1628 /* Since WM_ERASEBKGND does nothing, first prepare background */ 1629 if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush ); 1630 if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush ); 1631 1632 /* Draw label */ 1633 client = rtext; 1634 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext); 1635 1636 /* Only adjust rbox when rtext is valid */ 1637 if (dtFlags != (UINT)-1L) 1638 { 1639 rbox.top = rtext.top; 1640 rbox.bottom = rtext.bottom; 1641 } 1642 1643 /* Draw the check-box bitmap */ 1644 if (action == ODA_DRAWENTIRE || action == ODA_SELECT) 1645 { 1646 UINT flags; 1647 1648 if ((get_button_type(style) == BS_RADIOBUTTON) || 1649 (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO; 1650 else if (state & BST_INDETERMINATE) flags = DFCS_BUTTON3STATE; 1651 else flags = DFCS_BUTTONCHECK; 1652 1653 if (state & (BST_CHECKED | BST_INDETERMINATE)) flags |= DFCS_CHECKED; 1654 if (state & BST_PUSHED) flags |= DFCS_PUSHED; 1655 1656 if (style & WS_DISABLED) flags |= DFCS_INACTIVE; 1657 1658 /* rbox must have the correct height */ 1659 delta = rbox.bottom - rbox.top - checkBoxHeight; 1660 1661 if (style & BS_TOP) { 1662 if (delta > 0) { 1663 rbox.bottom = rbox.top + checkBoxHeight; 1664 } else { 1665 rbox.top -= -delta/2 + 1; 1666 rbox.bottom = rbox.top + checkBoxHeight; 1667 } 1668 } else if (style & BS_BOTTOM) { 1669 if (delta > 0) { 1670 rbox.top = rbox.bottom - checkBoxHeight; 1671 } else { 1672 rbox.bottom += -delta/2 + 1; 1673 rbox.top = rbox.bottom - checkBoxHeight; 1674 } 1675 } else { /* Default */ 1676 if (delta > 0) { 1677 int ofs = (delta / 2); 1678 rbox.bottom -= ofs + 1; 1679 rbox.top = rbox.bottom - checkBoxHeight; 1680 } else if (delta < 0) { 1681 int ofs = (-delta / 2); 1682 rbox.top -= ofs + 1; 1683 rbox.bottom = rbox.top + checkBoxHeight; 1684 } 1685 } 1686 1687 DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags ); 1688 } 1689 1690 if (dtFlags == (UINT)-1L) /* Noting to draw */ 1691 return; 1692 1693 if (action == ODA_DRAWENTIRE) 1694 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext); 1695 1696 /* ... and focus */ 1697 if (action == ODA_FOCUS || (state & BST_FOCUS)) 1698 { 1699 #ifdef __REACTOS__ 1700 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) 1701 { 1702 #endif 1703 rtext.left--; 1704 rtext.right++; 1705 IntersectRect(&rtext, &rtext, &client); 1706 DrawFocusRect( hDC, &rtext ); 1707 #ifdef __REACTOS__ 1708 } 1709 #endif 1710 } 1711 SelectClipRgn( hDC, hrgn ); 1712 if (hrgn) DeleteObject( hrgn ); 1713 } 1714 1715 1716 /********************************************************************** 1717 * BUTTON_CheckAutoRadioButton 1718 * 1719 * hwnd is checked, uncheck every other auto radio button in group 1720 */ 1721 static void BUTTON_CheckAutoRadioButton( HWND hwnd ) 1722 { 1723 HWND parent, sibling, start; 1724 1725 parent = GetParent(hwnd); 1726 /* make sure that starting control is not disabled or invisible */ 1727 start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE ); 1728 do 1729 { 1730 if (!sibling) break; 1731 if ((hwnd != sibling) && 1732 ((GetWindowLongPtrW( sibling, GWL_STYLE) & BS_TYPEMASK) == BS_AUTORADIOBUTTON)) 1733 SendMessageW( sibling, BM_SETCHECK, BST_UNCHECKED, 0 ); 1734 sibling = GetNextDlgGroupItem( parent, sibling, FALSE ); 1735 } while (sibling != start); 1736 } 1737 1738 1739 /********************************************************************** 1740 * Group Box Functions 1741 */ 1742 1743 static void GB_Paint( HWND hwnd, HDC hDC, UINT action ) 1744 { 1745 RECT rc, rcFrame; 1746 HBRUSH hbr; 1747 HFONT hFont; 1748 UINT dtFlags; 1749 TEXTMETRICW tm; 1750 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); 1751 HWND parent; 1752 HRGN hrgn; 1753 1754 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1755 /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */ 1756 parent = GetParent(hwnd); 1757 if (!parent) parent = hwnd; 1758 #if defined(__REACTOS__) && defined(_USER32_) 1759 hbr = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC); 1760 #else 1761 hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd); 1762 if (!hbr) /* did the app forget to call defwindowproc ? */ 1763 hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, 1764 (WPARAM)hDC, (LPARAM)hwnd); 1765 #endif 1766 GetClientRect( hwnd, &rc); 1767 rcFrame = rc; 1768 hrgn = set_control_clipping( hDC, &rc ); 1769 1770 GetTextMetricsW (hDC, &tm); 1771 rcFrame.top += (tm.tmHeight / 2) - 1; 1772 DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0)); 1773 1774 InflateRect(&rc, -7, 1); 1775 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc); 1776 1777 if (dtFlags != (UINT)-1L) 1778 { 1779 /* Because buttons have CS_PARENTDC class style, there is a chance 1780 * that label will be drawn out of client rect. 1781 * But Windows doesn't clip label's rect, so do I. 1782 */ 1783 1784 /* There is 1-pixel margin at the left, right, and bottom */ 1785 rc.left--; rc.right++; rc.bottom++; 1786 FillRect(hDC, &rc, hbr); 1787 rc.left++; rc.right--; rc.bottom--; 1788 1789 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc); 1790 } 1791 SelectClipRgn( hDC, hrgn ); 1792 if (hrgn) DeleteObject( hrgn ); 1793 } 1794 1795 1796 /********************************************************************** 1797 * User Button Functions 1798 */ 1799 1800 static void UB_Paint( HWND hwnd, HDC hDC, UINT action ) 1801 { 1802 RECT rc; 1803 HBRUSH hBrush; 1804 HFONT hFont; 1805 LONG state = get_button_state( hwnd ); 1806 HWND parent; 1807 1808 GetClientRect( hwnd, &rc); 1809 1810 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); 1811 1812 parent = GetParent(hwnd); 1813 if (!parent) parent = hwnd; 1814 #if defined(__REACTOS__) && defined(_USER32_) 1815 hBrush = GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); 1816 #else 1817 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd); 1818 if (!hBrush) /* did the app forget to call defwindowproc ? */ 1819 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, 1820 (WPARAM)hDC, (LPARAM)hwnd); 1821 #endif 1822 1823 FillRect( hDC, &rc, hBrush ); 1824 if (action == ODA_FOCUS || (state & BST_FOCUS)) 1825 #ifdef __REACTOS__ 1826 { 1827 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) 1828 #endif 1829 DrawFocusRect( hDC, &rc ); 1830 #ifdef __REACTOS__ 1831 } 1832 #endif 1833 1834 switch (action) 1835 { 1836 case ODA_FOCUS: 1837 BUTTON_NOTIFY_PARENT( hwnd, (state & BST_FOCUS) ? BN_SETFOCUS : BN_KILLFOCUS ); 1838 break; 1839 1840 case ODA_SELECT: 1841 BUTTON_NOTIFY_PARENT( hwnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE ); 1842 break; 1843 1844 default: 1845 BUTTON_NOTIFY_PARENT( hwnd, BN_PAINT ); 1846 break; 1847 } 1848 } 1849 1850 1851 /********************************************************************** 1852 * Ownerdrawn Button Functions 1853 */ 1854 1855 static void OB_Paint( HWND hwnd, HDC hDC, UINT action ) 1856 { 1857 LONG state = get_button_state( hwnd ); 1858 DRAWITEMSTRUCT dis; 1859 LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID ); 1860 HWND parent; 1861 HFONT hFont, hPrevFont = 0; 1862 HRGN hrgn; 1863 1864 dis.CtlType = ODT_BUTTON; 1865 dis.CtlID = id; 1866 dis.itemID = 0; 1867 dis.itemAction = action; 1868 dis.itemState = ((state & BST_FOCUS) ? ODS_FOCUS : 0) | 1869 ((state & BST_PUSHED) ? ODS_SELECTED : 0) | 1870 (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED); 1871 dis.hwndItem = hwnd; 1872 dis.hDC = hDC; 1873 dis.itemData = 0; 1874 GetClientRect( hwnd, &dis.rcItem ); 1875 1876 if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont ); 1877 parent = GetParent(hwnd); 1878 if (!parent) parent = hwnd; 1879 #if defined(__REACTOS__) && defined(_USER32_) 1880 GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); 1881 #else 1882 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); 1883 #endif 1884 1885 hrgn = set_control_clipping( hDC, &dis.rcItem ); 1886 1887 SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis ); 1888 if (hPrevFont) SelectObject(hDC, hPrevFont); 1889 SelectClipRgn( hDC, hrgn ); 1890 if (hrgn) DeleteObject( hrgn ); 1891 } 1892 1893 #ifndef _USER32_ 1894 void BUTTON_Register() 1895 { 1896 WNDCLASSW wndClass; 1897 1898 ZeroMemory(&wndClass, sizeof(WNDCLASSW)); 1899 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC; 1900 wndClass.lpfnWndProc = ButtonWndProcW; 1901 wndClass.cbClsExtra = 0; 1902 wndClass.cbWndExtra = sizeof(PBUTTON_DATA); 1903 wndClass.hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); 1904 wndClass.hbrBackground = 0; 1905 wndClass.lpszClassName = WC_BUTTONW; 1906 1907 RegisterClassW(&wndClass); 1908 } 1909 1910 void BUTTON_Unregister() 1911 { 1912 UnregisterClassW(WC_BUTTONW, NULL); 1913 } 1914 #endif