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