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