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
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.
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.
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
23 * - BS_NOTIFY: is it complete?
24 * - BS_RIGHTBUTTON: same as BS_LEFTTEXT
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.
35 * - BN_PUSHED/BN_HILITE
36 * + BN_KILLFOCUS: is it OK?
38 * + BN_SETFOCUS: is it OK?
39 * - BN_UNPUSHED/BN_UNHILITE
42 * Structures/Macros/Definitions
58 #include "wine/debug.h"
59 #include "wine/heap.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(button
);
65 /* undocumented flags */
66 #define BUTTON_NSTATES 0x0F
67 #define BUTTON_BTNPRESSED 0x40
68 #define BUTTON_UNKNOWN2 0x20
69 #define BUTTON_UNKNOWN3 0x10
71 #define BUTTON_NOTIFY_PARENT(hWnd, code) \
72 do { /* Notify parent which has created this button control */ \
73 TRACE("notification " #code " sent to hwnd=%p\n", GetParent(hWnd)); \
74 SendMessageW(GetParent(hWnd), WM_COMMAND, \
75 MAKEWPARAM(GetWindowLongPtrW((hWnd),GWLP_ID), (code)), \
79 typedef struct _BUTTON_INFO
88 DWORD image_type
; /* IMAGE_BITMAP or IMAGE_ICON */
89 BUTTON_IMAGELIST imagelist
;
99 static UINT
BUTTON_CalcLayoutRects( const BUTTON_INFO
*infoPtr
, HDC hdc
, RECT
*labelRc
, RECT
*imageRc
, RECT
*textRc
);
100 static void PB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
);
101 static void CB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
);
102 static void GB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
);
103 static void UB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
);
104 static void OB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
);
105 static void BUTTON_CheckAutoRadioButton( HWND hwnd
);
107 #define MAX_BTN_TYPE 16
109 static const WORD maxCheckState
[MAX_BTN_TYPE
] =
111 BST_UNCHECKED
, /* BS_PUSHBUTTON */
112 BST_UNCHECKED
, /* BS_DEFPUSHBUTTON */
113 BST_CHECKED
, /* BS_CHECKBOX */
114 BST_CHECKED
, /* BS_AUTOCHECKBOX */
115 BST_CHECKED
, /* BS_RADIOBUTTON */
116 BST_INDETERMINATE
, /* BS_3STATE */
117 BST_INDETERMINATE
, /* BS_AUTO3STATE */
118 BST_UNCHECKED
, /* BS_GROUPBOX */
119 BST_UNCHECKED
, /* BS_USERBUTTON */
120 BST_CHECKED
, /* BS_AUTORADIOBUTTON */
121 BST_UNCHECKED
, /* BS_PUSHBOX */
122 BST_UNCHECKED
, /* BS_OWNERDRAW */
123 BST_UNCHECKED
, /* BS_SPLITBUTTON */
124 BST_UNCHECKED
, /* BS_DEFSPLITBUTTON */
125 BST_UNCHECKED
, /* BS_COMMANDLINK */
126 BST_UNCHECKED
/* BS_DEFCOMMANDLINK */
129 /* Generic draw states, use get_draw_state() to get specific state for button type */
140 typedef void (*pfPaint
)( const BUTTON_INFO
*infoPtr
, HDC hdc
, UINT action
);
142 static const pfPaint btnPaintFunc
[MAX_BTN_TYPE
] =
144 PB_Paint
, /* BS_PUSHBUTTON */
145 PB_Paint
, /* BS_DEFPUSHBUTTON */
146 CB_Paint
, /* BS_CHECKBOX */
147 CB_Paint
, /* BS_AUTOCHECKBOX */
148 CB_Paint
, /* BS_RADIOBUTTON */
149 CB_Paint
, /* BS_3STATE */
150 CB_Paint
, /* BS_AUTO3STATE */
151 GB_Paint
, /* BS_GROUPBOX */
152 UB_Paint
, /* BS_USERBUTTON */
153 CB_Paint
, /* BS_AUTORADIOBUTTON */
154 NULL
, /* BS_PUSHBOX */
155 OB_Paint
, /* BS_OWNERDRAW */
156 PB_Paint
, /* BS_SPLITBUTTON */
157 PB_Paint
, /* BS_DEFSPLITBUTTON */
158 PB_Paint
, /* BS_COMMANDLINK */
159 PB_Paint
/* BS_DEFCOMMANDLINK */
162 typedef void (*pfThemedPaint
)( HTHEME theme
, const BUTTON_INFO
*infoPtr
, HDC hdc
, int drawState
, UINT dtflags
, BOOL focused
);
164 static void PB_ThemedPaint( HTHEME theme
, const BUTTON_INFO
*infoPtr
, HDC hdc
, int drawState
, UINT dtflags
, BOOL focused
);
165 static void CB_ThemedPaint( HTHEME theme
, const BUTTON_INFO
*infoPtr
, HDC hdc
, int drawState
, UINT dtflags
, BOOL focused
);
166 static void GB_ThemedPaint( HTHEME theme
, const BUTTON_INFO
*infoPtr
, HDC hdc
, int drawState
, UINT dtflags
, BOOL focused
);
168 static const pfThemedPaint btnThemedPaintFunc
[MAX_BTN_TYPE
] =
170 PB_ThemedPaint
, /* BS_PUSHBUTTON */
171 PB_ThemedPaint
, /* BS_DEFPUSHBUTTON */
172 CB_ThemedPaint
, /* BS_CHECKBOX */
173 CB_ThemedPaint
, /* BS_AUTOCHECKBOX */
174 CB_ThemedPaint
, /* BS_RADIOBUTTON */
175 CB_ThemedPaint
, /* BS_3STATE */
176 CB_ThemedPaint
, /* BS_AUTO3STATE */
177 GB_ThemedPaint
, /* BS_GROUPBOX */
178 NULL
, /* BS_USERBUTTON */
179 CB_ThemedPaint
, /* BS_AUTORADIOBUTTON */
180 NULL
, /* BS_PUSHBOX */
181 NULL
, /* BS_OWNERDRAW */
182 NULL
, /* BS_SPLITBUTTON */
183 NULL
, /* BS_DEFSPLITBUTTON */
184 NULL
, /* BS_COMMANDLINK */
185 NULL
, /* BS_DEFCOMMANDLINK */
188 typedef BOOL (*pfGetIdealSize
)(BUTTON_INFO
*infoPtr
, SIZE
*size
);
190 static BOOL
PB_GetIdealSize(BUTTON_INFO
*infoPtr
, SIZE
*size
);
191 static BOOL
CB_GetIdealSize(BUTTON_INFO
*infoPtr
, SIZE
*size
);
192 static BOOL
GB_GetIdealSize(BUTTON_INFO
*infoPtr
, SIZE
*size
);
194 static const pfGetIdealSize btnGetIdealSizeFunc
[MAX_BTN_TYPE
] = {
195 PB_GetIdealSize
, /* BS_PUSHBUTTON */
196 PB_GetIdealSize
, /* BS_DEFPUSHBUTTON */
197 CB_GetIdealSize
, /* BS_CHECKBOX */
198 CB_GetIdealSize
, /* BS_AUTOCHECKBOX */
199 CB_GetIdealSize
, /* BS_RADIOBUTTON */
200 GB_GetIdealSize
, /* BS_3STATE */
201 GB_GetIdealSize
, /* BS_AUTO3STATE */
202 GB_GetIdealSize
, /* BS_GROUPBOX */
203 PB_GetIdealSize
, /* BS_USERBUTTON */
204 CB_GetIdealSize
, /* BS_AUTORADIOBUTTON */
205 GB_GetIdealSize
, /* BS_PUSHBOX */
206 GB_GetIdealSize
, /* BS_OWNERDRAW */
207 /* GetIdealSize() for following types are unimplemented, use BS_PUSHBUTTON's for now */
208 PB_GetIdealSize
, /* BS_SPLITBUTTON */
209 PB_GetIdealSize
, /* BS_DEFSPLITBUTTON */
210 PB_GetIdealSize
, /* BS_COMMANDLINK */
211 PB_GetIdealSize
/* BS_DEFCOMMANDLINK */
214 static inline UINT
get_button_type( LONG window_style
)
216 return (window_style
& BS_TYPEMASK
);
219 /* paint a button of any type */
220 static inline void paint_button( BUTTON_INFO
*infoPtr
, LONG style
, UINT action
)
222 if (btnPaintFunc
[style
] && IsWindowVisible(infoPtr
->hwnd
))
224 HDC hdc
= GetDC( infoPtr
->hwnd
);
225 btnPaintFunc
[style
]( infoPtr
, hdc
, action
);
226 ReleaseDC( infoPtr
->hwnd
, hdc
);
230 /* retrieve the button text; returned buffer must be freed by caller */
231 static inline WCHAR
*get_button_text( const BUTTON_INFO
*infoPtr
)
233 INT len
= GetWindowTextLengthW( infoPtr
->hwnd
);
234 WCHAR
*buffer
= heap_alloc( (len
+ 1) * sizeof(WCHAR
) );
236 GetWindowTextW( infoPtr
->hwnd
, buffer
, len
+ 1 );
240 HRGN
set_control_clipping( HDC hdc
, const RECT
*rect
)
243 HRGN hrgn
= CreateRectRgn( 0, 0, 0, 0 );
245 if (GetClipRgn( hdc
, hrgn
) != 1)
247 DeleteObject( hrgn
);
250 DPtoLP( hdc
, (POINT
*)&rc
, 2 );
251 if (GetLayout( hdc
) & LAYOUT_RTL
) /* compensate for the shifting done by IntersectClipRect */
256 IntersectClipRect( hdc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
260 static WCHAR
*heap_strndupW(const WCHAR
*src
, size_t length
)
262 size_t size
= (length
+ 1) * sizeof(WCHAR
);
263 WCHAR
*dst
= heap_alloc(size
);
264 if (dst
) memcpy(dst
, src
, size
);
268 /**********************************************************************
269 * Convert button styles to flags used by DrawText.
271 static UINT
BUTTON_BStoDT( DWORD style
, DWORD ex_style
)
273 UINT dtStyle
= DT_NOCLIP
; /* We use SelectClipRgn to limit output */
275 /* "Convert" pushlike buttons to pushbuttons */
276 if (style
& BS_PUSHLIKE
)
277 style
&= ~BS_TYPEMASK
;
279 if (!(style
& BS_MULTILINE
))
280 dtStyle
|= DT_SINGLELINE
;
282 dtStyle
|= DT_WORDBREAK
;
284 switch (style
& BS_CENTER
)
286 case BS_LEFT
: /* DT_LEFT is 0 */ break;
287 case BS_RIGHT
: dtStyle
|= DT_RIGHT
; break;
288 case BS_CENTER
: dtStyle
|= DT_CENTER
; break;
290 /* Pushbutton's text is centered by default */
291 if (get_button_type(style
) <= BS_DEFPUSHBUTTON
) dtStyle
|= DT_CENTER
;
292 /* all other flavours have left aligned text */
295 if (ex_style
& WS_EX_RIGHT
) dtStyle
= DT_RIGHT
| (dtStyle
& ~(DT_LEFT
| DT_CENTER
));
297 /* DrawText ignores vertical alignment for multiline text,
298 * but we use these flags to align label manually.
300 if (get_button_type(style
) != BS_GROUPBOX
)
302 switch (style
& BS_VCENTER
)
304 case BS_TOP
: /* DT_TOP is 0 */ break;
305 case BS_BOTTOM
: dtStyle
|= DT_BOTTOM
; break;
306 case BS_VCENTER
: /* fall through */
307 default: dtStyle
|= DT_VCENTER
; break;
314 static int get_draw_state(const BUTTON_INFO
*infoPtr
)
316 static const int pb_states
[DRAW_STATE_COUNT
] = { PBS_NORMAL
, PBS_DISABLED
, PBS_HOT
, PBS_PRESSED
, PBS_DEFAULTED
};
317 static const int cb_states
[3][DRAW_STATE_COUNT
] =
319 { CBS_UNCHECKEDNORMAL
, CBS_UNCHECKEDDISABLED
, CBS_UNCHECKEDHOT
, CBS_UNCHECKEDPRESSED
, CBS_UNCHECKEDNORMAL
},
320 { CBS_CHECKEDNORMAL
, CBS_CHECKEDDISABLED
, CBS_CHECKEDHOT
, CBS_CHECKEDPRESSED
, CBS_CHECKEDNORMAL
},
321 { CBS_MIXEDNORMAL
, CBS_MIXEDDISABLED
, CBS_MIXEDHOT
, CBS_MIXEDPRESSED
, CBS_MIXEDNORMAL
}
323 static const int rb_states
[2][DRAW_STATE_COUNT
] =
325 { RBS_UNCHECKEDNORMAL
, RBS_UNCHECKEDDISABLED
, RBS_UNCHECKEDHOT
, RBS_UNCHECKEDPRESSED
, RBS_UNCHECKEDNORMAL
},
326 { RBS_CHECKEDNORMAL
, RBS_CHECKEDDISABLED
, RBS_CHECKEDHOT
, RBS_CHECKEDPRESSED
, RBS_CHECKEDNORMAL
}
328 static const int gb_states
[DRAW_STATE_COUNT
] = { GBS_NORMAL
, GBS_DISABLED
, GBS_NORMAL
, GBS_NORMAL
, GBS_NORMAL
};
329 LONG style
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
330 UINT type
= get_button_type(style
);
331 int check_state
= infoPtr
->state
& 3;
332 enum draw_state state
;
334 if (!IsWindowEnabled(infoPtr
->hwnd
))
335 state
= STATE_DISABLED
;
336 else if (infoPtr
->state
& BST_PUSHED
)
337 state
= STATE_PRESSED
;
338 else if (infoPtr
->state
& BST_HOT
)
340 else if (infoPtr
->state
& BST_FOCUS
)
341 state
= STATE_DEFAULTED
;
343 state
= STATE_NORMAL
;
348 case BS_DEFPUSHBUTTON
:
351 case BS_DEFSPLITBUTTON
:
353 case BS_DEFCOMMANDLINK
:
354 return pb_states
[state
];
356 case BS_AUTOCHECKBOX
:
357 return cb_states
[check_state
][state
];
361 case BS_AUTORADIOBUTTON
:
362 return rb_states
[check_state
][state
];
364 return gb_states
[state
];
366 WARN("Unsupported button type 0x%08x\n", type
);
371 static LRESULT CALLBACK
BUTTON_WindowProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
373 BUTTON_INFO
*infoPtr
= (BUTTON_INFO
*)GetWindowLongPtrW(hWnd
, 0);
376 LONG style
= GetWindowLongW( hWnd
, GWL_STYLE
);
377 UINT btn_type
= get_button_type( style
);
378 LONG state
, new_state
;
382 if (!IsWindow( hWnd
)) return 0;
384 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
385 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
387 pt
.x
= (short)LOWORD(lParam
);
388 pt
.y
= (short)HIWORD(lParam
);
397 case BS_PUSHBUTTON
: return DLGC_BUTTON
| DLGC_UNDEFPUSHBUTTON
;
398 case BS_DEFCOMMANDLINK
:
399 case BS_DEFPUSHBUTTON
: return DLGC_BUTTON
| DLGC_DEFPUSHBUTTON
;
401 case BS_AUTORADIOBUTTON
: return DLGC_BUTTON
| DLGC_RADIOBUTTON
;
402 case BS_GROUPBOX
: return DLGC_STATIC
;
403 case BS_SPLITBUTTON
: return DLGC_BUTTON
| DLGC_UNDEFPUSHBUTTON
| DLGC_WANTARROWS
;
404 case BS_DEFSPLITBUTTON
: return DLGC_BUTTON
| DLGC_DEFPUSHBUTTON
| DLGC_WANTARROWS
;
405 default: return DLGC_BUTTON
;
409 theme
= GetWindowTheme( hWnd
);
411 RedrawWindow( hWnd
, NULL
, NULL
, RDW_FRAME
| RDW_INVALIDATE
| RDW_UPDATENOW
);
413 paint_button( infoPtr
, btn_type
, ODA_DRAWENTIRE
);
418 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
420 infoPtr
= heap_alloc_zero( sizeof(*infoPtr
) );
421 SetWindowLongPtrW( hWnd
, 0, (LONG_PTR
)infoPtr
);
422 infoPtr
->hwnd
= hWnd
;
423 infoPtr
->parent
= cs
->hwndParent
;
424 infoPtr
->style
= cs
->style
;
425 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
429 SetWindowLongPtrW( hWnd
, 0, 0 );
430 heap_free(infoPtr
->note
);
435 if (btn_type
>= MAX_BTN_TYPE
)
436 return -1; /* abort */
438 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
439 if (btn_type
== BS_USERBUTTON
)
441 style
= (style
& ~BS_TYPEMASK
) | BS_PUSHBUTTON
;
442 SetWindowLongW( hWnd
, GWL_STYLE
, style
);
444 infoPtr
->state
= BST_UNCHECKED
;
445 OpenThemeData( hWnd
, WC_BUTTONW
);
449 theme
= GetWindowTheme( hWnd
);
450 CloseThemeData( theme
);
453 case WM_THEMECHANGED
:
454 theme
= GetWindowTheme( hWnd
);
455 CloseThemeData( theme
);
456 OpenThemeData( hWnd
, WC_BUTTONW
);
460 if (btn_type
== BS_OWNERDRAW
)
462 HDC hdc
= (HDC
)wParam
;
465 HWND parent
= GetParent(hWnd
);
466 if (!parent
) parent
= hWnd
;
467 hBrush
= (HBRUSH
)SendMessageW(parent
, WM_CTLCOLORBTN
, (WPARAM
)hdc
, (LPARAM
)hWnd
);
468 if (!hBrush
) /* did the app forget to call defwindowproc ? */
469 hBrush
= (HBRUSH
)DefWindowProcW(parent
, WM_CTLCOLORBTN
,
470 (WPARAM
)hdc
, (LPARAM
)hWnd
);
471 GetClientRect(hWnd
, &rc
);
472 FillRect(hdc
, &rc
, hBrush
);
482 theme
= GetWindowTheme( hWnd
);
483 hdc
= wParam
? (HDC
)wParam
: BeginPaint( hWnd
, &ps
);
485 if (theme
&& btnThemedPaintFunc
[btn_type
])
487 int drawState
= get_draw_state(infoPtr
);
488 UINT dtflags
= BUTTON_BStoDT(style
, GetWindowLongW(hWnd
, GWL_EXSTYLE
));
490 btnThemedPaintFunc
[btn_type
](theme
, infoPtr
, hdc
, drawState
, dtflags
, infoPtr
->state
& BST_FOCUS
);
492 else if (btnPaintFunc
[btn_type
])
494 int nOldMode
= SetBkMode( hdc
, OPAQUE
);
495 btnPaintFunc
[btn_type
]( infoPtr
, hdc
, ODA_DRAWENTIRE
);
496 SetBkMode(hdc
, nOldMode
); /* reset painting mode */
499 if ( !wParam
) EndPaint( hWnd
, &ps
);
504 if (wParam
== VK_SPACE
)
506 SendMessageW( hWnd
, BM_SETSTATE
, TRUE
, 0 );
507 infoPtr
->state
|= BUTTON_BTNPRESSED
;
512 case WM_LBUTTONDBLCLK
:
513 if(style
& BS_NOTIFY
||
514 btn_type
== BS_RADIOBUTTON
||
515 btn_type
== BS_USERBUTTON
||
516 btn_type
== BS_OWNERDRAW
)
518 BUTTON_NOTIFY_PARENT(hWnd
, BN_DOUBLECLICKED
);
525 infoPtr
->state
|= BUTTON_BTNPRESSED
;
526 SendMessageW( hWnd
, BM_SETSTATE
, TRUE
, 0 );
530 if (wParam
!= VK_SPACE
)
534 state
= infoPtr
->state
;
535 if (!(state
& BUTTON_BTNPRESSED
)) break;
536 infoPtr
->state
&= BUTTON_NSTATES
;
537 if (!(state
& BST_PUSHED
))
542 SendMessageW( hWnd
, BM_SETSTATE
, FALSE
, 0 );
543 GetClientRect( hWnd
, &rect
);
544 if (uMsg
== WM_KEYUP
|| PtInRect( &rect
, pt
))
548 case BS_AUTOCHECKBOX
:
549 SendMessageW( hWnd
, BM_SETCHECK
, !(infoPtr
->state
& BST_CHECKED
), 0 );
551 case BS_AUTORADIOBUTTON
:
552 SendMessageW( hWnd
, BM_SETCHECK
, TRUE
, 0 );
555 SendMessageW( hWnd
, BM_SETCHECK
, (infoPtr
->state
& BST_INDETERMINATE
) ? 0 :
556 ((infoPtr
->state
& 3) + 1), 0 );
560 BUTTON_NOTIFY_PARENT(hWnd
, BN_CLICKED
);
568 case WM_CAPTURECHANGED
:
569 TRACE("WM_CAPTURECHANGED %p\n", hWnd
);
570 if (hWnd
== (HWND
)lParam
) break;
571 if (infoPtr
->state
& BUTTON_BTNPRESSED
)
573 infoPtr
->state
&= BUTTON_NSTATES
;
574 if (infoPtr
->state
& BST_PUSHED
)
575 SendMessageW( hWnd
, BM_SETSTATE
, FALSE
, 0 );
581 TRACKMOUSEEVENT mouse_event
;
583 mouse_event
.cbSize
= sizeof(TRACKMOUSEEVENT
);
584 mouse_event
.dwFlags
= TME_QUERY
;
585 if (!TrackMouseEvent(&mouse_event
) || !(mouse_event
.dwFlags
& (TME_HOVER
| TME_LEAVE
)))
587 mouse_event
.dwFlags
= TME_HOVER
| TME_LEAVE
;
588 mouse_event
.hwndTrack
= hWnd
;
589 mouse_event
.dwHoverTime
= 1;
590 TrackMouseEvent(&mouse_event
);
593 if ((wParam
& MK_LBUTTON
) && GetCapture() == hWnd
)
595 GetClientRect( hWnd
, &rect
);
596 SendMessageW( hWnd
, BM_SETSTATE
, PtInRect(&rect
, pt
), 0 );
603 infoPtr
->state
|= BST_HOT
;
604 InvalidateRect( hWnd
, NULL
, FALSE
);
610 infoPtr
->state
&= ~BST_HOT
;
611 InvalidateRect( hWnd
, NULL
, FALSE
);
617 /* Clear an old text here as Windows does */
618 if (IsWindowVisible(hWnd
))
620 HDC hdc
= GetDC(hWnd
);
623 HWND parent
= GetParent(hWnd
);
624 UINT message
= (btn_type
== BS_PUSHBUTTON
||
625 btn_type
== BS_DEFPUSHBUTTON
||
626 btn_type
== BS_USERBUTTON
||
627 btn_type
== BS_OWNERDRAW
) ?
628 WM_CTLCOLORBTN
: WM_CTLCOLORSTATIC
;
630 if (!parent
) parent
= hWnd
;
631 hbrush
= (HBRUSH
)SendMessageW(parent
, message
,
632 (WPARAM
)hdc
, (LPARAM
)hWnd
);
633 if (!hbrush
) /* did the app forget to call DefWindowProc ? */
634 hbrush
= (HBRUSH
)DefWindowProcW(parent
, message
,
635 (WPARAM
)hdc
, (LPARAM
)hWnd
);
637 GetClientRect(hWnd
, &client
);
639 /* FIXME: check other BS_* handlers */
640 if (btn_type
== BS_GROUPBOX
)
641 InflateRect(&rc
, -7, 1); /* GB_Paint does this */
642 BUTTON_CalcLayoutRects(infoPtr
, hdc
, &rc
, NULL
, NULL
);
643 /* Clip by client rect bounds */
644 if (rc
.right
> client
.right
) rc
.right
= client
.right
;
645 if (rc
.bottom
> client
.bottom
) rc
.bottom
= client
.bottom
;
646 FillRect(hdc
, &rc
, hbrush
);
647 ReleaseDC(hWnd
, hdc
);
650 DefWindowProcW( hWnd
, WM_SETTEXT
, wParam
, lParam
);
651 if (btn_type
== BS_GROUPBOX
) /* Yes, only for BS_GROUPBOX */
652 InvalidateRect( hWnd
, NULL
, TRUE
);
654 paint_button( infoPtr
, btn_type
, ODA_DRAWENTIRE
);
655 return 1; /* success. FIXME: check text length */
660 WCHAR
*note
= (WCHAR
*)lParam
;
661 if (btn_type
!= BS_COMMANDLINK
&& btn_type
!= BS_DEFCOMMANDLINK
)
663 SetLastError(ERROR_NOT_SUPPORTED
);
667 heap_free(infoPtr
->note
);
670 infoPtr
->note_length
= lstrlenW(note
);
671 infoPtr
->note
= heap_strndupW(note
, infoPtr
->note_length
);
674 if (!note
|| !infoPtr
->note
)
676 infoPtr
->note_length
= 0;
677 infoPtr
->note
= heap_alloc_zero(sizeof(WCHAR
));
680 SetLastError(NO_ERROR
);
686 DWORD
*size
= (DWORD
*)wParam
;
687 WCHAR
*buffer
= (WCHAR
*)lParam
;
690 if (btn_type
!= BS_COMMANDLINK
&& btn_type
!= BS_DEFCOMMANDLINK
)
692 SetLastError(ERROR_NOT_SUPPORTED
);
696 if (!buffer
|| !size
|| !infoPtr
->note
)
698 SetLastError(ERROR_INVALID_PARAMETER
);
704 length
= min(*size
- 1, infoPtr
->note_length
);
705 memcpy(buffer
, infoPtr
->note
, length
* sizeof(WCHAR
));
706 buffer
[length
] = '\0';
709 if (*size
< infoPtr
->note_length
+ 1)
711 *size
= infoPtr
->note_length
+ 1;
712 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
717 SetLastError(NO_ERROR
);
722 case BCM_GETNOTELENGTH
:
724 if (btn_type
!= BS_COMMANDLINK
&& btn_type
!= BS_DEFCOMMANDLINK
)
726 SetLastError(ERROR_NOT_SUPPORTED
);
730 return infoPtr
->note_length
;
734 infoPtr
->font
= (HFONT
)wParam
;
735 if (lParam
) InvalidateRect(hWnd
, NULL
, TRUE
);
739 return (LRESULT
)infoPtr
->font
;
742 TRACE("WM_SETFOCUS %p\n",hWnd
);
743 infoPtr
->state
|= BST_FOCUS
;
744 paint_button( infoPtr
, btn_type
, ODA_FOCUS
);
745 if (style
& BS_NOTIFY
)
746 BUTTON_NOTIFY_PARENT(hWnd
, BN_SETFOCUS
);
750 TRACE("WM_KILLFOCUS %p\n",hWnd
);
751 infoPtr
->state
&= ~BST_FOCUS
;
752 paint_button( infoPtr
, btn_type
, ODA_FOCUS
);
754 if ((infoPtr
->state
& BUTTON_BTNPRESSED
) && GetCapture() == hWnd
)
756 if (style
& BS_NOTIFY
)
757 BUTTON_NOTIFY_PARENT(hWnd
, BN_KILLFOCUS
);
759 InvalidateRect( hWnd
, NULL
, FALSE
);
762 case WM_SYSCOLORCHANGE
:
763 InvalidateRect( hWnd
, NULL
, FALSE
);
767 btn_type
= wParam
& BS_TYPEMASK
;
768 style
= (style
& ~BS_TYPEMASK
) | btn_type
;
769 SetWindowLongW( hWnd
, GWL_STYLE
, style
);
771 /* Only redraw if lParam flag is set.*/
773 InvalidateRect( hWnd
, NULL
, TRUE
);
778 SendMessageW( hWnd
, WM_LBUTTONDOWN
, 0, 0 );
779 SendMessageW( hWnd
, WM_LBUTTONUP
, 0, 0 );
783 infoPtr
->image_type
= (DWORD
)wParam
;
784 oldHbitmap
= infoPtr
->u
.image
;
785 infoPtr
->u
.image
= (HANDLE
)lParam
;
786 InvalidateRect( hWnd
, NULL
, FALSE
);
787 return (LRESULT
)oldHbitmap
;
790 return (LRESULT
)infoPtr
->u
.image
;
792 case BCM_SETIMAGELIST
:
794 BUTTON_IMAGELIST
*imagelist
= (BUTTON_IMAGELIST
*)lParam
;
796 if (!imagelist
) return FALSE
;
798 infoPtr
->imagelist
= *imagelist
;
802 case BCM_GETIMAGELIST
:
804 BUTTON_IMAGELIST
*imagelist
= (BUTTON_IMAGELIST
*)lParam
;
806 if (!imagelist
) return FALSE
;
808 *imagelist
= infoPtr
->imagelist
;
813 return infoPtr
->state
& 3;
816 if (wParam
> maxCheckState
[btn_type
]) wParam
= maxCheckState
[btn_type
];
817 if ((btn_type
== BS_RADIOBUTTON
) || (btn_type
== BS_AUTORADIOBUTTON
))
819 style
= wParam
? style
| WS_TABSTOP
: style
& ~WS_TABSTOP
;
820 SetWindowLongW( hWnd
, GWL_STYLE
, style
);
822 if ((infoPtr
->state
& 3) != wParam
)
824 infoPtr
->state
= (infoPtr
->state
& ~3) | wParam
;
825 InvalidateRect( hWnd
, NULL
, FALSE
);
827 if ((btn_type
== BS_AUTORADIOBUTTON
) && (wParam
== BST_CHECKED
) && (style
& WS_CHILD
))
828 BUTTON_CheckAutoRadioButton( hWnd
);
832 return infoPtr
->state
;
835 state
= infoPtr
->state
;
836 new_state
= wParam
? BST_PUSHED
: 0;
838 if ((state
^ new_state
) & BST_PUSHED
)
843 state
&= ~BST_PUSHED
;
845 if (btn_type
== BS_USERBUTTON
)
846 BUTTON_NOTIFY_PARENT( hWnd
, (state
& BST_PUSHED
) ? BN_HILITE
: BN_UNHILITE
);
847 infoPtr
->state
= state
;
849 InvalidateRect( hWnd
, NULL
, FALSE
);
853 case BCM_SETTEXTMARGIN
:
855 RECT
*text_margin
= (RECT
*)lParam
;
857 if (!text_margin
) return FALSE
;
859 infoPtr
->text_margin
= *text_margin
;
863 case BCM_GETTEXTMARGIN
:
865 RECT
*text_margin
= (RECT
*)lParam
;
867 if (!text_margin
) return FALSE
;
869 *text_margin
= infoPtr
->text_margin
;
873 case BCM_GETIDEALSIZE
:
875 SIZE
*size
= (SIZE
*)lParam
;
877 if (!size
) return FALSE
;
879 return btnGetIdealSizeFunc
[btn_type
](infoPtr
, size
);
883 if(btn_type
== BS_GROUPBOX
) return HTTRANSPARENT
;
886 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
891 /* If maxWidth is zero, rectangle width is unlimited */
892 static RECT
BUTTON_GetTextRect(const BUTTON_INFO
*infoPtr
, HDC hdc
, const WCHAR
*text
, LONG maxWidth
)
894 LONG style
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
895 LONG exStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_EXSTYLE
);
896 UINT dtStyle
= BUTTON_BStoDT(style
, exStyle
);
900 rect
.right
= maxWidth
;
901 hPrevFont
= SelectObject(hdc
, infoPtr
->font
);
902 /* Calculate height without DT_VCENTER and DT_BOTTOM to get the correct height */
903 DrawTextW(hdc
, text
, -1, &rect
, (dtStyle
& ~(DT_VCENTER
| DT_BOTTOM
)) | DT_CALCRECT
);
904 if (hPrevFont
) SelectObject(hdc
, hPrevFont
);
909 static BOOL
show_image_only(const BUTTON_INFO
*infoPtr
)
911 LONG style
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
912 return (style
& (BS_ICON
| BS_BITMAP
)) && (infoPtr
->u
.image
|| infoPtr
->imagelist
.himl
);
915 static BOOL
show_image_and_text(const BUTTON_INFO
*infoPtr
)
917 LONG style
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
918 UINT type
= get_button_type(style
);
919 return !(style
& (BS_ICON
| BS_BITMAP
))
920 && ((infoPtr
->u
.image
921 && (type
== BS_PUSHBUTTON
|| type
== BS_DEFPUSHBUTTON
|| type
== BS_USERBUTTON
|| type
== BS_SPLITBUTTON
922 || type
== BS_DEFSPLITBUTTON
|| type
== BS_COMMANDLINK
|| type
== BS_DEFCOMMANDLINK
))
923 || (infoPtr
->imagelist
.himl
&& type
!= BS_GROUPBOX
));
926 static BOOL
show_image(const BUTTON_INFO
*infoPtr
)
928 return show_image_only(infoPtr
) || show_image_and_text(infoPtr
);
931 /* Get a bounding rectangle that is large enough to contain a image and a text side by side.
932 * Note: (left,top) of the result rectangle may not be (0,0), offset it by yourself if needed */
933 static RECT
BUTTON_GetBoundingLabelRect(LONG style
, const RECT
*textRect
, const RECT
*imageRect
)
936 RECT rect
= *imageRect
;
937 INT textWidth
= textRect
->right
- textRect
->left
;
938 INT textHeight
= textRect
->bottom
- textRect
->top
;
939 INT imageWidth
= imageRect
->right
- imageRect
->left
;
940 INT imageHeight
= imageRect
->bottom
- imageRect
->top
;
942 if ((style
& BS_CENTER
) == BS_RIGHT
)
943 OffsetRect(&rect
, textWidth
, 0);
944 else if ((style
& BS_CENTER
) == BS_LEFT
)
945 OffsetRect(&rect
, -imageWidth
, 0);
946 else if ((style
& BS_VCENTER
) == BS_BOTTOM
)
947 OffsetRect(&rect
, 0, textHeight
);
948 else if ((style
& BS_VCENTER
) == BS_TOP
)
949 OffsetRect(&rect
, 0, -imageHeight
);
951 OffsetRect(&rect
, -imageWidth
, 0);
953 UnionRect(&labelRect
, textRect
, &rect
);
957 /* Position a rectangle inside a bounding rectangle according to button alignment flags */
958 static void BUTTON_PositionRect(LONG style
, const RECT
*outerRect
, RECT
*innerRect
, const RECT
*margin
)
960 INT width
= innerRect
->right
- innerRect
->left
;
961 INT height
= innerRect
->bottom
- innerRect
->top
;
963 if ((style
& WS_EX_RIGHT
) && !(style
& BS_CENTER
)) style
|= BS_CENTER
;
965 if (!(style
& BS_CENTER
))
967 /* Push button's text is centered by default, all other types have left aligned text */
968 if (get_button_type(style
) <= BS_DEFPUSHBUTTON
)
974 if (!(style
& BS_VCENTER
))
976 /* Group box's text is top aligned by default */
977 if (get_button_type(style
) == BS_GROUPBOX
)
981 switch (style
& BS_CENTER
)
984 innerRect
->left
= outerRect
->left
+ (outerRect
->right
- outerRect
->left
- width
) / 2;
985 innerRect
->right
= innerRect
->left
+ width
;
988 innerRect
->right
= outerRect
->right
- margin
->right
;
989 innerRect
->left
= innerRect
->right
- width
;
993 innerRect
->left
= outerRect
->left
+ margin
->left
;
994 innerRect
->right
= innerRect
->left
+ width
;
998 switch (style
& BS_VCENTER
)
1001 innerRect
->top
= outerRect
->top
+ margin
->top
;
1002 innerRect
->bottom
= innerRect
->top
+ height
;
1005 innerRect
->bottom
= outerRect
->bottom
- margin
->bottom
;
1006 innerRect
->top
= innerRect
->bottom
- height
;
1010 innerRect
->top
= outerRect
->top
+ (outerRect
->bottom
- outerRect
->top
- height
) / 2;
1011 innerRect
->bottom
= innerRect
->top
+ height
;
1016 /* Convert imagelist align style to button align style */
1017 static UINT
BUTTON_ILStoBS(UINT align
)
1021 case BUTTON_IMAGELIST_ALIGN_TOP
:
1022 return BS_CENTER
| BS_TOP
;
1023 case BUTTON_IMAGELIST_ALIGN_BOTTOM
:
1024 return BS_CENTER
| BS_BOTTOM
;
1025 case BUTTON_IMAGELIST_ALIGN_CENTER
:
1026 return BS_CENTER
| BS_VCENTER
;
1027 case BUTTON_IMAGELIST_ALIGN_RIGHT
:
1028 return BS_RIGHT
| BS_VCENTER
;
1029 case BUTTON_IMAGELIST_ALIGN_LEFT
:
1031 return BS_LEFT
| BS_VCENTER
;
1035 static SIZE
BUTTON_GetImageSize(const BUTTON_INFO
*infoPtr
)
1041 /* ImageList has priority over image */
1042 if (infoPtr
->imagelist
.himl
)
1043 ImageList_GetIconSize(infoPtr
->imagelist
.himl
, &size
.cx
, &size
.cy
);
1044 else if (infoPtr
->u
.image
)
1046 if (infoPtr
->image_type
== IMAGE_ICON
)
1048 GetIconInfo(infoPtr
->u
.icon
, &iconInfo
);
1049 GetObjectW(iconInfo
.hbmColor
, sizeof(bm
), &bm
);
1050 DeleteObject(iconInfo
.hbmColor
);
1051 DeleteObject(iconInfo
.hbmMask
);
1053 else if (infoPtr
->image_type
== IMAGE_BITMAP
)
1054 GetObjectW(infoPtr
->u
.bitmap
, sizeof(bm
), &bm
);
1056 size
.cx
= bm
.bmWidth
;
1057 size
.cy
= bm
.bmHeight
;
1063 static const RECT
*BUTTON_GetTextMargin(const BUTTON_INFO
*infoPtr
)
1065 static const RECT oneMargin
= {1, 1, 1, 1};
1067 /* Use text margin only when showing both image and text, and image is not imagelist */
1068 if (show_image_and_text(infoPtr
) && !infoPtr
->imagelist
.himl
)
1069 return &infoPtr
->text_margin
;
1074 static void BUTTON_GetClientRectSize(BUTTON_INFO
*infoPtr
, SIZE
*size
)
1077 GetClientRect(infoPtr
->hwnd
, &rect
);
1078 size
->cx
= rect
.right
- rect
.left
;
1079 size
->cy
= rect
.bottom
- rect
.top
;
1082 static void BUTTON_GetTextIdealSize(BUTTON_INFO
*infoPtr
, LONG maxWidth
, SIZE
*size
)
1084 WCHAR
*text
= get_button_text(infoPtr
);
1087 const RECT
*margin
= BUTTON_GetTextMargin(infoPtr
);
1091 maxWidth
-= margin
->right
+ margin
->right
;
1092 if (maxWidth
<= 0) maxWidth
= 1;
1095 hdc
= GetDC(infoPtr
->hwnd
);
1096 rect
= BUTTON_GetTextRect(infoPtr
, hdc
, text
, maxWidth
);
1097 ReleaseDC(infoPtr
->hwnd
, hdc
);
1100 size
->cx
= rect
.right
- rect
.left
+ margin
->left
+ margin
->right
;
1101 size
->cy
= rect
.bottom
- rect
.top
+ margin
->top
+ margin
->bottom
;
1104 static void BUTTON_GetLabelIdealSize(BUTTON_INFO
*infoPtr
, LONG maxWidth
, SIZE
*size
)
1106 LONG style
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
1111 imageSize
= BUTTON_GetImageSize(infoPtr
);
1112 if (infoPtr
->imagelist
.himl
)
1114 imageSize
.cx
+= infoPtr
->imagelist
.margin
.left
+ infoPtr
->imagelist
.margin
.right
;
1115 imageSize
.cy
+= infoPtr
->imagelist
.margin
.top
+ infoPtr
->imagelist
.margin
.bottom
;
1116 if (infoPtr
->imagelist
.uAlign
== BUTTON_IMAGELIST_ALIGN_TOP
1117 || infoPtr
->imagelist
.uAlign
== BUTTON_IMAGELIST_ALIGN_BOTTOM
)
1124 /* horizontal alignment flags has priority over vertical ones if both are specified */
1125 if (!(style
& (BS_CENTER
| BS_VCENTER
)) || ((style
& BS_CENTER
) && (style
& BS_CENTER
) != BS_CENTER
)
1126 || !(style
& BS_VCENTER
) || (style
& BS_VCENTER
) == BS_VCENTER
)
1136 maxWidth
-= imageSize
.cx
;
1137 if (maxWidth
<= 0) maxWidth
= 1;
1139 BUTTON_GetTextIdealSize(infoPtr
, maxWidth
, &textSize
);
1140 size
->cx
= textSize
.cx
+ imageSize
.cx
;
1141 size
->cy
= max(textSize
.cy
, imageSize
.cy
);
1145 BUTTON_GetTextIdealSize(infoPtr
, maxWidth
, &textSize
);
1146 size
->cx
= max(textSize
.cx
, imageSize
.cx
);
1147 size
->cy
= textSize
.cy
+ imageSize
.cy
;
1151 static BOOL
GB_GetIdealSize(BUTTON_INFO
*infoPtr
, SIZE
*size
)
1153 BUTTON_GetClientRectSize(infoPtr
, size
);
1157 static BOOL
CB_GetIdealSize(BUTTON_INFO
*infoPtr
, SIZE
*size
)
1159 LONG style
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
1160 WCHAR
*text
= get_button_text(infoPtr
);
1168 LONG checkboxWidth
, checkboxHeight
;
1171 if (text
) textLength
= lstrlenW(text
);
1173 if (textLength
== 0)
1175 BUTTON_GetClientRectSize(infoPtr
, size
);
1179 hdc
= GetDC(infoPtr
->hwnd
);
1180 scaleX
= GetDeviceCaps(hdc
, LOGPIXELSX
) / 96.0;
1181 scaleY
= GetDeviceCaps(hdc
, LOGPIXELSY
) / 96.0;
1182 if ((hfont
= infoPtr
->font
)) SelectObject(hdc
, hfont
);
1183 GetCharWidthW(hdc
, '0', '0', &textOffset
);
1185 ReleaseDC(infoPtr
->hwnd
, hdc
);
1187 checkboxWidth
= 12 * scaleX
+ 1;
1188 checkboxHeight
= 12 * scaleY
+ 1;
1191 maxWidth
= size
->cx
- checkboxWidth
- textOffset
;
1192 if (maxWidth
<= 0) maxWidth
= 1;
1195 /* Checkbox doesn't support both image(but not image list) and text */
1196 if (!(style
& (BS_ICON
| BS_BITMAP
)) && infoPtr
->u
.image
)
1197 BUTTON_GetTextIdealSize(infoPtr
, maxWidth
, &labelSize
);
1199 BUTTON_GetLabelIdealSize(infoPtr
, maxWidth
, &labelSize
);
1201 size
->cx
= labelSize
.cx
+ checkboxWidth
+ textOffset
;
1202 size
->cy
= max(labelSize
.cy
, checkboxHeight
);
1207 static BOOL
PB_GetIdealSize(BUTTON_INFO
*infoPtr
, SIZE
*size
)
1209 WCHAR
*text
= get_button_text(infoPtr
);
1213 if (text
) textLength
= lstrlenW(text
);
1215 if (textLength
== 0)
1217 BUTTON_GetClientRectSize(infoPtr
, size
);
1223 /* Ideal size include text size even if image only flags(BS_ICON, BS_BITMAP) are specified */
1224 BUTTON_GetLabelIdealSize(infoPtr
, size
->cx
, &labelSize
);
1226 size
->cx
= labelSize
.cx
;
1227 size
->cy
= labelSize
.cy
;
1232 /**********************************************************************
1233 * BUTTON_CalcLayoutRects
1235 * Calculates the rectangles of the button label(image and text) and its parts depending on a button's style.
1237 * Returns flags to be passed to DrawText.
1238 * Calculated rectangle doesn't take into account button state
1239 * (pushed, etc.). If there is nothing to draw (no text/image) output
1240 * rectangle is empty, and return value is (UINT)-1.
1243 * infoPtr [I] Button pointer
1244 * hdc [I] Handle to device context to draw to
1245 * labelRc [I/O] Input the rect the label to be positioned in, and output the label rect
1246 * imageRc [O] Optional, output the image rect
1247 * textRc [O] Optional, output the text rect
1249 static UINT
BUTTON_CalcLayoutRects(const BUTTON_INFO
*infoPtr
, HDC hdc
, RECT
*labelRc
, RECT
*imageRc
, RECT
*textRc
)
1251 LONG style
= GetWindowLongW( infoPtr
->hwnd
, GWL_STYLE
);
1252 LONG ex_style
= GetWindowLongW( infoPtr
->hwnd
, GWL_EXSTYLE
);
1253 LONG split_style
= infoPtr
->imagelist
.himl
? BUTTON_ILStoBS(infoPtr
->imagelist
.uAlign
) : style
;
1254 WCHAR
*text
= get_button_text(infoPtr
);
1255 SIZE imageSize
= BUTTON_GetImageSize(infoPtr
);
1256 UINT dtStyle
= BUTTON_BStoDT(style
, ex_style
);
1257 RECT labelRect
, imageRect
, imageRectWithMargin
, textRect
;
1258 LONG imageMarginWidth
, imageMarginHeight
;
1259 const RECT
*textMargin
= BUTTON_GetTextMargin(infoPtr
);
1260 RECT emptyMargin
= {0};
1263 /* Calculate label rectangle according to label type */
1264 if ((imageSize
.cx
== 0 && imageSize
.cy
== 0) && (text
== NULL
|| text
[0] == '\0'))
1266 SetRectEmpty(labelRc
);
1267 SetRectEmpty(imageRc
);
1268 SetRectEmpty(textRc
);
1273 SetRect(&imageRect
, 0, 0, imageSize
.cx
, imageSize
.cy
);
1274 imageRectWithMargin
= imageRect
;
1275 if (infoPtr
->imagelist
.himl
)
1277 imageRectWithMargin
.top
-= infoPtr
->imagelist
.margin
.top
;
1278 imageRectWithMargin
.bottom
+= infoPtr
->imagelist
.margin
.bottom
;
1279 imageRectWithMargin
.left
-= infoPtr
->imagelist
.margin
.left
;
1280 imageRectWithMargin
.right
+= infoPtr
->imagelist
.margin
.right
;
1283 /* Show image only */
1284 if (show_image_only(infoPtr
))
1286 BUTTON_PositionRect(style
, labelRc
, &imageRect
,
1287 infoPtr
->imagelist
.himl
? &infoPtr
->imagelist
.margin
: &emptyMargin
);
1288 labelRect
= imageRect
;
1289 SetRectEmpty(&textRect
);
1294 maxTextWidth
= labelRc
->right
- labelRc
->left
;
1295 textRect
= BUTTON_GetTextRect(infoPtr
, hdc
, text
, maxTextWidth
);
1298 /* Show image and text */
1299 if (show_image_and_text(infoPtr
))
1301 RECT boundingLabelRect
, boundingImageRect
, boundingTextRect
;
1303 /* Get label rect */
1304 /* Image list may have different alignment than the button, use the whole rect for label in this case */
1305 if (infoPtr
->imagelist
.himl
)
1306 labelRect
= *labelRc
;
1309 /* Get a label bounding rectangle to position the label in the user specified label rectangle because
1310 * text and image need to align together. */
1311 boundingLabelRect
= BUTTON_GetBoundingLabelRect(split_style
, &textRect
, &imageRectWithMargin
);
1312 BUTTON_PositionRect(split_style
, labelRc
, &boundingLabelRect
, &emptyMargin
);
1313 labelRect
= boundingLabelRect
;
1316 /* When imagelist has center align, use the whole rect for imagelist and text */
1317 if(infoPtr
->imagelist
.himl
&& infoPtr
->imagelist
.uAlign
== BUTTON_IMAGELIST_ALIGN_CENTER
)
1319 boundingImageRect
= labelRect
;
1320 boundingTextRect
= labelRect
;
1321 BUTTON_PositionRect(split_style
, &boundingImageRect
, &imageRect
,
1322 infoPtr
->imagelist
.himl
? &infoPtr
->imagelist
.margin
: &emptyMargin
);
1323 /* Text doesn't use imagelist align */
1324 BUTTON_PositionRect(style
, &boundingTextRect
, &textRect
, textMargin
);
1328 /* Get image rect */
1329 /* Split the label rect to two halves as two bounding rectangles for image and text */
1330 boundingImageRect
= labelRect
;
1331 imageMarginWidth
= imageRectWithMargin
.right
- imageRectWithMargin
.left
;
1332 imageMarginHeight
= imageRectWithMargin
.bottom
- imageRectWithMargin
.top
;
1333 if ((split_style
& BS_CENTER
) == BS_RIGHT
)
1334 boundingImageRect
.left
= boundingImageRect
.right
- imageMarginWidth
;
1335 else if ((split_style
& BS_CENTER
) == BS_LEFT
)
1336 boundingImageRect
.right
= boundingImageRect
.left
+ imageMarginWidth
;
1337 else if ((split_style
& BS_VCENTER
) == BS_BOTTOM
)
1338 boundingImageRect
.top
= boundingImageRect
.bottom
- imageMarginHeight
;
1339 else if ((split_style
& BS_VCENTER
) == BS_TOP
)
1340 boundingImageRect
.bottom
= boundingImageRect
.top
+ imageMarginHeight
;
1342 boundingImageRect
.right
= boundingImageRect
.left
+ imageMarginWidth
;
1343 BUTTON_PositionRect(split_style
, &boundingImageRect
, &imageRect
,
1344 infoPtr
->imagelist
.himl
? &infoPtr
->imagelist
.margin
: &emptyMargin
);
1347 SubtractRect(&boundingTextRect
, &labelRect
, &boundingImageRect
);
1348 /* Text doesn't use imagelist align */
1349 BUTTON_PositionRect(style
, &boundingTextRect
, &textRect
, textMargin
);
1352 /* Show text only */
1355 if (get_button_type(style
) != BS_GROUPBOX
)
1356 BUTTON_PositionRect(style
, labelRc
, &textRect
, textMargin
);
1358 /* GroupBox is always top aligned */
1359 BUTTON_PositionRect((style
& ~BS_VCENTER
) | BS_TOP
, labelRc
, &textRect
, textMargin
);
1360 labelRect
= textRect
;
1361 SetRectEmpty(&imageRect
);
1365 CopyRect(labelRc
, &labelRect
);
1366 CopyRect(imageRc
, &imageRect
);
1367 CopyRect(textRc
, &textRect
);
1373 /**********************************************************************
1374 * BUTTON_DrawTextCallback
1376 * Callback function used by DrawStateW function.
1378 static BOOL CALLBACK
BUTTON_DrawTextCallback(HDC hdc
, LPARAM lp
, WPARAM wp
, int cx
, int cy
)
1382 SetRect(&rc
, 0, 0, cx
, cy
);
1383 DrawTextW(hdc
, (LPCWSTR
)lp
, -1, &rc
, (UINT
)wp
);
1387 /**********************************************************************
1390 * Common function for drawing button label.
1393 * 1. When BS_SINGLELINE is specified and text contains '\t', '\n' or '\r' in the middle, they are rendered as
1394 * squares now whereas they should be ignored.
1395 * 2. When BS_MULTILINE is specified and text contains space in the middle, the space mistakenly be rendered as newline.
1397 static void BUTTON_DrawLabel(const BUTTON_INFO
*infoPtr
, HDC hdc
, UINT dtFlags
, const RECT
*imageRect
,
1398 const RECT
*textRect
)
1400 DRAWSTATEPROC lpOutputProc
= NULL
;
1404 UINT flags
= IsWindowEnabled(infoPtr
->hwnd
) ? DSS_NORMAL
: DSS_DISABLED
;
1406 LONG state
= infoPtr
->state
;
1408 LONG style
= GetWindowLongW( infoPtr
->hwnd
, GWL_STYLE
);
1411 /* FIXME: To draw disabled label in Win31 look-and-feel, we probably
1412 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
1413 * I don't have Win31 on hand to verify that, so I leave it as is.
1416 if ((style
& BS_PUSHLIKE
) && (state
& BST_INDETERMINATE
))
1418 hbr
= GetSysColorBrush(COLOR_GRAYTEXT
);
1422 if (show_image(infoPtr
))
1424 if (infoPtr
->imagelist
.himl
)
1426 if (ImageList_GetImageCount(infoPtr
->imagelist
.himl
) == 1)
1427 ImageList_Draw(infoPtr
->imagelist
.himl
, 0, hdc
, imageRect
->left
, imageRect
->top
, ILD_NORMAL
);
1430 draw_state
= get_draw_state(infoPtr
);
1431 ImageList_Draw(infoPtr
->imagelist
.himl
, draw_state
- 1, hdc
, imageRect
->left
, imageRect
->top
,
1437 switch (infoPtr
->image_type
)
1440 imageFlags
= flags
| DST_ICON
;
1441 lp
= (LPARAM
)infoPtr
->u
.icon
;
1444 imageFlags
= flags
| DST_BITMAP
;
1445 lp
= (LPARAM
)infoPtr
->u
.bitmap
;
1451 DrawStateW(hdc
, hbr
, lpOutputProc
, lp
, wp
, imageRect
->left
, imageRect
->top
,
1452 imageRect
->right
- imageRect
->left
, imageRect
->bottom
- imageRect
->top
, imageFlags
);
1456 if (show_image_only(infoPtr
)) return;
1458 /* DST_COMPLEX -- is 0 */
1459 lpOutputProc
= BUTTON_DrawTextCallback
;
1460 if (!(text
= get_button_text(infoPtr
))) return;
1463 DrawStateW(hdc
, hbr
, lpOutputProc
, lp
, wp
, textRect
->left
, textRect
->top
, textRect
->right
- textRect
->left
,
1464 textRect
->bottom
- textRect
->top
, flags
);
1468 /**********************************************************************
1469 * Push Button Functions
1471 static void PB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
)
1473 RECT rc
, labelRect
, imageRect
, textRect
;
1474 UINT dtFlags
, uState
;
1478 COLORREF oldTxtColor
;
1480 LONG state
= infoPtr
->state
;
1481 LONG style
= GetWindowLongW( infoPtr
->hwnd
, GWL_STYLE
);
1482 BOOL pushedState
= (state
& BST_PUSHED
);
1486 GetClientRect( infoPtr
->hwnd
, &rc
);
1488 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
1489 if ((hFont
= infoPtr
->font
)) SelectObject( hDC
, hFont
);
1490 parent
= GetParent(infoPtr
->hwnd
);
1491 if (!parent
) parent
= infoPtr
->hwnd
;
1492 SendMessageW( parent
, WM_CTLCOLORBTN
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1494 hrgn
= set_control_clipping( hDC
, &rc
);
1496 hpen
= CreatePen( PS_SOLID
, 1, GetSysColor(COLOR_WINDOWFRAME
));
1497 hOldPen
= SelectObject(hDC
, hpen
);
1498 hOldBrush
= SelectObject(hDC
,GetSysColorBrush(COLOR_BTNFACE
));
1499 oldBkMode
= SetBkMode(hDC
, TRANSPARENT
);
1501 if (get_button_type(style
) == BS_DEFPUSHBUTTON
)
1503 if (action
!= ODA_FOCUS
)
1504 Rectangle(hDC
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1505 InflateRect( &rc
, -1, -1 );
1508 /* completely skip the drawing if only focus has changed */
1509 if (action
== ODA_FOCUS
) goto draw_focus
;
1511 uState
= DFCS_BUTTONPUSH
;
1513 if (style
& BS_FLAT
)
1514 uState
|= DFCS_MONO
;
1515 else if (pushedState
)
1517 if (get_button_type(style
) == BS_DEFPUSHBUTTON
)
1518 uState
|= DFCS_FLAT
;
1520 uState
|= DFCS_PUSHED
;
1523 if (state
& (BST_CHECKED
| BST_INDETERMINATE
))
1524 uState
|= DFCS_CHECKED
;
1526 DrawFrameControl( hDC
, &rc
, DFC_BUTTON
, uState
);
1528 /* draw button label */
1530 /* Shrink label rect at all sides by 2 so that the content won't touch the surrounding frame */
1531 InflateRect(&labelRect
, -2, -2);
1532 dtFlags
= BUTTON_CalcLayoutRects(infoPtr
, hDC
, &labelRect
, &imageRect
, &textRect
);
1534 if (dtFlags
== (UINT
)-1L)
1537 if (pushedState
) OffsetRect(&labelRect
, 1, 1);
1539 oldTxtColor
= SetTextColor( hDC
, GetSysColor(COLOR_BTNTEXT
) );
1541 BUTTON_DrawLabel(infoPtr
, hDC
, dtFlags
, &imageRect
, &textRect
);
1543 SetTextColor( hDC
, oldTxtColor
);
1546 if (action
== ODA_FOCUS
|| (state
& BST_FOCUS
))
1548 InflateRect( &rc
, -2, -2 );
1549 DrawFocusRect( hDC
, &rc
);
1553 SelectObject( hDC
, hOldPen
);
1554 SelectObject( hDC
, hOldBrush
);
1555 SetBkMode(hDC
, oldBkMode
);
1556 SelectClipRgn( hDC
, hrgn
);
1557 if (hrgn
) DeleteObject( hrgn
);
1558 DeleteObject( hpen
);
1561 /**********************************************************************
1562 * Check Box & Radio Button Functions
1565 static void CB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
)
1567 RECT rbox
, labelRect
, imageRect
, textRect
, client
;
1569 int delta
, text_offset
, checkBoxWidth
, checkBoxHeight
;
1572 LONG state
= infoPtr
->state
;
1573 LONG style
= GetWindowLongW( infoPtr
->hwnd
, GWL_STYLE
);
1574 LONG ex_style
= GetWindowLongW( infoPtr
->hwnd
, GWL_EXSTYLE
);
1578 if (style
& BS_PUSHLIKE
)
1580 PB_Paint( infoPtr
, hDC
, action
);
1584 GetClientRect(infoPtr
->hwnd
, &client
);
1585 rbox
= labelRect
= client
;
1587 checkBoxWidth
= 12 * GetDpiForWindow( infoPtr
->hwnd
) / 96 + 1;
1588 checkBoxHeight
= 12 * GetDpiForWindow( infoPtr
->hwnd
) / 96 + 1;
1590 if ((hFont
= infoPtr
->font
)) SelectObject( hDC
, hFont
);
1591 GetCharWidthW( hDC
, '0', '0', &text_offset
);
1594 parent
= GetParent(infoPtr
->hwnd
);
1595 if (!parent
) parent
= infoPtr
->hwnd
;
1596 hBrush
= (HBRUSH
)SendMessageW(parent
, WM_CTLCOLORSTATIC
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1597 if (!hBrush
) /* did the app forget to call defwindowproc ? */
1598 hBrush
= (HBRUSH
)DefWindowProcW(parent
, WM_CTLCOLORSTATIC
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1599 hrgn
= set_control_clipping( hDC
, &client
);
1601 if (style
& BS_LEFTTEXT
|| ex_style
& WS_EX_RIGHT
)
1603 labelRect
.right
-= checkBoxWidth
+ text_offset
;
1604 rbox
.left
= rbox
.right
- checkBoxWidth
;
1608 labelRect
.left
+= checkBoxWidth
+ text_offset
;
1609 rbox
.right
= checkBoxWidth
;
1612 /* Since WM_ERASEBKGND does nothing, first prepare background */
1613 if (action
== ODA_SELECT
) FillRect( hDC
, &rbox
, hBrush
);
1614 if (action
== ODA_DRAWENTIRE
) FillRect( hDC
, &client
, hBrush
);
1618 dtFlags
= BUTTON_CalcLayoutRects(infoPtr
, hDC
, &labelRect
, &imageRect
, &textRect
);
1620 /* Only adjust rbox when rtext is valid */
1621 if (dtFlags
!= (UINT
)-1L)
1623 rbox
.top
= labelRect
.top
;
1624 rbox
.bottom
= labelRect
.bottom
;
1627 /* Draw the check-box bitmap */
1628 if (action
== ODA_DRAWENTIRE
|| action
== ODA_SELECT
)
1632 if ((get_button_type(style
) == BS_RADIOBUTTON
) ||
1633 (get_button_type(style
) == BS_AUTORADIOBUTTON
)) flags
= DFCS_BUTTONRADIO
;
1634 else if (state
& BST_INDETERMINATE
) flags
= DFCS_BUTTON3STATE
;
1635 else flags
= DFCS_BUTTONCHECK
;
1637 if (state
& (BST_CHECKED
| BST_INDETERMINATE
)) flags
|= DFCS_CHECKED
;
1638 if (state
& BST_PUSHED
) flags
|= DFCS_PUSHED
;
1640 if (style
& WS_DISABLED
) flags
|= DFCS_INACTIVE
;
1642 /* rbox must have the correct height */
1643 delta
= rbox
.bottom
- rbox
.top
- checkBoxHeight
;
1645 if ((style
& BS_VCENTER
) == BS_TOP
) {
1647 rbox
.bottom
= rbox
.top
+ checkBoxHeight
;
1649 rbox
.top
-= -delta
/2 + 1;
1650 rbox
.bottom
= rbox
.top
+ checkBoxHeight
;
1652 } else if ((style
& BS_VCENTER
) == BS_BOTTOM
) {
1654 rbox
.top
= rbox
.bottom
- checkBoxHeight
;
1656 rbox
.bottom
+= -delta
/2 + 1;
1657 rbox
.top
= rbox
.bottom
- checkBoxHeight
;
1659 } else { /* Default */
1661 int ofs
= (delta
/ 2);
1662 rbox
.bottom
-= ofs
+ 1;
1663 rbox
.top
= rbox
.bottom
- checkBoxHeight
;
1664 } else if (delta
< 0) {
1665 int ofs
= (-delta
/ 2);
1666 rbox
.top
-= ofs
+ 1;
1667 rbox
.bottom
= rbox
.top
+ checkBoxHeight
;
1671 DrawFrameControl( hDC
, &rbox
, DFC_BUTTON
, flags
);
1674 if (dtFlags
== (UINT
)-1L) /* Noting to draw */
1677 if (action
== ODA_DRAWENTIRE
) BUTTON_DrawLabel(infoPtr
, hDC
, dtFlags
, &imageRect
, &textRect
);
1680 if (action
== ODA_FOCUS
|| (state
& BST_FOCUS
))
1684 IntersectRect(&labelRect
, &labelRect
, &client
);
1685 DrawFocusRect(hDC
, &labelRect
);
1687 SelectClipRgn( hDC
, hrgn
);
1688 if (hrgn
) DeleteObject( hrgn
);
1692 /**********************************************************************
1693 * BUTTON_CheckAutoRadioButton
1695 * hwnd is checked, uncheck every other auto radio button in group
1697 static void BUTTON_CheckAutoRadioButton( HWND hwnd
)
1699 HWND parent
, sibling
, start
;
1701 parent
= GetParent(hwnd
);
1702 /* make sure that starting control is not disabled or invisible */
1703 start
= sibling
= GetNextDlgGroupItem( parent
, hwnd
, TRUE
);
1706 if (!sibling
) break;
1707 if ((hwnd
!= sibling
) &&
1708 ((GetWindowLongW( sibling
, GWL_STYLE
) & BS_TYPEMASK
) == BS_AUTORADIOBUTTON
))
1709 SendMessageW( sibling
, BM_SETCHECK
, BST_UNCHECKED
, 0 );
1710 sibling
= GetNextDlgGroupItem( parent
, sibling
, FALSE
);
1711 } while (sibling
!= start
);
1715 /**********************************************************************
1716 * Group Box Functions
1719 static void GB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
)
1721 RECT labelRect
, imageRect
, textRect
, rcFrame
;
1726 LONG style
= GetWindowLongW( infoPtr
->hwnd
, GWL_STYLE
);
1730 if ((hFont
= infoPtr
->font
)) SelectObject( hDC
, hFont
);
1731 /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
1732 parent
= GetParent(infoPtr
->hwnd
);
1733 if (!parent
) parent
= infoPtr
->hwnd
;
1734 hbr
= (HBRUSH
)SendMessageW(parent
, WM_CTLCOLORSTATIC
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1735 if (!hbr
) /* did the app forget to call defwindowproc ? */
1736 hbr
= (HBRUSH
)DefWindowProcW(parent
, WM_CTLCOLORSTATIC
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1737 GetClientRect(infoPtr
->hwnd
, &labelRect
);
1738 rcFrame
= labelRect
;
1739 hrgn
= set_control_clipping(hDC
, &labelRect
);
1741 GetTextMetricsW (hDC
, &tm
);
1742 rcFrame
.top
+= (tm
.tmHeight
/ 2) - 1;
1743 DrawEdge (hDC
, &rcFrame
, EDGE_ETCHED
, BF_RECT
| ((style
& BS_FLAT
) ? BF_FLAT
: 0));
1745 InflateRect(&labelRect
, -7, 1);
1746 dtFlags
= BUTTON_CalcLayoutRects(infoPtr
, hDC
, &labelRect
, &imageRect
, &textRect
);
1748 if (dtFlags
!= (UINT
)-1)
1750 /* Because buttons have CS_PARENTDC class style, there is a chance
1751 * that label will be drawn out of client rect.
1752 * But Windows doesn't clip label's rect, so do I.
1755 /* There is 1-pixel margin at the left, right, and bottom */
1759 FillRect(hDC
, &labelRect
, hbr
);
1764 BUTTON_DrawLabel(infoPtr
, hDC
, dtFlags
, &imageRect
, &textRect
);
1766 SelectClipRgn( hDC
, hrgn
);
1767 if (hrgn
) DeleteObject( hrgn
);
1771 /**********************************************************************
1772 * User Button Functions
1775 static void UB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
)
1780 LONG state
= infoPtr
->state
;
1783 GetClientRect( infoPtr
->hwnd
, &rc
);
1785 if ((hFont
= infoPtr
->font
)) SelectObject( hDC
, hFont
);
1787 parent
= GetParent(infoPtr
->hwnd
);
1788 if (!parent
) parent
= infoPtr
->hwnd
;
1789 hBrush
= (HBRUSH
)SendMessageW(parent
, WM_CTLCOLORBTN
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1790 if (!hBrush
) /* did the app forget to call defwindowproc ? */
1791 hBrush
= (HBRUSH
)DefWindowProcW(parent
, WM_CTLCOLORBTN
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1793 FillRect( hDC
, &rc
, hBrush
);
1794 if (action
== ODA_FOCUS
|| (state
& BST_FOCUS
))
1795 DrawFocusRect( hDC
, &rc
);
1800 BUTTON_NOTIFY_PARENT( infoPtr
->hwnd
, (state
& BST_FOCUS
) ? BN_SETFOCUS
: BN_KILLFOCUS
);
1804 BUTTON_NOTIFY_PARENT( infoPtr
->hwnd
, (state
& BST_PUSHED
) ? BN_HILITE
: BN_UNHILITE
);
1813 /**********************************************************************
1814 * Ownerdrawn Button Functions
1817 static void OB_Paint( const BUTTON_INFO
*infoPtr
, HDC hDC
, UINT action
)
1819 LONG state
= infoPtr
->state
;
1821 LONG_PTR id
= GetWindowLongPtrW( infoPtr
->hwnd
, GWLP_ID
);
1826 dis
.CtlType
= ODT_BUTTON
;
1829 dis
.itemAction
= action
;
1830 dis
.itemState
= ((state
& BST_FOCUS
) ? ODS_FOCUS
: 0) |
1831 ((state
& BST_PUSHED
) ? ODS_SELECTED
: 0) |
1832 (IsWindowEnabled(infoPtr
->hwnd
) ? 0: ODS_DISABLED
);
1833 dis
.hwndItem
= infoPtr
->hwnd
;
1836 GetClientRect( infoPtr
->hwnd
, &dis
.rcItem
);
1838 if ((hFont
= infoPtr
->font
)) SelectObject( hDC
, hFont
);
1839 parent
= GetParent(infoPtr
->hwnd
);
1840 if (!parent
) parent
= infoPtr
->hwnd
;
1841 SendMessageW( parent
, WM_CTLCOLORBTN
, (WPARAM
)hDC
, (LPARAM
)infoPtr
->hwnd
);
1843 hrgn
= set_control_clipping( hDC
, &dis
.rcItem
);
1845 SendMessageW( GetParent(infoPtr
->hwnd
), WM_DRAWITEM
, id
, (LPARAM
)&dis
);
1846 SelectClipRgn( hDC
, hrgn
);
1847 if (hrgn
) DeleteObject( hrgn
);
1850 static void PB_ThemedPaint(HTHEME theme
, const BUTTON_INFO
*infoPtr
, HDC hDC
, int state
, UINT dtFlags
, BOOL focused
)
1852 RECT bgRect
, textRect
;
1853 HFONT font
= infoPtr
->font
;
1854 HFONT hPrevFont
= font
? SelectObject(hDC
, font
) : NULL
;
1855 WCHAR
*text
= get_button_text(infoPtr
);
1857 GetClientRect(infoPtr
->hwnd
, &bgRect
);
1858 GetThemeBackgroundContentRect(theme
, hDC
, BP_PUSHBUTTON
, state
, &bgRect
, &textRect
);
1860 if (IsThemeBackgroundPartiallyTransparent(theme
, BP_PUSHBUTTON
, state
))
1861 DrawThemeParentBackground(infoPtr
->hwnd
, hDC
, NULL
);
1862 DrawThemeBackground(theme
, hDC
, BP_PUSHBUTTON
, state
, &bgRect
, NULL
);
1865 DrawThemeText(theme
, hDC
, BP_PUSHBUTTON
, state
, text
, lstrlenW(text
), dtFlags
, 0, &textRect
);
1872 RECT focusRect
= bgRect
;
1874 GetThemeMargins(theme
, hDC
, BP_PUSHBUTTON
, state
, TMT_CONTENTMARGINS
, NULL
, &margins
);
1876 focusRect
.left
+= margins
.cxLeftWidth
;
1877 focusRect
.top
+= margins
.cyTopHeight
;
1878 focusRect
.right
-= margins
.cxRightWidth
;
1879 focusRect
.bottom
-= margins
.cyBottomHeight
;
1881 DrawFocusRect( hDC
, &focusRect
);
1884 if (hPrevFont
) SelectObject(hDC
, hPrevFont
);
1887 static void CB_ThemedPaint(HTHEME theme
, const BUTTON_INFO
*infoPtr
, HDC hDC
, int state
, UINT dtFlags
, BOOL focused
)
1890 RECT bgRect
, textRect
;
1891 HFONT font
, hPrevFont
= NULL
;
1892 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwnd
, GWL_STYLE
);
1893 UINT btn_type
= get_button_type( dwStyle
);
1894 int part
= (btn_type
== BS_RADIOBUTTON
) || (btn_type
== BS_AUTORADIOBUTTON
) ? BP_RADIOBUTTON
: BP_CHECKBOX
;
1895 WCHAR
*text
= get_button_text(infoPtr
);
1897 BOOL created_font
= FALSE
;
1899 HRESULT hr
= GetThemeFont(theme
, hDC
, part
, state
, TMT_FONT
, &lf
);
1900 if (SUCCEEDED(hr
)) {
1901 font
= CreateFontIndirectW(&lf
);
1903 TRACE("Failed to create font\n");
1905 TRACE("font = %s\n", debugstr_w(lf
.lfFaceName
));
1906 hPrevFont
= SelectObject(hDC
, font
);
1907 created_font
= TRUE
;
1910 font
= (HFONT
)SendMessageW(infoPtr
->hwnd
, WM_GETFONT
, 0, 0);
1911 hPrevFont
= SelectObject(hDC
, font
);
1914 if (FAILED(GetThemePartSize(theme
, hDC
, part
, state
, NULL
, TS_DRAW
, &sz
)))
1917 GetClientRect(infoPtr
->hwnd
, &bgRect
);
1918 GetThemeBackgroundContentRect(theme
, hDC
, part
, state
, &bgRect
, &textRect
);
1920 if (dtFlags
& DT_SINGLELINE
) /* Center the checkbox / radio button to the text. */
1921 bgRect
.top
= bgRect
.top
+ (textRect
.bottom
- textRect
.top
- sz
.cy
) / 2;
1923 /* adjust for the check/radio marker */
1924 bgRect
.bottom
= bgRect
.top
+ sz
.cy
;
1925 bgRect
.right
= bgRect
.left
+ sz
.cx
;
1926 textRect
.left
= bgRect
.right
+ 6;
1928 DrawThemeParentBackground(infoPtr
->hwnd
, hDC
, NULL
);
1930 DrawThemeBackground(theme
, hDC
, part
, state
, &bgRect
, NULL
);
1933 DrawThemeText(theme
, hDC
, part
, state
, text
, lstrlenW(text
), dtFlags
, 0, &textRect
);
1939 focusRect
= textRect
;
1941 DrawTextW(hDC
, text
, lstrlenW(text
), &focusRect
, dtFlags
| DT_CALCRECT
);
1943 if (focusRect
.right
< textRect
.right
) focusRect
.right
++;
1944 focusRect
.bottom
= textRect
.bottom
;
1946 DrawFocusRect( hDC
, &focusRect
);
1952 if (created_font
) DeleteObject(font
);
1953 if (hPrevFont
) SelectObject(hDC
, hPrevFont
);
1956 static void GB_ThemedPaint(HTHEME theme
, const BUTTON_INFO
*infoPtr
, HDC hDC
, int state
, UINT dtFlags
, BOOL focused
)
1958 RECT bgRect
, textRect
, contentRect
;
1959 WCHAR
*text
= get_button_text(infoPtr
);
1961 HFONT font
, hPrevFont
= NULL
;
1962 BOOL created_font
= FALSE
;
1964 HRESULT hr
= GetThemeFont(theme
, hDC
, BP_GROUPBOX
, state
, TMT_FONT
, &lf
);
1965 if (SUCCEEDED(hr
)) {
1966 font
= CreateFontIndirectW(&lf
);
1968 TRACE("Failed to create font\n");
1970 hPrevFont
= SelectObject(hDC
, font
);
1971 created_font
= TRUE
;
1974 font
= (HFONT
)SendMessageW(infoPtr
->hwnd
, WM_GETFONT
, 0, 0);
1975 hPrevFont
= SelectObject(hDC
, font
);
1978 GetClientRect(infoPtr
->hwnd
, &bgRect
);
1984 GetTextExtentPoint32W(hDC
, text
, lstrlenW(text
), &textExtent
);
1985 bgRect
.top
+= (textExtent
.cy
/ 2);
1986 textRect
.left
+= 10;
1987 textRect
.bottom
= textRect
.top
+ textExtent
.cy
;
1988 textRect
.right
= textRect
.left
+ textExtent
.cx
+ 4;
1990 ExcludeClipRect(hDC
, textRect
.left
, textRect
.top
, textRect
.right
, textRect
.bottom
);
1993 GetThemeBackgroundContentRect(theme
, hDC
, BP_GROUPBOX
, state
, &bgRect
, &contentRect
);
1994 ExcludeClipRect(hDC
, contentRect
.left
, contentRect
.top
, contentRect
.right
, contentRect
.bottom
);
1996 if (IsThemeBackgroundPartiallyTransparent(theme
, BP_GROUPBOX
, state
))
1997 DrawThemeParentBackground(infoPtr
->hwnd
, hDC
, NULL
);
1998 DrawThemeBackground(theme
, hDC
, BP_GROUPBOX
, state
, &bgRect
, NULL
);
2000 SelectClipRgn(hDC
, NULL
);
2004 InflateRect(&textRect
, -2, 0);
2005 DrawThemeText(theme
, hDC
, BP_GROUPBOX
, state
, text
, lstrlenW(text
), 0, 0, &textRect
);
2009 if (created_font
) DeleteObject(font
);
2010 if (hPrevFont
) SelectObject(hDC
, hPrevFont
);
2013 void BUTTON_Register(void)
2017 memset(&wndClass
, 0, sizeof(wndClass
));
2018 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_VREDRAW
| CS_HREDRAW
| CS_PARENTDC
;
2019 wndClass
.lpfnWndProc
= BUTTON_WindowProc
;
2020 wndClass
.cbClsExtra
= 0;
2021 wndClass
.cbWndExtra
= sizeof(BUTTON_INFO
*);
2022 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
2023 wndClass
.hbrBackground
= NULL
;
2024 wndClass
.lpszClassName
= WC_BUTTONW
;
2025 RegisterClassW(&wndClass
);