Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/comctl32/button.c
4388 views
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_NCCREATE: Turns any BS_OWNERDRAW button into a BS_PUSHBUTTON button.
29
* - WM_SYSKEYUP
30
*
31
* Notifications
32
* - BCN_HOTITEMCHANGE
33
* - BN_DISABLE
34
* - BN_PUSHED/BN_HILITE
35
* + BN_KILLFOCUS: is it OK?
36
* - BN_PAINT
37
* + BN_SETFOCUS: is it OK?
38
* - BN_UNPUSHED/BN_UNHILITE
39
*
40
* Structures/Macros/Definitions
41
* - NMBCHOTITEM
42
*/
43
44
#include <stdarg.h>
45
#include <string.h>
46
#include <stdlib.h>
47
48
#define OEMRESOURCE
49
50
#include "windef.h"
51
#include "winbase.h"
52
#include "wingdi.h"
53
#include "winuser.h"
54
#include "uxtheme.h"
55
#include "vssym32.h"
56
#include "wine/debug.h"
57
58
#include "comctl32.h"
59
#include "uiautomationclient.h"
60
61
WINE_DEFAULT_DEBUG_CHANNEL(button);
62
63
/* undocumented flags */
64
#define BUTTON_NSTATES 0x0F
65
#define BUTTON_BTNPRESSED 0x40
66
#define BUTTON_UNKNOWN2 0x20
67
#define BUTTON_UNKNOWN3 0x10
68
69
#define BUTTON_NOTIFY_PARENT(hWnd, code) \
70
do { /* Notify parent which has created this button control */ \
71
TRACE("notification " #code " sent to hwnd=%p\n", GetParent(hWnd)); \
72
SendMessageW(GetParent(hWnd), WM_COMMAND, \
73
MAKEWPARAM(GetWindowLongPtrW((hWnd),GWLP_ID), (code)), \
74
(LPARAM)(hWnd)); \
75
} while(0)
76
77
typedef struct _BUTTON_INFO
78
{
79
HWND hwnd;
80
HWND parent;
81
LONG style;
82
LONG state;
83
HFONT font;
84
WCHAR *note;
85
INT note_length;
86
DWORD image_type; /* IMAGE_BITMAP or IMAGE_ICON */
87
BUTTON_IMAGELIST imagelist;
88
UINT split_style;
89
HIMAGELIST glyph; /* this is a font character code when split_style doesn't have BCSS_IMAGE */
90
SIZE glyph_size;
91
RECT text_margin;
92
HANDLE image; /* Original handle set with BM_SETIMAGE and returned with BM_GETIMAGE. */
93
union
94
{
95
HICON icon;
96
HBITMAP bitmap;
97
HANDLE image; /* Duplicated handle used for drawing. */
98
} u;
99
} BUTTON_INFO;
100
101
static UINT BUTTON_CalcLayoutRects( const BUTTON_INFO *infoPtr, HDC hdc, RECT *labelRc, RECT *imageRc, RECT *textRc );
102
static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
103
static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
104
static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
105
static void UB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
106
static void OB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
107
static void SB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
108
static void CL_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action );
109
static void BUTTON_CheckAutoRadioButton( HWND hwnd );
110
static void get_split_button_rects(const BUTTON_INFO*, const RECT*, RECT*, RECT*);
111
static BOOL notify_split_button_dropdown(const BUTTON_INFO*, const POINT*, HWND);
112
static void draw_split_button_dropdown_glyph(const BUTTON_INFO*, HDC, RECT*);
113
114
#define MAX_BTN_TYPE 16
115
116
static const WORD maxCheckState[MAX_BTN_TYPE] =
117
{
118
BST_UNCHECKED, /* BS_PUSHBUTTON */
119
BST_UNCHECKED, /* BS_DEFPUSHBUTTON */
120
BST_CHECKED, /* BS_CHECKBOX */
121
BST_CHECKED, /* BS_AUTOCHECKBOX */
122
BST_CHECKED, /* BS_RADIOBUTTON */
123
BST_INDETERMINATE, /* BS_3STATE */
124
BST_INDETERMINATE, /* BS_AUTO3STATE */
125
BST_UNCHECKED, /* BS_GROUPBOX */
126
BST_UNCHECKED, /* BS_USERBUTTON */
127
BST_CHECKED, /* BS_AUTORADIOBUTTON */
128
BST_UNCHECKED, /* BS_PUSHBOX */
129
BST_UNCHECKED, /* BS_OWNERDRAW */
130
BST_UNCHECKED, /* BS_SPLITBUTTON */
131
BST_UNCHECKED, /* BS_DEFSPLITBUTTON */
132
BST_UNCHECKED, /* BS_COMMANDLINK */
133
BST_UNCHECKED /* BS_DEFCOMMANDLINK */
134
};
135
136
/* Generic draw states, use get_draw_state() to get specific state for button type */
137
enum draw_state
138
{
139
STATE_NORMAL,
140
STATE_DISABLED,
141
STATE_HOT,
142
STATE_PRESSED,
143
STATE_DEFAULTED,
144
DRAW_STATE_COUNT
145
};
146
147
typedef void (*pfPaint)( const BUTTON_INFO *infoPtr, HDC hdc, UINT action );
148
149
static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
150
{
151
PB_Paint, /* BS_PUSHBUTTON */
152
PB_Paint, /* BS_DEFPUSHBUTTON */
153
CB_Paint, /* BS_CHECKBOX */
154
CB_Paint, /* BS_AUTOCHECKBOX */
155
CB_Paint, /* BS_RADIOBUTTON */
156
CB_Paint, /* BS_3STATE */
157
CB_Paint, /* BS_AUTO3STATE */
158
GB_Paint, /* BS_GROUPBOX */
159
UB_Paint, /* BS_USERBUTTON */
160
CB_Paint, /* BS_AUTORADIOBUTTON */
161
NULL, /* BS_PUSHBOX */
162
OB_Paint, /* BS_OWNERDRAW */
163
SB_Paint, /* BS_SPLITBUTTON */
164
SB_Paint, /* BS_DEFSPLITBUTTON */
165
CL_Paint, /* BS_COMMANDLINK */
166
CL_Paint /* BS_DEFCOMMANDLINK */
167
};
168
169
typedef void (*pfThemedPaint)( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused);
170
171
static void PB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused);
172
static void CB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused);
173
static void GB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused);
174
static void SB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused);
175
static void CL_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused);
176
177
static const pfThemedPaint btnThemedPaintFunc[MAX_BTN_TYPE] =
178
{
179
PB_ThemedPaint, /* BS_PUSHBUTTON */
180
PB_ThemedPaint, /* BS_DEFPUSHBUTTON */
181
CB_ThemedPaint, /* BS_CHECKBOX */
182
CB_ThemedPaint, /* BS_AUTOCHECKBOX */
183
CB_ThemedPaint, /* BS_RADIOBUTTON */
184
CB_ThemedPaint, /* BS_3STATE */
185
CB_ThemedPaint, /* BS_AUTO3STATE */
186
GB_ThemedPaint, /* BS_GROUPBOX */
187
NULL, /* BS_USERBUTTON */
188
CB_ThemedPaint, /* BS_AUTORADIOBUTTON */
189
NULL, /* BS_PUSHBOX */
190
NULL, /* BS_OWNERDRAW */
191
SB_ThemedPaint, /* BS_SPLITBUTTON */
192
SB_ThemedPaint, /* BS_DEFSPLITBUTTON */
193
CL_ThemedPaint, /* BS_COMMANDLINK */
194
CL_ThemedPaint /* BS_DEFCOMMANDLINK */
195
};
196
197
typedef BOOL (*pfGetIdealSize)(BUTTON_INFO *infoPtr, SIZE *size);
198
199
static BOOL PB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size);
200
static BOOL CB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size);
201
static BOOL GB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size);
202
static BOOL SB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size);
203
static BOOL CL_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size);
204
205
static const pfGetIdealSize btnGetIdealSizeFunc[MAX_BTN_TYPE] = {
206
PB_GetIdealSize, /* BS_PUSHBUTTON */
207
PB_GetIdealSize, /* BS_DEFPUSHBUTTON */
208
CB_GetIdealSize, /* BS_CHECKBOX */
209
CB_GetIdealSize, /* BS_AUTOCHECKBOX */
210
CB_GetIdealSize, /* BS_RADIOBUTTON */
211
GB_GetIdealSize, /* BS_3STATE */
212
GB_GetIdealSize, /* BS_AUTO3STATE */
213
GB_GetIdealSize, /* BS_GROUPBOX */
214
PB_GetIdealSize, /* BS_USERBUTTON */
215
CB_GetIdealSize, /* BS_AUTORADIOBUTTON */
216
GB_GetIdealSize, /* BS_PUSHBOX */
217
GB_GetIdealSize, /* BS_OWNERDRAW */
218
SB_GetIdealSize, /* BS_SPLITBUTTON */
219
SB_GetIdealSize, /* BS_DEFSPLITBUTTON */
220
CL_GetIdealSize, /* BS_COMMANDLINK */
221
CL_GetIdealSize /* BS_DEFCOMMANDLINK */
222
};
223
224
/* Fixed margin for command links, regardless of DPI (based on tests done on Windows) */
225
enum { command_link_margin = 6 };
226
227
/* The width and height for the default command link glyph (when there's no image) */
228
enum { command_link_defglyph_size = 17 };
229
230
static inline UINT get_button_type( LONG window_style )
231
{
232
return (window_style & BS_TYPEMASK);
233
}
234
235
static inline BOOL button_centers_text( LONG window_style )
236
{
237
/* Push button's text is centered by default, same for split buttons */
238
UINT type = get_button_type(window_style);
239
return type <= BS_DEFPUSHBUTTON || type == BS_SPLITBUTTON || type == BS_DEFSPLITBUTTON;
240
}
241
242
/* paint a button of any type */
243
static inline void paint_button( BUTTON_INFO *infoPtr, LONG style, UINT action )
244
{
245
if (btnPaintFunc[style] && IsWindowVisible(infoPtr->hwnd))
246
{
247
HDC hdc = GetDC( infoPtr->hwnd );
248
btnPaintFunc[style]( infoPtr, hdc, action );
249
ReleaseDC( infoPtr->hwnd, hdc );
250
}
251
}
252
253
/* retrieve the button text; returned buffer must be freed by caller */
254
static inline WCHAR *get_button_text( const BUTTON_INFO *infoPtr )
255
{
256
INT len = GetWindowTextLengthW( infoPtr->hwnd );
257
WCHAR *buffer = Alloc( (len + 1) * sizeof(WCHAR) );
258
if (buffer)
259
GetWindowTextW( infoPtr->hwnd, buffer, len + 1 );
260
return buffer;
261
}
262
263
/* get the default glyph size for split buttons */
264
static LONG get_default_glyph_size(const BUTTON_INFO *infoPtr)
265
{
266
if (infoPtr->split_style & BCSS_IMAGE)
267
{
268
/* Size it to fit, including the left and right edges */
269
int w, h;
270
if (!ImageList_GetIconSize(infoPtr->glyph, &w, &h)) w = 0;
271
return w + GetSystemMetrics(SM_CXEDGE) * 2;
272
}
273
274
/* The glyph size relies on the default menu font's cell height */
275
return GetSystemMetrics(SM_CYMENUCHECK);
276
}
277
278
static BOOL is_themed_paint_supported(HTHEME theme, UINT btn_type)
279
{
280
if (!theme || !btnThemedPaintFunc[btn_type])
281
return FALSE;
282
283
if (btn_type == BS_COMMANDLINK || btn_type == BS_DEFCOMMANDLINK)
284
{
285
if (!IsThemePartDefined(theme, BP_COMMANDLINK, 0))
286
return FALSE;
287
}
288
289
return TRUE;
290
}
291
292
static void init_custom_draw(NMCUSTOMDRAW *nmcd, const BUTTON_INFO *infoPtr, HDC hdc, const RECT *rc)
293
{
294
nmcd->hdr.hwndFrom = infoPtr->hwnd;
295
nmcd->hdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID);
296
nmcd->hdr.code = NM_CUSTOMDRAW;
297
nmcd->hdc = hdc;
298
nmcd->rc = *rc;
299
nmcd->dwDrawStage = CDDS_PREERASE;
300
nmcd->dwItemSpec = 0;
301
nmcd->lItemlParam = 0;
302
nmcd->uItemState = IsWindowEnabled(infoPtr->hwnd) ? 0 : CDIS_DISABLED;
303
if (infoPtr->state & BST_PUSHED) nmcd->uItemState |= CDIS_SELECTED;
304
if (infoPtr->state & BST_FOCUS) nmcd->uItemState |= CDIS_FOCUS;
305
if (infoPtr->state & BST_HOT) nmcd->uItemState |= CDIS_HOT;
306
if (infoPtr->state & BST_INDETERMINATE)
307
nmcd->uItemState |= CDIS_INDETERMINATE;
308
309
/* Windows doesn't seem to send CDIS_CHECKED (it fails the tests) */
310
/* CDIS_SHOWKEYBOARDCUES is misleading, as the meaning is reversed */
311
/* FIXME: Handle it properly when we support keyboard cues? */
312
}
313
314
HRGN set_control_clipping( HDC hdc, const RECT *rect )
315
{
316
RECT rc = *rect;
317
HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
318
319
if (GetClipRgn( hdc, hrgn ) != 1)
320
{
321
DeleteObject( hrgn );
322
hrgn = 0;
323
}
324
DPtoLP( hdc, (POINT *)&rc, 2 );
325
if (GetLayout( hdc ) & LAYOUT_RTL) /* compensate for the shifting done by IntersectClipRect */
326
{
327
rc.left++;
328
rc.right++;
329
}
330
IntersectClipRect( hdc, rc.left, rc.top, rc.right, rc.bottom );
331
return hrgn;
332
}
333
334
static WCHAR *heap_strndupW(const WCHAR *src, size_t length)
335
{
336
size_t size = (length + 1) * sizeof(WCHAR);
337
WCHAR *dst = Alloc(size);
338
if (dst) memcpy(dst, src, size);
339
return dst;
340
}
341
342
/**********************************************************************
343
* Convert button styles to flags used by DrawText.
344
*/
345
static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style )
346
{
347
UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */
348
349
/* "Convert" pushlike buttons to pushbuttons */
350
if (style & BS_PUSHLIKE)
351
style &= ~BS_TYPEMASK;
352
353
if (!(style & BS_MULTILINE))
354
dtStyle |= DT_SINGLELINE;
355
else
356
dtStyle |= DT_WORDBREAK;
357
358
switch (style & BS_CENTER)
359
{
360
case BS_LEFT: /* DT_LEFT is 0 */ break;
361
case BS_RIGHT: dtStyle |= DT_RIGHT; break;
362
case BS_CENTER: dtStyle |= DT_CENTER; break;
363
default:
364
if (button_centers_text(style)) dtStyle |= DT_CENTER;
365
}
366
367
if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER));
368
369
/* DrawText ignores vertical alignment for multiline text,
370
* but we use these flags to align label manually.
371
*/
372
if (get_button_type(style) != BS_GROUPBOX)
373
{
374
switch (style & BS_VCENTER)
375
{
376
case BS_TOP: /* DT_TOP is 0 */ break;
377
case BS_BOTTOM: dtStyle |= DT_BOTTOM; break;
378
case BS_VCENTER: /* fall through */
379
default: dtStyle |= DT_VCENTER; break;
380
}
381
}
382
383
return dtStyle;
384
}
385
386
static int get_draw_state(const BUTTON_INFO *infoPtr)
387
{
388
static const int pb_states[DRAW_STATE_COUNT] = { PBS_NORMAL, PBS_DISABLED, PBS_HOT, PBS_PRESSED, PBS_DEFAULTED };
389
static const int cb_states[3][DRAW_STATE_COUNT] =
390
{
391
{ CBS_UNCHECKEDNORMAL, CBS_UNCHECKEDDISABLED, CBS_UNCHECKEDHOT, CBS_UNCHECKEDPRESSED, CBS_UNCHECKEDNORMAL },
392
{ CBS_CHECKEDNORMAL, CBS_CHECKEDDISABLED, CBS_CHECKEDHOT, CBS_CHECKEDPRESSED, CBS_CHECKEDNORMAL },
393
{ CBS_MIXEDNORMAL, CBS_MIXEDDISABLED, CBS_MIXEDHOT, CBS_MIXEDPRESSED, CBS_MIXEDNORMAL }
394
};
395
static const int pushlike_cb_states[3][DRAW_STATE_COUNT] =
396
{
397
{ PBS_NORMAL, PBS_DISABLED, PBS_HOT, PBS_PRESSED, PBS_NORMAL },
398
{ PBS_PRESSED, PBS_PRESSED, PBS_PRESSED, PBS_PRESSED, PBS_PRESSED },
399
{ PBS_NORMAL, PBS_DISABLED, PBS_HOT, PBS_PRESSED, PBS_NORMAL }
400
};
401
static const int rb_states[2][DRAW_STATE_COUNT] =
402
{
403
{ RBS_UNCHECKEDNORMAL, RBS_UNCHECKEDDISABLED, RBS_UNCHECKEDHOT, RBS_UNCHECKEDPRESSED, RBS_UNCHECKEDNORMAL },
404
{ RBS_CHECKEDNORMAL, RBS_CHECKEDDISABLED, RBS_CHECKEDHOT, RBS_CHECKEDPRESSED, RBS_CHECKEDNORMAL }
405
};
406
static const int pushlike_rb_states[2][DRAW_STATE_COUNT] =
407
{
408
{ PBS_NORMAL, PBS_DISABLED, PBS_HOT, PBS_PRESSED, PBS_NORMAL },
409
{ PBS_PRESSED, PBS_PRESSED, PBS_PRESSED, PBS_PRESSED, PBS_PRESSED }
410
};
411
static const int gb_states[DRAW_STATE_COUNT] = { GBS_NORMAL, GBS_DISABLED, GBS_NORMAL, GBS_NORMAL, GBS_NORMAL };
412
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
413
UINT type = get_button_type(style);
414
int check_state = infoPtr->state & 3;
415
enum draw_state state;
416
417
if (!IsWindowEnabled(infoPtr->hwnd))
418
state = STATE_DISABLED;
419
else if (infoPtr->state & BST_PUSHED)
420
state = STATE_PRESSED;
421
else if (infoPtr->state & BST_HOT)
422
state = STATE_HOT;
423
else if (infoPtr->state & BST_FOCUS || type == BS_DEFPUSHBUTTON || type == BS_DEFSPLITBUTTON
424
|| (type == BS_DEFCOMMANDLINK && !(style & BS_PUSHLIKE)))
425
state = STATE_DEFAULTED;
426
else
427
state = STATE_NORMAL;
428
429
switch (type)
430
{
431
case BS_PUSHBUTTON:
432
case BS_DEFPUSHBUTTON:
433
case BS_USERBUTTON:
434
case BS_SPLITBUTTON:
435
case BS_DEFSPLITBUTTON:
436
case BS_COMMANDLINK:
437
case BS_DEFCOMMANDLINK:
438
return pb_states[state];
439
case BS_CHECKBOX:
440
case BS_AUTOCHECKBOX:
441
case BS_3STATE:
442
case BS_AUTO3STATE:
443
return style & BS_PUSHLIKE ? pushlike_cb_states[check_state][state]
444
: cb_states[check_state][state];
445
case BS_RADIOBUTTON:
446
case BS_AUTORADIOBUTTON:
447
return style & BS_PUSHLIKE ? pushlike_rb_states[check_state][state]
448
: rb_states[check_state][state];
449
case BS_GROUPBOX:
450
return style & BS_PUSHLIKE ? pb_states[state] : gb_states[state];
451
default:
452
WARN("Unsupported button type 0x%08x\n", type);
453
return PBS_NORMAL;
454
}
455
}
456
457
static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
458
{
459
BUTTON_INFO *infoPtr = (BUTTON_INFO *)GetWindowLongPtrW(hWnd, 0);
460
RECT rect;
461
POINT pt;
462
LONG style = GetWindowLongW( hWnd, GWL_STYLE );
463
UINT btn_type = get_button_type( style );
464
LONG state, new_state;
465
HANDLE oldHbitmap;
466
HTHEME theme;
467
468
if (!IsWindow( hWnd )) return 0;
469
470
if (!infoPtr && (uMsg != WM_NCCREATE))
471
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
472
473
pt.x = (short)LOWORD(lParam);
474
pt.y = (short)HIWORD(lParam);
475
476
switch (uMsg)
477
{
478
case WM_GETDLGCODE:
479
switch(btn_type)
480
{
481
case BS_COMMANDLINK:
482
case BS_USERBUTTON:
483
case BS_PUSHBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
484
case BS_DEFCOMMANDLINK:
485
case BS_DEFPUSHBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
486
case BS_RADIOBUTTON:
487
case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON;
488
case BS_GROUPBOX: return DLGC_STATIC;
489
case BS_SPLITBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON | DLGC_WANTARROWS;
490
case BS_DEFSPLITBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON | DLGC_WANTARROWS;
491
default: return DLGC_BUTTON;
492
}
493
494
case WM_ENABLE:
495
theme = GetWindowTheme( hWnd );
496
if (theme)
497
RedrawWindow( hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW );
498
else
499
paint_button( infoPtr, btn_type, ODA_DRAWENTIRE );
500
break;
501
502
case WM_NCCREATE:
503
{
504
CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam;
505
506
infoPtr = Alloc( sizeof(*infoPtr) );
507
SetWindowLongPtrW( hWnd, 0, (LONG_PTR)infoPtr );
508
infoPtr->hwnd = hWnd;
509
infoPtr->parent = cs->hwndParent;
510
infoPtr->style = cs->style;
511
infoPtr->split_style = BCSS_STRETCH;
512
infoPtr->glyph = (HIMAGELIST)0x36; /* Marlett down arrow char code */
513
infoPtr->glyph_size.cx = get_default_glyph_size(infoPtr);
514
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
515
}
516
517
case WM_NCDESTROY:
518
SetWindowLongPtrW( hWnd, 0, 0 );
519
if (infoPtr->image_type == IMAGE_BITMAP)
520
DeleteObject(infoPtr->u.bitmap);
521
else if (infoPtr->image_type == IMAGE_ICON)
522
DestroyIcon(infoPtr->u.icon);
523
Free(infoPtr->note);
524
Free(infoPtr);
525
break;
526
527
case WM_CREATE:
528
{
529
HWND parent;
530
531
if (btn_type >= MAX_BTN_TYPE)
532
return -1; /* abort */
533
534
/* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
535
if (btn_type == BS_USERBUTTON )
536
{
537
style = (style & ~BS_TYPEMASK) | BS_PUSHBUTTON;
538
SetWindowLongW( hWnd, GWL_STYLE, style );
539
}
540
infoPtr->state = BST_UNCHECKED;
541
OpenThemeData( hWnd, WC_BUTTONW );
542
543
parent = GetParent( hWnd );
544
if (parent)
545
EnableThemeDialogTexture( parent, ETDT_ENABLE );
546
return 0;
547
}
548
549
case WM_DESTROY:
550
theme = GetWindowTheme( hWnd );
551
CloseThemeData( theme );
552
break;
553
554
case WM_THEMECHANGED:
555
theme = GetWindowTheme( hWnd );
556
CloseThemeData( theme );
557
OpenThemeData( hWnd, WC_BUTTONW );
558
InvalidateRect( hWnd, NULL, TRUE );
559
break;
560
561
case WM_ERASEBKGND:
562
if (btn_type == BS_OWNERDRAW)
563
{
564
HDC hdc = (HDC)wParam;
565
RECT rc;
566
HBRUSH hBrush;
567
HWND parent = GetParent(hWnd);
568
if (!parent) parent = hWnd;
569
hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd);
570
if (!hBrush) /* did the app forget to call defwindowproc ? */
571
hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
572
(WPARAM)hdc, (LPARAM)hWnd);
573
GetClientRect(hWnd, &rc);
574
FillRect(hdc, &rc, hBrush);
575
}
576
return 1;
577
578
case WM_PRINTCLIENT:
579
case WM_PAINT:
580
{
581
PAINTSTRUCT ps;
582
HDC hdc;
583
584
theme = GetWindowTheme( hWnd );
585
hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
586
587
if (is_themed_paint_supported(theme, btn_type))
588
{
589
int drawState = get_draw_state(infoPtr);
590
UINT dtflags = BUTTON_BStoDT(style, GetWindowLongW(hWnd, GWL_EXSTYLE));
591
592
btnThemedPaintFunc[btn_type](theme, infoPtr, hdc, drawState, dtflags, infoPtr->state & BST_FOCUS);
593
}
594
else if (btnPaintFunc[btn_type])
595
{
596
int nOldMode = SetBkMode( hdc, OPAQUE );
597
btnPaintFunc[btn_type]( infoPtr, hdc, ODA_DRAWENTIRE );
598
SetBkMode(hdc, nOldMode); /* reset painting mode */
599
}
600
601
if ( !wParam ) EndPaint( hWnd, &ps );
602
break;
603
}
604
605
case WM_KEYDOWN:
606
if (wParam == VK_SPACE)
607
{
608
SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
609
infoPtr->state |= BUTTON_BTNPRESSED;
610
SetCapture( hWnd );
611
}
612
else if (wParam == VK_UP || wParam == VK_DOWN)
613
{
614
/* Up and down arrows work on every button, and even with BCSS_NOSPLIT */
615
notify_split_button_dropdown(infoPtr, NULL, hWnd);
616
}
617
break;
618
619
case WM_LBUTTONDBLCLK:
620
if(style & BS_NOTIFY ||
621
btn_type == BS_RADIOBUTTON ||
622
btn_type == BS_USERBUTTON ||
623
btn_type == BS_OWNERDRAW)
624
{
625
BUTTON_NOTIFY_PARENT(hWnd, BN_DOUBLECLICKED);
626
break;
627
}
628
/* fall through */
629
case WM_LBUTTONDOWN:
630
infoPtr->state |= BUTTON_BTNPRESSED;
631
SetFocus( hWnd );
632
633
if ((btn_type == BS_SPLITBUTTON || btn_type == BS_DEFSPLITBUTTON) &&
634
!(infoPtr->split_style & BCSS_NOSPLIT) &&
635
notify_split_button_dropdown(infoPtr, &pt, hWnd))
636
{
637
infoPtr->state &= ~BUTTON_BTNPRESSED;
638
break;
639
}
640
641
SetCapture( hWnd );
642
SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
643
break;
644
645
case WM_KEYUP:
646
if (wParam != VK_SPACE)
647
break;
648
/* fall through */
649
case WM_LBUTTONUP:
650
state = infoPtr->state;
651
if (state & BST_DROPDOWNPUSHED)
652
SendMessageW(hWnd, BCM_SETDROPDOWNSTATE, FALSE, 0);
653
if (!(state & BUTTON_BTNPRESSED)) break;
654
infoPtr->state &= BUTTON_NSTATES | BST_HOT;
655
if (!(state & BST_PUSHED))
656
{
657
ReleaseCapture();
658
break;
659
}
660
SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
661
GetClientRect( hWnd, &rect );
662
if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
663
{
664
switch(btn_type)
665
{
666
case BS_AUTOCHECKBOX:
667
SendMessageW( hWnd, BM_SETCHECK, !(infoPtr->state & BST_CHECKED), 0 );
668
break;
669
case BS_AUTORADIOBUTTON:
670
SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
671
break;
672
case BS_AUTO3STATE:
673
SendMessageW( hWnd, BM_SETCHECK, (infoPtr->state & BST_INDETERMINATE) ? 0 :
674
((infoPtr->state & 3) + 1), 0 );
675
break;
676
}
677
ReleaseCapture();
678
BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED);
679
}
680
else
681
{
682
ReleaseCapture();
683
}
684
break;
685
686
case WM_CAPTURECHANGED:
687
TRACE("WM_CAPTURECHANGED %p\n", hWnd);
688
if (hWnd == (HWND)lParam) break;
689
if (infoPtr->state & BUTTON_BTNPRESSED)
690
{
691
infoPtr->state &= BUTTON_NSTATES;
692
if (infoPtr->state & BST_PUSHED)
693
SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
694
}
695
break;
696
697
case WM_MOUSEMOVE:
698
{
699
TRACKMOUSEEVENT mouse_event;
700
701
mouse_event.cbSize = sizeof(TRACKMOUSEEVENT);
702
mouse_event.dwFlags = TME_QUERY;
703
if (!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags & (TME_HOVER | TME_LEAVE)))
704
{
705
mouse_event.dwFlags = TME_HOVER | TME_LEAVE;
706
mouse_event.hwndTrack = hWnd;
707
mouse_event.dwHoverTime = 1;
708
TrackMouseEvent(&mouse_event);
709
}
710
711
if ((wParam & MK_LBUTTON) && GetCapture() == hWnd)
712
{
713
GetClientRect( hWnd, &rect );
714
SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
715
}
716
break;
717
}
718
719
case WM_MOUSEHOVER:
720
{
721
infoPtr->state |= BST_HOT;
722
InvalidateRect( hWnd, NULL, FALSE );
723
break;
724
}
725
726
case WM_MOUSELEAVE:
727
{
728
infoPtr->state &= ~BST_HOT;
729
InvalidateRect( hWnd, NULL, FALSE );
730
break;
731
}
732
733
case WM_SETTEXT:
734
{
735
/* Clear an old text here as Windows does */
736
if (IsWindowVisible(hWnd))
737
{
738
HDC hdc = GetDC(hWnd);
739
HBRUSH hbrush;
740
RECT client, rc;
741
HWND parent = GetParent(hWnd);
742
UINT message = (btn_type == BS_PUSHBUTTON ||
743
btn_type == BS_DEFPUSHBUTTON ||
744
btn_type == BS_USERBUTTON ||
745
btn_type == BS_OWNERDRAW) ?
746
WM_CTLCOLORBTN : WM_CTLCOLORSTATIC;
747
748
if (!parent) parent = hWnd;
749
hbrush = (HBRUSH)SendMessageW(parent, message,
750
(WPARAM)hdc, (LPARAM)hWnd);
751
if (!hbrush) /* did the app forget to call DefWindowProc ? */
752
hbrush = (HBRUSH)DefWindowProcW(parent, message,
753
(WPARAM)hdc, (LPARAM)hWnd);
754
755
GetClientRect(hWnd, &client);
756
rc = client;
757
/* FIXME: check other BS_* handlers */
758
if (btn_type == BS_GROUPBOX)
759
InflateRect(&rc, -7, 1); /* GB_Paint does this */
760
BUTTON_CalcLayoutRects(infoPtr, hdc, &rc, NULL, NULL);
761
/* Clip by client rect bounds */
762
if (rc.right > client.right) rc.right = client.right;
763
if (rc.bottom > client.bottom) rc.bottom = client.bottom;
764
FillRect(hdc, &rc, hbrush);
765
ReleaseDC(hWnd, hdc);
766
}
767
768
DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
769
if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */
770
InvalidateRect( hWnd, NULL, TRUE );
771
else if (GetWindowTheme( hWnd ))
772
RedrawWindow( hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW );
773
else
774
paint_button( infoPtr, btn_type, ODA_DRAWENTIRE );
775
return 1; /* success. FIXME: check text length */
776
}
777
778
case BCM_SETNOTE:
779
{
780
WCHAR *note = (WCHAR *)lParam;
781
if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
782
{
783
SetLastError(ERROR_NOT_SUPPORTED);
784
return FALSE;
785
}
786
787
Free(infoPtr->note);
788
if (note)
789
{
790
infoPtr->note_length = lstrlenW(note);
791
infoPtr->note = heap_strndupW(note, infoPtr->note_length);
792
}
793
794
if (!note || !infoPtr->note)
795
{
796
infoPtr->note_length = 0;
797
infoPtr->note = Alloc(sizeof(WCHAR));
798
}
799
800
SetLastError(NO_ERROR);
801
return TRUE;
802
}
803
804
case BCM_GETNOTE:
805
{
806
DWORD *size = (DWORD *)wParam;
807
WCHAR *buffer = (WCHAR *)lParam;
808
INT length = 0;
809
810
if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
811
{
812
SetLastError(ERROR_NOT_SUPPORTED);
813
return FALSE;
814
}
815
816
if (!buffer || !size || !infoPtr->note)
817
{
818
SetLastError(ERROR_INVALID_PARAMETER);
819
return FALSE;
820
}
821
822
if (*size > 0)
823
{
824
length = min(*size - 1, infoPtr->note_length);
825
memcpy(buffer, infoPtr->note, length * sizeof(WCHAR));
826
buffer[length] = '\0';
827
}
828
829
if (*size < infoPtr->note_length + 1)
830
{
831
*size = infoPtr->note_length + 1;
832
SetLastError(ERROR_INSUFFICIENT_BUFFER);
833
return FALSE;
834
}
835
else
836
{
837
SetLastError(NO_ERROR);
838
return TRUE;
839
}
840
}
841
842
case BCM_GETNOTELENGTH:
843
{
844
if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
845
{
846
SetLastError(ERROR_NOT_SUPPORTED);
847
return 0;
848
}
849
850
return infoPtr->note_length;
851
}
852
853
case WM_SETFONT:
854
infoPtr->font = (HFONT)wParam;
855
if (lParam) InvalidateRect(hWnd, NULL, TRUE);
856
break;
857
858
case WM_GETFONT:
859
return (LRESULT)infoPtr->font;
860
861
case WM_SETFOCUS:
862
TRACE("WM_SETFOCUS %p\n",hWnd);
863
infoPtr->state |= BST_FOCUS;
864
865
if (btn_type == BS_OWNERDRAW)
866
paint_button( infoPtr, btn_type, ODA_FOCUS );
867
else
868
InvalidateRect(hWnd, NULL, FALSE);
869
870
if (style & BS_NOTIFY)
871
BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS);
872
873
if (((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON)) &&
874
!(infoPtr->state & (BST_CHECKED | BUTTON_BTNPRESSED)))
875
{
876
BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED);
877
}
878
break;
879
880
case WM_KILLFOCUS:
881
TRACE("WM_KILLFOCUS %p\n",hWnd);
882
infoPtr->state &= ~BST_FOCUS;
883
884
if ((infoPtr->state & BUTTON_BTNPRESSED) && GetCapture() == hWnd)
885
ReleaseCapture();
886
if (style & BS_NOTIFY)
887
BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS);
888
889
InvalidateRect( hWnd, NULL, FALSE );
890
break;
891
892
case WM_SYSCOLORCHANGE:
893
InvalidateRect( hWnd, NULL, FALSE );
894
break;
895
896
case WM_GETOBJECT:
897
if ((LONG)lParam == OBJID_QUERYCLASSNAMEIDX)
898
return 0x10002;
899
break;
900
901
case BM_SETSTYLE:
902
{
903
DWORD new_btn_type;
904
905
new_btn_type= wParam & BS_TYPEMASK;
906
if (btn_type >= BS_SPLITBUTTON && new_btn_type <= BS_DEFPUSHBUTTON)
907
new_btn_type = (btn_type & ~BS_DEFPUSHBUTTON) | new_btn_type;
908
909
style = (style & ~BS_TYPEMASK) | new_btn_type;
910
SetWindowLongW( hWnd, GWL_STYLE, style );
911
912
NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 );
913
914
/* Only redraw if lParam flag is set.*/
915
if (lParam)
916
InvalidateRect( hWnd, NULL, TRUE );
917
918
break;
919
}
920
case BM_CLICK:
921
SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
922
SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
923
break;
924
925
case BM_SETIMAGE:
926
infoPtr->image_type = (DWORD)wParam;
927
oldHbitmap = infoPtr->image;
928
infoPtr->u.image = CopyImage((HANDLE)lParam, infoPtr->image_type, 0, 0, 0);
929
infoPtr->image = (HANDLE)lParam;
930
InvalidateRect( hWnd, NULL, FALSE );
931
return (LRESULT)oldHbitmap;
932
933
case BM_GETIMAGE:
934
return (LRESULT)infoPtr->image;
935
936
case BCM_SETIMAGELIST:
937
{
938
BUTTON_IMAGELIST *imagelist = (BUTTON_IMAGELIST *)lParam;
939
940
if (!imagelist) return FALSE;
941
942
infoPtr->imagelist = *imagelist;
943
return TRUE;
944
}
945
946
case BCM_GETIMAGELIST:
947
{
948
BUTTON_IMAGELIST *imagelist = (BUTTON_IMAGELIST *)lParam;
949
950
if (!imagelist) return FALSE;
951
952
*imagelist = infoPtr->imagelist;
953
return TRUE;
954
}
955
956
case BCM_SETSPLITINFO:
957
{
958
BUTTON_SPLITINFO *info = (BUTTON_SPLITINFO*)lParam;
959
960
if (!info) return TRUE;
961
962
if (info->mask & (BCSIF_GLYPH | BCSIF_IMAGE))
963
{
964
infoPtr->split_style &= ~BCSS_IMAGE;
965
if (!(info->mask & BCSIF_GLYPH))
966
infoPtr->split_style |= BCSS_IMAGE;
967
infoPtr->glyph = info->himlGlyph;
968
infoPtr->glyph_size.cx = infoPtr->glyph_size.cy = 0;
969
}
970
971
if (info->mask & BCSIF_STYLE)
972
infoPtr->split_style = info->uSplitStyle;
973
if (info->mask & BCSIF_SIZE)
974
infoPtr->glyph_size = info->size;
975
976
/* Calculate fitting value for cx if invalid (cy is untouched) */
977
if (infoPtr->glyph_size.cx <= 0)
978
infoPtr->glyph_size.cx = get_default_glyph_size(infoPtr);
979
980
/* Windows doesn't invalidate or redraw it, so we don't, either */
981
return TRUE;
982
}
983
984
case BCM_GETSPLITINFO:
985
{
986
BUTTON_SPLITINFO *info = (BUTTON_SPLITINFO*)lParam;
987
988
if (!info) return FALSE;
989
990
if (info->mask & BCSIF_STYLE)
991
info->uSplitStyle = infoPtr->split_style;
992
if (info->mask & (BCSIF_GLYPH | BCSIF_IMAGE))
993
info->himlGlyph = infoPtr->glyph;
994
if (info->mask & BCSIF_SIZE)
995
info->size = infoPtr->glyph_size;
996
997
return TRUE;
998
}
999
1000
case BM_GETCHECK:
1001
return infoPtr->state & 3;
1002
1003
case BM_SETCHECK:
1004
if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
1005
if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
1006
{
1007
style = wParam ? style | WS_TABSTOP : style & ~WS_TABSTOP;
1008
SetWindowLongW( hWnd, GWL_STYLE, style );
1009
}
1010
if ((infoPtr->state & 3) != wParam)
1011
{
1012
infoPtr->state = (infoPtr->state & ~3) | wParam;
1013
InvalidateRect( hWnd, NULL, FALSE );
1014
NotifyWinEvent( UIA_ToggleToggleStatePropertyId, hWnd, OBJID_CLIENT, 0 );
1015
NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 );
1016
}
1017
if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD))
1018
BUTTON_CheckAutoRadioButton( hWnd );
1019
break;
1020
1021
case BM_GETSTATE:
1022
return infoPtr->state;
1023
1024
case BM_SETSTATE:
1025
state = infoPtr->state;
1026
new_state = wParam ? BST_PUSHED : 0;
1027
1028
if ((state ^ new_state) & BST_PUSHED)
1029
{
1030
if (wParam)
1031
state |= BST_PUSHED;
1032
else
1033
state &= ~BST_PUSHED;
1034
1035
if (btn_type == BS_USERBUTTON)
1036
BUTTON_NOTIFY_PARENT( hWnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE );
1037
infoPtr->state = state;
1038
1039
NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 );
1040
1041
InvalidateRect( hWnd, NULL, FALSE );
1042
}
1043
break;
1044
1045
case BCM_SETDROPDOWNSTATE:
1046
new_state = wParam ? BST_DROPDOWNPUSHED : 0;
1047
1048
if ((infoPtr->state ^ new_state) & BST_DROPDOWNPUSHED)
1049
{
1050
infoPtr->state &= ~BST_DROPDOWNPUSHED;
1051
infoPtr->state |= new_state;
1052
NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 );
1053
NotifyWinEvent( UIA_ExpandCollapseExpandCollapseStatePropertyId, hWnd, OBJID_CLIENT, 0 );
1054
1055
/* Windows sends this twice for some reason */
1056
NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 );
1057
NotifyWinEvent( UIA_ExpandCollapseExpandCollapseStatePropertyId, hWnd, OBJID_CLIENT, 0 );
1058
1059
InvalidateRect(hWnd, NULL, FALSE);
1060
}
1061
break;
1062
1063
case BCM_SETTEXTMARGIN:
1064
{
1065
RECT *text_margin = (RECT *)lParam;
1066
1067
if (!text_margin) return FALSE;
1068
1069
infoPtr->text_margin = *text_margin;
1070
return TRUE;
1071
}
1072
1073
case BCM_GETTEXTMARGIN:
1074
{
1075
RECT *text_margin = (RECT *)lParam;
1076
1077
if (!text_margin) return FALSE;
1078
1079
*text_margin = infoPtr->text_margin;
1080
return TRUE;
1081
}
1082
1083
case BCM_GETIDEALSIZE:
1084
{
1085
SIZE *size = (SIZE *)lParam;
1086
1087
if (!size) return FALSE;
1088
1089
return btnGetIdealSizeFunc[btn_type](infoPtr, size);
1090
}
1091
1092
case WM_NCHITTEST:
1093
if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
1094
/* fall through */
1095
default:
1096
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1097
}
1098
return 0;
1099
}
1100
1101
/* If maxWidth is zero, rectangle width is unlimited */
1102
static RECT BUTTON_GetTextRect(const BUTTON_INFO *infoPtr, HDC hdc, const WCHAR *text, LONG maxWidth)
1103
{
1104
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1105
LONG exStyle = GetWindowLongW(infoPtr->hwnd, GWL_EXSTYLE);
1106
UINT dtStyle = BUTTON_BStoDT(style, exStyle);
1107
HFONT hPrevFont;
1108
RECT rect = {0};
1109
1110
rect.right = maxWidth;
1111
hPrevFont = SelectObject(hdc, infoPtr->font);
1112
/* Calculate height without DT_VCENTER and DT_BOTTOM to get the correct height */
1113
DrawTextW(hdc, text, -1, &rect, (dtStyle & ~(DT_VCENTER | DT_BOTTOM)) | DT_CALCRECT);
1114
if (hPrevFont) SelectObject(hdc, hPrevFont);
1115
1116
return rect;
1117
}
1118
1119
static BOOL show_image_only(const BUTTON_INFO *infoPtr)
1120
{
1121
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1122
return (style & (BS_ICON | BS_BITMAP)) && (infoPtr->u.image || infoPtr->imagelist.himl);
1123
}
1124
1125
static BOOL show_image_and_text(const BUTTON_INFO *infoPtr)
1126
{
1127
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1128
UINT type = get_button_type(style);
1129
return !(style & (BS_ICON | BS_BITMAP))
1130
&& ((infoPtr->u.image
1131
&& (type == BS_PUSHBUTTON || type == BS_DEFPUSHBUTTON || type == BS_USERBUTTON || type == BS_SPLITBUTTON
1132
|| type == BS_DEFSPLITBUTTON || type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK))
1133
|| (infoPtr->imagelist.himl && type != BS_GROUPBOX));
1134
}
1135
1136
static BOOL show_image(const BUTTON_INFO *infoPtr)
1137
{
1138
return show_image_only(infoPtr) || show_image_and_text(infoPtr);
1139
}
1140
1141
/* Get a bounding rectangle that is large enough to contain a image and a text side by side.
1142
* Note: (left,top) of the result rectangle may not be (0,0), offset it by yourself if needed */
1143
static RECT BUTTON_GetBoundingLabelRect(LONG style, const RECT *textRect, const RECT *imageRect)
1144
{
1145
RECT labelRect;
1146
RECT rect = *imageRect;
1147
INT textWidth = textRect->right - textRect->left;
1148
INT textHeight = textRect->bottom - textRect->top;
1149
INT imageWidth = imageRect->right - imageRect->left;
1150
INT imageHeight = imageRect->bottom - imageRect->top;
1151
1152
if ((style & BS_CENTER) == BS_RIGHT)
1153
OffsetRect(&rect, textWidth, 0);
1154
else if ((style & BS_CENTER) == BS_LEFT)
1155
OffsetRect(&rect, -imageWidth, 0);
1156
else if ((style & BS_VCENTER) == BS_BOTTOM)
1157
OffsetRect(&rect, 0, textHeight);
1158
else if ((style & BS_VCENTER) == BS_TOP)
1159
OffsetRect(&rect, 0, -imageHeight);
1160
else
1161
OffsetRect(&rect, -imageWidth, 0);
1162
1163
UnionRect(&labelRect, textRect, &rect);
1164
return labelRect;
1165
}
1166
1167
/* Position a rectangle inside a bounding rectangle according to button alignment flags */
1168
static void BUTTON_PositionRect(LONG style, const RECT *outerRect, RECT *innerRect, const RECT *margin)
1169
{
1170
INT width = innerRect->right - innerRect->left;
1171
INT height = innerRect->bottom - innerRect->top;
1172
1173
if ((style & BS_PUSHLIKE) && !(style & BS_CENTER)) style |= BS_CENTER;
1174
1175
if (!(style & BS_CENTER))
1176
{
1177
if (button_centers_text(style))
1178
style |= BS_CENTER;
1179
else
1180
style |= BS_LEFT;
1181
}
1182
1183
if (!(style & BS_VCENTER))
1184
{
1185
/* Group box's text is top aligned by default */
1186
if (get_button_type(style) == BS_GROUPBOX)
1187
style |= BS_TOP;
1188
}
1189
1190
switch (style & BS_CENTER)
1191
{
1192
case BS_CENTER:
1193
/* The left and right margins are added to the inner rectangle to get a new rectangle. Then
1194
* the new rectangle is adjusted to be in the horizontal center */
1195
innerRect->left = outerRect->left + (outerRect->right - outerRect->left - width
1196
+ margin->left - margin->right) / 2;
1197
innerRect->right = innerRect->left + width;
1198
break;
1199
case BS_RIGHT:
1200
innerRect->right = outerRect->right - margin->right;
1201
innerRect->left = innerRect->right - width;
1202
break;
1203
case BS_LEFT:
1204
default:
1205
innerRect->left = outerRect->left + margin->left;
1206
innerRect->right = innerRect->left + width;
1207
break;
1208
}
1209
1210
switch (style & BS_VCENTER)
1211
{
1212
case BS_TOP:
1213
innerRect->top = outerRect->top + margin->top;
1214
innerRect->bottom = innerRect->top + height;
1215
break;
1216
case BS_BOTTOM:
1217
innerRect->bottom = outerRect->bottom - margin->bottom;
1218
innerRect->top = innerRect->bottom - height;
1219
break;
1220
case BS_VCENTER:
1221
default:
1222
/* The top and bottom margins are added to the inner rectangle to get a new rectangle. Then
1223
* the new rectangle is adjusted to be in the vertical center */
1224
innerRect->top = outerRect->top + (outerRect->bottom - outerRect->top - height
1225
+ margin->top - margin->bottom) / 2;
1226
innerRect->bottom = innerRect->top + height;
1227
break;
1228
}
1229
}
1230
1231
/* Convert imagelist align style to button align style */
1232
static UINT BUTTON_ILStoBS(UINT align)
1233
{
1234
switch (align)
1235
{
1236
case BUTTON_IMAGELIST_ALIGN_TOP:
1237
return BS_CENTER | BS_TOP;
1238
case BUTTON_IMAGELIST_ALIGN_BOTTOM:
1239
return BS_CENTER | BS_BOTTOM;
1240
case BUTTON_IMAGELIST_ALIGN_CENTER:
1241
return BS_CENTER | BS_VCENTER;
1242
case BUTTON_IMAGELIST_ALIGN_RIGHT:
1243
return BS_RIGHT | BS_VCENTER;
1244
case BUTTON_IMAGELIST_ALIGN_LEFT:
1245
default:
1246
return BS_LEFT | BS_VCENTER;
1247
}
1248
}
1249
1250
static SIZE BUTTON_GetImageSize(const BUTTON_INFO *infoPtr)
1251
{
1252
ICONINFO iconInfo;
1253
BITMAP bm = {0};
1254
SIZE size = {0};
1255
1256
/* ImageList has priority over image */
1257
if (infoPtr->imagelist.himl)
1258
{
1259
int scx, scy;
1260
ImageList_GetIconSize(infoPtr->imagelist.himl, &scx, &scy);
1261
size.cx = scx;
1262
size.cy = scy;
1263
}
1264
else if (infoPtr->u.image)
1265
{
1266
if (infoPtr->image_type == IMAGE_ICON)
1267
{
1268
GetIconInfo(infoPtr->u.icon, &iconInfo);
1269
GetObjectW(iconInfo.hbmColor, sizeof(bm), &bm);
1270
DeleteObject(iconInfo.hbmColor);
1271
DeleteObject(iconInfo.hbmMask);
1272
}
1273
else if (infoPtr->image_type == IMAGE_BITMAP)
1274
GetObjectW(infoPtr->u.bitmap, sizeof(bm), &bm);
1275
1276
size.cx = bm.bmWidth;
1277
size.cy = bm.bmHeight;
1278
}
1279
1280
return size;
1281
}
1282
1283
static const RECT *BUTTON_GetTextMargin(const BUTTON_INFO *infoPtr)
1284
{
1285
static const RECT oneMargin = {1, 1, 1, 1};
1286
1287
/* Use text margin only when showing both image and text, and image is not imagelist */
1288
if (show_image_and_text(infoPtr) && !infoPtr->imagelist.himl)
1289
return &infoPtr->text_margin;
1290
else
1291
return &oneMargin;
1292
}
1293
1294
static void BUTTON_GetClientRectSize(BUTTON_INFO *infoPtr, SIZE *size)
1295
{
1296
RECT rect;
1297
GetClientRect(infoPtr->hwnd, &rect);
1298
size->cx = rect.right - rect.left;
1299
size->cy = rect.bottom - rect.top;
1300
}
1301
1302
static void BUTTON_GetTextIdealSize(BUTTON_INFO *infoPtr, LONG maxWidth, SIZE *size)
1303
{
1304
WCHAR *text = get_button_text(infoPtr);
1305
HDC hdc;
1306
RECT rect;
1307
const RECT *margin = BUTTON_GetTextMargin(infoPtr);
1308
1309
if (maxWidth != 0)
1310
{
1311
maxWidth -= margin->right + margin->right;
1312
if (maxWidth <= 0) maxWidth = 1;
1313
}
1314
1315
hdc = GetDC(infoPtr->hwnd);
1316
rect = BUTTON_GetTextRect(infoPtr, hdc, text, maxWidth);
1317
ReleaseDC(infoPtr->hwnd, hdc);
1318
Free(text);
1319
1320
size->cx = rect.right - rect.left + margin->left + margin->right;
1321
size->cy = rect.bottom - rect.top + margin->top + margin->bottom;
1322
}
1323
1324
static void BUTTON_GetLabelIdealSize(BUTTON_INFO *infoPtr, LONG maxWidth, SIZE *size)
1325
{
1326
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1327
SIZE imageSize;
1328
SIZE textSize;
1329
BOOL horizontal;
1330
1331
imageSize = BUTTON_GetImageSize(infoPtr);
1332
if (infoPtr->imagelist.himl)
1333
{
1334
imageSize.cx += infoPtr->imagelist.margin.left + infoPtr->imagelist.margin.right;
1335
imageSize.cy += infoPtr->imagelist.margin.top + infoPtr->imagelist.margin.bottom;
1336
if (infoPtr->imagelist.uAlign == BUTTON_IMAGELIST_ALIGN_TOP
1337
|| infoPtr->imagelist.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM)
1338
horizontal = FALSE;
1339
else
1340
horizontal = TRUE;
1341
}
1342
else
1343
{
1344
/* horizontal alignment flags has priority over vertical ones if both are specified */
1345
if (!(style & (BS_CENTER | BS_VCENTER)) || ((style & BS_CENTER) && (style & BS_CENTER) != BS_CENTER)
1346
|| !(style & BS_VCENTER) || (style & BS_VCENTER) == BS_VCENTER)
1347
horizontal = TRUE;
1348
else
1349
horizontal = FALSE;
1350
}
1351
1352
if (horizontal)
1353
{
1354
if (maxWidth != 0)
1355
{
1356
maxWidth -= imageSize.cx;
1357
if (maxWidth <= 0) maxWidth = 1;
1358
}
1359
BUTTON_GetTextIdealSize(infoPtr, maxWidth, &textSize);
1360
size->cx = textSize.cx + imageSize.cx;
1361
size->cy = max(textSize.cy, imageSize.cy);
1362
}
1363
else
1364
{
1365
BUTTON_GetTextIdealSize(infoPtr, maxWidth, &textSize);
1366
size->cx = max(textSize.cx, imageSize.cx);
1367
size->cy = textSize.cy + imageSize.cy;
1368
}
1369
}
1370
1371
static BOOL GB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size)
1372
{
1373
BUTTON_GetClientRectSize(infoPtr, size);
1374
return TRUE;
1375
}
1376
1377
static BOOL CB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size)
1378
{
1379
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1380
HDC hdc;
1381
HFONT hfont;
1382
SIZE labelSize;
1383
INT textOffset;
1384
double scaleX;
1385
double scaleY;
1386
LONG checkboxWidth, checkboxHeight;
1387
LONG maxWidth = 0;
1388
1389
if (SendMessageW(infoPtr->hwnd, WM_GETTEXTLENGTH, 0, 0) == 0)
1390
{
1391
BUTTON_GetClientRectSize(infoPtr, size);
1392
return TRUE;
1393
}
1394
1395
hdc = GetDC(infoPtr->hwnd);
1396
scaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0;
1397
scaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0;
1398
if ((hfont = infoPtr->font)) SelectObject(hdc, hfont);
1399
GetCharWidthW(hdc, '0', '0', &textOffset);
1400
textOffset /= 2;
1401
ReleaseDC(infoPtr->hwnd, hdc);
1402
1403
checkboxWidth = 12 * scaleX + 1;
1404
checkboxHeight = 12 * scaleY + 1;
1405
if (size->cx)
1406
{
1407
maxWidth = size->cx - checkboxWidth - textOffset;
1408
if (maxWidth <= 0) maxWidth = 1;
1409
}
1410
1411
/* Checkbox doesn't support both image(but not image list) and text */
1412
if (!(style & (BS_ICON | BS_BITMAP)) && infoPtr->u.image)
1413
BUTTON_GetTextIdealSize(infoPtr, maxWidth, &labelSize);
1414
else
1415
BUTTON_GetLabelIdealSize(infoPtr, maxWidth, &labelSize);
1416
1417
size->cx = labelSize.cx + checkboxWidth + textOffset;
1418
size->cy = max(labelSize.cy, checkboxHeight);
1419
1420
return TRUE;
1421
}
1422
1423
static BOOL PB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size)
1424
{
1425
SIZE labelSize;
1426
1427
if (SendMessageW(infoPtr->hwnd, WM_GETTEXTLENGTH, 0, 0) == 0)
1428
BUTTON_GetClientRectSize(infoPtr, size);
1429
else
1430
{
1431
/* Ideal size include text size even if image only flags(BS_ICON, BS_BITMAP) are specified */
1432
BUTTON_GetLabelIdealSize(infoPtr, size->cx, &labelSize);
1433
1434
size->cx = labelSize.cx;
1435
size->cy = labelSize.cy;
1436
}
1437
return TRUE;
1438
}
1439
1440
static BOOL SB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size)
1441
{
1442
LONG extra_width = infoPtr->glyph_size.cx * 2 + GetSystemMetrics(SM_CXEDGE);
1443
SIZE label_size;
1444
1445
if (SendMessageW(infoPtr->hwnd, WM_GETTEXTLENGTH, 0, 0) == 0)
1446
{
1447
BUTTON_GetClientRectSize(infoPtr, size);
1448
size->cx = max(size->cx, extra_width);
1449
}
1450
else
1451
{
1452
BUTTON_GetLabelIdealSize(infoPtr, size->cx, &label_size);
1453
size->cx = label_size.cx + ((size->cx == 0) ? extra_width : 0);
1454
size->cy = label_size.cy;
1455
}
1456
return TRUE;
1457
}
1458
1459
static BOOL CL_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size)
1460
{
1461
HTHEME theme = GetWindowTheme(infoPtr->hwnd);
1462
HDC hdc = GetDC(infoPtr->hwnd);
1463
LONG w, text_w = 0, text_h = 0;
1464
UINT flags = DT_TOP | DT_LEFT;
1465
HFONT font, old_font = NULL;
1466
RECT text_bound = { 0 };
1467
SIZE img_size;
1468
RECT margin;
1469
WCHAR *text;
1470
1471
/* Get the image size */
1472
if (infoPtr->u.image || infoPtr->imagelist.himl)
1473
img_size = BUTTON_GetImageSize(infoPtr);
1474
else
1475
{
1476
if (theme)
1477
GetThemePartSize(theme, NULL, BP_COMMANDLINKGLYPH, CMDLS_NORMAL, NULL, TS_DRAW, &img_size);
1478
else
1479
img_size.cx = img_size.cy = command_link_defglyph_size;
1480
}
1481
1482
/* Get the content margins */
1483
if (theme)
1484
{
1485
RECT r = { 0, 0, 0xffff, 0xffff };
1486
GetThemeBackgroundContentRect(theme, hdc, BP_COMMANDLINK, CMDLS_NORMAL, &r, &margin);
1487
margin.left -= r.left;
1488
margin.top -= r.top;
1489
margin.right = r.right - margin.right;
1490
margin.bottom = r.bottom - margin.bottom;
1491
}
1492
else
1493
{
1494
margin.left = margin.right = command_link_margin;
1495
margin.top = margin.bottom = command_link_margin;
1496
}
1497
1498
/* Account for the border margins and the margin between image and text */
1499
w = margin.left + margin.right + (img_size.cx ? (img_size.cx + command_link_margin) : 0);
1500
1501
/* If a rectangle with a specific width was requested, bound the text to it */
1502
if (size->cx > w)
1503
{
1504
text_bound.right = size->cx - w;
1505
flags |= DT_WORDBREAK;
1506
}
1507
1508
if (theme)
1509
{
1510
if (infoPtr->font) old_font = SelectObject(hdc, infoPtr->font);
1511
1512
/* Find the text's rect */
1513
if ((text = get_button_text(infoPtr)))
1514
{
1515
RECT r;
1516
GetThemeTextExtent(theme, hdc, BP_COMMANDLINK, CMDLS_NORMAL,
1517
text, -1, flags, &text_bound, &r);
1518
Free(text);
1519
text_w = r.right - r.left;
1520
text_h = r.bottom - r.top;
1521
}
1522
1523
/* Find the note's rect */
1524
if (infoPtr->note)
1525
{
1526
DTTOPTS opts;
1527
1528
opts.dwSize = sizeof(opts);
1529
opts.dwFlags = DTT_FONTPROP | DTT_CALCRECT;
1530
opts.iFontPropId = TMT_BODYFONT;
1531
DrawThemeTextEx(theme, hdc, BP_COMMANDLINK, CMDLS_NORMAL,
1532
infoPtr->note, infoPtr->note_length,
1533
flags | DT_NOPREFIX | DT_CALCRECT, &text_bound, &opts);
1534
text_w = max(text_w, text_bound.right - text_bound.left);
1535
text_h += text_bound.bottom - text_bound.top;
1536
}
1537
}
1538
else
1539
{
1540
NONCLIENTMETRICSW ncm;
1541
1542
ncm.cbSize = sizeof(ncm);
1543
if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
1544
{
1545
LONG note_weight = ncm.lfMessageFont.lfWeight;
1546
1547
/* Find the text's rect */
1548
ncm.lfMessageFont.lfWeight = FW_BOLD;
1549
if ((font = CreateFontIndirectW(&ncm.lfMessageFont)))
1550
{
1551
if ((text = get_button_text(infoPtr)))
1552
{
1553
RECT r = text_bound;
1554
old_font = SelectObject(hdc, font);
1555
DrawTextW(hdc, text, -1, &r, flags | DT_CALCRECT);
1556
Free(text);
1557
1558
text_w = r.right - r.left;
1559
text_h = r.bottom - r.top;
1560
}
1561
DeleteObject(font);
1562
}
1563
1564
/* Find the note's rect */
1565
ncm.lfMessageFont.lfWeight = note_weight;
1566
if (infoPtr->note && (font = CreateFontIndirectW(&ncm.lfMessageFont)))
1567
{
1568
HFONT tmp = SelectObject(hdc, font);
1569
if (!old_font) old_font = tmp;
1570
1571
DrawTextW(hdc, infoPtr->note, infoPtr->note_length, &text_bound,
1572
flags | DT_NOPREFIX | DT_CALCRECT);
1573
DeleteObject(font);
1574
1575
text_w = max(text_w, text_bound.right - text_bound.left);
1576
text_h += text_bound.bottom - text_bound.top + 2;
1577
}
1578
}
1579
}
1580
w += text_w;
1581
1582
size->cx = min(size->cx, w);
1583
size->cy = max(text_h, img_size.cy) + margin.top + margin.bottom;
1584
1585
if (old_font) SelectObject(hdc, old_font);
1586
ReleaseDC(infoPtr->hwnd, hdc);
1587
return TRUE;
1588
}
1589
1590
/**********************************************************************
1591
* BUTTON_CalcLayoutRects
1592
*
1593
* Calculates the rectangles of the button label(image and text) and its parts depending on a button's style.
1594
*
1595
* Returns flags to be passed to DrawText.
1596
* Calculated rectangle doesn't take into account button state
1597
* (pushed, etc.). If there is nothing to draw (no text/image) output
1598
* rectangle is empty, and return value is (UINT)-1.
1599
*
1600
* PARAMS:
1601
* infoPtr [I] Button pointer
1602
* hdc [I] Handle to device context to draw to
1603
* labelRc [I/O] Input the rect the label to be positioned in, and output the label rect
1604
* imageRc [O] Optional, output the image rect
1605
* textRc [O] Optional, output the text rect
1606
*/
1607
static UINT BUTTON_CalcLayoutRects(const BUTTON_INFO *infoPtr, HDC hdc, RECT *labelRc, RECT *imageRc, RECT *textRc)
1608
{
1609
WCHAR *text = get_button_text(infoPtr);
1610
SIZE imageSize = BUTTON_GetImageSize(infoPtr);
1611
RECT labelRect, imageRect, imageRectWithMargin, textRect;
1612
LONG imageMarginWidth, imageMarginHeight;
1613
const RECT *textMargin = BUTTON_GetTextMargin(infoPtr);
1614
LONG style, ex_style, split_style;
1615
RECT emptyMargin = {0};
1616
LONG maxTextWidth;
1617
UINT dtStyle;
1618
1619
/* Calculate label rectangle according to label type */
1620
if ((imageSize.cx == 0 && imageSize.cy == 0) && (text == NULL || text[0] == '\0'))
1621
{
1622
SetRectEmpty(labelRc);
1623
SetRectEmpty(imageRc);
1624
SetRectEmpty(textRc);
1625
Free(text);
1626
return (UINT)-1;
1627
}
1628
1629
style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
1630
ex_style = GetWindowLongW(infoPtr->hwnd, GWL_EXSTYLE);
1631
/* Add BS_RIGHT directly. When both WS_EX_RIGHT and BS_LEFT are present, it becomes BS_CENTER */
1632
if (ex_style & WS_EX_RIGHT)
1633
style |= BS_RIGHT;
1634
split_style = infoPtr->imagelist.himl ? BUTTON_ILStoBS(infoPtr->imagelist.uAlign) : style;
1635
dtStyle = BUTTON_BStoDT(style, ex_style);
1636
1637
/* Group boxes are top aligned unless BS_PUSHLIKE is set and it's not themed */
1638
if (get_button_type(style) == BS_GROUPBOX
1639
&& (!(style & BS_PUSHLIKE) || GetWindowTheme(infoPtr->hwnd)))
1640
style &= ~BS_VCENTER | BS_TOP;
1641
1642
SetRect(&imageRect, 0, 0, imageSize.cx, imageSize.cy);
1643
imageRectWithMargin = imageRect;
1644
if (infoPtr->imagelist.himl)
1645
{
1646
imageRectWithMargin.top -= infoPtr->imagelist.margin.top;
1647
imageRectWithMargin.bottom += infoPtr->imagelist.margin.bottom;
1648
imageRectWithMargin.left -= infoPtr->imagelist.margin.left;
1649
imageRectWithMargin.right += infoPtr->imagelist.margin.right;
1650
}
1651
1652
/* Show image only */
1653
if (show_image_only(infoPtr))
1654
{
1655
BUTTON_PositionRect(style, labelRc, &imageRect,
1656
infoPtr->imagelist.himl ? &infoPtr->imagelist.margin : &emptyMargin);
1657
labelRect = imageRect;
1658
SetRectEmpty(&textRect);
1659
}
1660
else
1661
{
1662
/* Get text rect */
1663
maxTextWidth = labelRc->right - labelRc->left;
1664
textRect = BUTTON_GetTextRect(infoPtr, hdc, text, maxTextWidth);
1665
1666
/* Show image and text */
1667
if (show_image_and_text(infoPtr))
1668
{
1669
RECT boundingLabelRect, boundingImageRect, boundingTextRect;
1670
1671
/* Get label rect */
1672
/* Image list may have different alignment than the button, use the whole rect for label in this case */
1673
if (infoPtr->imagelist.himl)
1674
labelRect = *labelRc;
1675
else
1676
{
1677
/* Get a label bounding rectangle to position the label in the user specified label rectangle because
1678
* text and image need to align together. */
1679
boundingLabelRect = BUTTON_GetBoundingLabelRect(split_style, &textRect, &imageRectWithMargin);
1680
BUTTON_PositionRect(split_style, labelRc, &boundingLabelRect, &emptyMargin);
1681
labelRect = boundingLabelRect;
1682
}
1683
1684
/* When imagelist has center align, use the whole rect for imagelist and text */
1685
if(infoPtr->imagelist.himl && infoPtr->imagelist.uAlign == BUTTON_IMAGELIST_ALIGN_CENTER)
1686
{
1687
boundingImageRect = labelRect;
1688
boundingTextRect = labelRect;
1689
BUTTON_PositionRect(split_style, &boundingImageRect, &imageRect,
1690
infoPtr->imagelist.himl ? &infoPtr->imagelist.margin : &emptyMargin);
1691
/* Text doesn't use imagelist align */
1692
BUTTON_PositionRect(style, &boundingTextRect, &textRect, textMargin);
1693
}
1694
else
1695
{
1696
/* Get image rect */
1697
/* Split the label rect to two halves as two bounding rectangles for image and text */
1698
boundingImageRect = labelRect;
1699
imageMarginWidth = imageRectWithMargin.right - imageRectWithMargin.left;
1700
imageMarginHeight = imageRectWithMargin.bottom - imageRectWithMargin.top;
1701
if ((split_style & BS_CENTER) == BS_RIGHT)
1702
boundingImageRect.left = boundingImageRect.right - imageMarginWidth;
1703
else if ((split_style & BS_CENTER) == BS_LEFT)
1704
boundingImageRect.right = boundingImageRect.left + imageMarginWidth;
1705
else if ((split_style & BS_VCENTER) == BS_BOTTOM)
1706
boundingImageRect.top = boundingImageRect.bottom - imageMarginHeight;
1707
else if ((split_style & BS_VCENTER) == BS_TOP)
1708
boundingImageRect.bottom = boundingImageRect.top + imageMarginHeight;
1709
else
1710
boundingImageRect.right = boundingImageRect.left + imageMarginWidth;
1711
BUTTON_PositionRect(split_style, &boundingImageRect, &imageRect,
1712
infoPtr->imagelist.himl ? &infoPtr->imagelist.margin : &emptyMargin);
1713
1714
/* Get text rect */
1715
SubtractRect(&boundingTextRect, &labelRect, &boundingImageRect);
1716
/* Text doesn't use imagelist align */
1717
BUTTON_PositionRect(style, &boundingTextRect, &textRect, textMargin);
1718
}
1719
}
1720
/* Show text only */
1721
else
1722
{
1723
BUTTON_PositionRect(style, labelRc, &textRect, textMargin);
1724
labelRect = textRect;
1725
SetRectEmpty(&imageRect);
1726
}
1727
}
1728
Free(text);
1729
1730
CopyRect(labelRc, &labelRect);
1731
CopyRect(imageRc, &imageRect);
1732
CopyRect(textRc, &textRect);
1733
1734
return dtStyle;
1735
}
1736
1737
1738
/**********************************************************************
1739
* BUTTON_DrawImage
1740
*
1741
* Draw the button's image into the specified rectangle.
1742
*/
1743
static void BUTTON_DrawImage(const BUTTON_INFO *infoPtr, HDC hdc, HBRUSH hbr, UINT flags, const RECT *rect)
1744
{
1745
if (infoPtr->imagelist.himl)
1746
{
1747
int i = (ImageList_GetImageCount(infoPtr->imagelist.himl) == 1) ? 0 : get_draw_state(infoPtr) - 1;
1748
1749
ImageList_Draw(infoPtr->imagelist.himl, i, hdc, rect->left, rect->top, ILD_NORMAL);
1750
}
1751
else
1752
{
1753
switch (infoPtr->image_type)
1754
{
1755
case IMAGE_ICON:
1756
flags |= DST_ICON;
1757
break;
1758
case IMAGE_BITMAP:
1759
flags |= DST_BITMAP;
1760
break;
1761
default:
1762
return;
1763
}
1764
1765
DrawStateW(hdc, hbr, NULL, (LPARAM)infoPtr->u.image, 0, rect->left, rect->top,
1766
rect->right - rect->left, rect->bottom - rect->top, flags);
1767
}
1768
}
1769
1770
1771
/**********************************************************************
1772
* BUTTON_DrawTextCallback
1773
*
1774
* Callback function used by DrawStateW function.
1775
*/
1776
static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
1777
{
1778
RECT rc;
1779
1780
SetRect(&rc, 0, 0, cx, cy);
1781
DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
1782
return TRUE;
1783
}
1784
1785
/**********************************************************************
1786
* BUTTON_DrawLabel
1787
*
1788
* Common function for drawing button label.
1789
*
1790
* FIXME:
1791
* 1. When BS_SINGLELINE is specified and text contains '\t', '\n' or '\r' in the middle, they are rendered as
1792
* squares now whereas they should be ignored.
1793
* 2. When BS_MULTILINE is specified and text contains space in the middle, the space mistakenly be rendered as newline.
1794
*/
1795
static void BUTTON_DrawLabel(const BUTTON_INFO *infoPtr, HDC hdc, UINT dtFlags, const RECT *imageRect,
1796
const RECT *textRect)
1797
{
1798
HBRUSH hbr = 0;
1799
UINT flags = IsWindowEnabled(infoPtr->hwnd) ? DSS_NORMAL : DSS_DISABLED;
1800
LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE );
1801
WCHAR *text;
1802
1803
/* FIXME: To draw disabled label in Win31 look-and-feel, we probably
1804
* must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
1805
* I don't have Win31 on hand to verify that, so I leave it as is.
1806
*/
1807
1808
if ((style & BS_PUSHLIKE) && (infoPtr->state & BST_INDETERMINATE))
1809
{
1810
hbr = GetSysColorBrush(COLOR_GRAYTEXT);
1811
flags |= DSS_MONO;
1812
}
1813
1814
if (show_image(infoPtr)) BUTTON_DrawImage(infoPtr, hdc, hbr, flags, imageRect);
1815
if (show_image_only(infoPtr)) return;
1816
1817
/* DST_COMPLEX -- is 0 */
1818
if (!(text = get_button_text(infoPtr))) return;
1819
DrawStateW(hdc, hbr, BUTTON_DrawTextCallback, (LPARAM)text, dtFlags, textRect->left, textRect->top,
1820
textRect->right - textRect->left, textRect->bottom - textRect->top, flags);
1821
Free(text);
1822
}
1823
1824
static void BUTTON_DrawThemedLabel(const BUTTON_INFO *info, HDC hdc, UINT text_flags,
1825
const RECT *image_rect, const RECT *text_rect, HTHEME theme,
1826
int part, int state)
1827
{
1828
HBRUSH brush = NULL;
1829
UINT image_flags;
1830
WCHAR *text;
1831
1832
if (show_image(info))
1833
{
1834
image_flags = IsWindowEnabled(info->hwnd) ? DSS_NORMAL : DSS_DISABLED;
1835
1836
if ((GetWindowLongW(info->hwnd, GWL_STYLE) & BS_PUSHLIKE)
1837
&& (info->state & BST_INDETERMINATE))
1838
{
1839
brush = GetSysColorBrush(COLOR_GRAYTEXT);
1840
image_flags |= DSS_MONO;
1841
}
1842
1843
BUTTON_DrawImage(info, hdc, brush, image_flags, image_rect);
1844
}
1845
1846
if (show_image_only(info))
1847
return;
1848
1849
if (!(text = get_button_text(info)))
1850
return;
1851
1852
DrawThemeText(theme, hdc, part, state, text, lstrlenW(text), text_flags, 0, text_rect);
1853
Free(text);
1854
}
1855
1856
/**********************************************************************
1857
* Push Button Functions
1858
*/
1859
static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
1860
{
1861
RECT rc, labelRect, imageRect, textRect;
1862
UINT dtFlags = (UINT)-1, uState;
1863
HPEN hOldPen, hpen;
1864
HBRUSH hOldBrush;
1865
INT oldBkMode;
1866
COLORREF oldTxtColor;
1867
LRESULT cdrf;
1868
HFONT hFont;
1869
NMCUSTOMDRAW nmcd;
1870
LONG state = infoPtr->state;
1871
LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE );
1872
BOOL pushedState = (state & BST_PUSHED);
1873
HWND parent;
1874
HRGN hrgn;
1875
1876
GetClientRect( infoPtr->hwnd, &rc );
1877
1878
/* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
1879
if ((hFont = infoPtr->font)) SelectObject( hDC, hFont );
1880
parent = GetParent(infoPtr->hwnd);
1881
if (!parent) parent = infoPtr->hwnd;
1882
SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd );
1883
1884
hrgn = set_control_clipping( hDC, &rc );
1885
1886
hpen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
1887
hOldPen = SelectObject(hDC, hpen);
1888
hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
1889
oldBkMode = SetBkMode(hDC, TRANSPARENT);
1890
1891
init_custom_draw(&nmcd, infoPtr, hDC, &rc);
1892
1893
/* Send erase notifications */
1894
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
1895
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
1896
1897
if (get_button_type(style) == BS_DEFPUSHBUTTON)
1898
{
1899
if (action != ODA_FOCUS)
1900
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
1901
InflateRect( &rc, -1, -1 );
1902
}
1903
1904
/* Skip the frame drawing if only focus has changed */
1905
if (action != ODA_FOCUS)
1906
{
1907
uState = DFCS_BUTTONPUSH;
1908
1909
if (style & BS_FLAT)
1910
uState |= DFCS_MONO;
1911
else if (pushedState)
1912
{
1913
if (get_button_type(style) == BS_DEFPUSHBUTTON )
1914
uState |= DFCS_FLAT;
1915
else
1916
uState |= DFCS_PUSHED;
1917
}
1918
1919
if (state & (BST_CHECKED | BST_INDETERMINATE))
1920
uState |= DFCS_CHECKED;
1921
1922
DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
1923
}
1924
1925
if (cdrf & CDRF_NOTIFYPOSTERASE)
1926
{
1927
nmcd.dwDrawStage = CDDS_POSTERASE;
1928
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
1929
}
1930
1931
/* Send paint notifications */
1932
nmcd.dwDrawStage = CDDS_PREPAINT;
1933
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
1934
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
1935
1936
if (!(cdrf & CDRF_DOERASE) && action != ODA_FOCUS)
1937
{
1938
/* draw button label */
1939
labelRect = rc;
1940
/* Shrink label rect at all sides by 2 so that the content won't touch the surrounding frame */
1941
InflateRect(&labelRect, -2, -2);
1942
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
1943
1944
if (dtFlags != (UINT)-1L)
1945
{
1946
if (pushedState) OffsetRect(&labelRect, 1, 1);
1947
1948
oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
1949
1950
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
1951
1952
SetTextColor( hDC, oldTxtColor );
1953
}
1954
}
1955
1956
if (cdrf & CDRF_NOTIFYPOSTPAINT)
1957
{
1958
nmcd.dwDrawStage = CDDS_POSTPAINT;
1959
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
1960
}
1961
if ((cdrf & CDRF_SKIPPOSTPAINT) || dtFlags == (UINT)-1L) goto cleanup;
1962
1963
if (action == ODA_FOCUS || (state & BST_FOCUS))
1964
{
1965
InflateRect( &rc, -2, -2 );
1966
DrawFocusRect( hDC, &rc );
1967
}
1968
1969
cleanup:
1970
SelectObject( hDC, hOldPen );
1971
SelectObject( hDC, hOldBrush );
1972
SetBkMode(hDC, oldBkMode);
1973
SelectClipRgn( hDC, hrgn );
1974
if (hrgn) DeleteObject( hrgn );
1975
DeleteObject( hpen );
1976
}
1977
1978
/**********************************************************************
1979
* Check Box & Radio Button Functions
1980
*/
1981
1982
/* Get adjusted check box or radio box rectangle */
1983
static RECT get_box_rect(LONG style, LONG ex_style, const RECT *content_rect,
1984
const RECT *label_rect, BOOL has_label, SIZE box_size)
1985
{
1986
RECT rect;
1987
int delta;
1988
1989
rect = *content_rect;
1990
1991
if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT)
1992
rect.left = rect.right - box_size.cx;
1993
else
1994
rect.right = rect.left + box_size.cx;
1995
1996
/* Adjust box when label is valid */
1997
if (has_label)
1998
{
1999
rect.top = label_rect->top;
2000
rect.bottom = label_rect->bottom;
2001
}
2002
2003
/* Box must have the correct height */
2004
delta = rect.bottom - rect.top - box_size.cy;
2005
if ((style & BS_VCENTER) == BS_TOP)
2006
{
2007
if (delta <= 0)
2008
rect.top -= -delta / 2 + 1;
2009
2010
rect.bottom = rect.top + box_size.cy;
2011
}
2012
else if ((style & BS_VCENTER) == BS_BOTTOM)
2013
{
2014
if (delta <= 0)
2015
rect.bottom += -delta / 2 + 1;
2016
2017
rect.top = rect.bottom - box_size.cy;
2018
}
2019
else
2020
{
2021
if (delta > 0)
2022
{
2023
rect.bottom -= delta / 2 + 1;
2024
rect.top = rect.bottom - box_size.cy;
2025
}
2026
else if (delta < 0)
2027
{
2028
rect.top -= -delta / 2 + 1;
2029
rect.bottom = rect.top + box_size.cy;
2030
}
2031
}
2032
2033
return rect;
2034
}
2035
2036
static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
2037
{
2038
RECT rbox, labelRect, oldLabelRect, imageRect, textRect, client;
2039
HBRUSH hBrush;
2040
int text_offset;
2041
UINT dtFlags;
2042
LRESULT cdrf;
2043
HFONT hFont;
2044
NMCUSTOMDRAW nmcd;
2045
LONG state = infoPtr->state;
2046
LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE );
2047
LONG ex_style = GetWindowLongW( infoPtr->hwnd, GWL_EXSTYLE );
2048
SIZE box_size;
2049
HWND parent;
2050
HRGN hrgn;
2051
2052
if (style & BS_PUSHLIKE)
2053
{
2054
PB_Paint( infoPtr, hDC, action );
2055
return;
2056
}
2057
2058
GetClientRect(infoPtr->hwnd, &client);
2059
labelRect = client;
2060
2061
box_size.cx = 12 * GetDpiForWindow(infoPtr->hwnd) / 96 + 1;
2062
box_size.cy = box_size.cx;
2063
2064
if ((hFont = infoPtr->font)) SelectObject( hDC, hFont );
2065
GetCharWidthW( hDC, '0', '0', &text_offset );
2066
text_offset /= 2;
2067
2068
parent = GetParent(infoPtr->hwnd);
2069
if (!parent) parent = infoPtr->hwnd;
2070
hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2071
if (!hBrush) /* did the app forget to call defwindowproc ? */
2072
hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2073
hrgn = set_control_clipping( hDC, &client );
2074
2075
if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT)
2076
labelRect.right -= box_size.cx + text_offset;
2077
else
2078
labelRect.left += box_size.cx + text_offset;
2079
2080
oldLabelRect = labelRect;
2081
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
2082
rbox = get_box_rect(style, ex_style, &client, &labelRect, dtFlags != (UINT)-1L, box_size);
2083
2084
init_custom_draw(&nmcd, infoPtr, hDC, &client);
2085
2086
/* Send erase notifications */
2087
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2088
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2089
2090
/* Since WM_ERASEBKGND does nothing, first prepare background */
2091
if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
2092
if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush );
2093
if (cdrf & CDRF_NOTIFYPOSTERASE)
2094
{
2095
nmcd.dwDrawStage = CDDS_POSTERASE;
2096
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2097
}
2098
2099
/* Draw label */
2100
/* Send paint notifications */
2101
nmcd.dwDrawStage = CDDS_PREPAINT;
2102
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2103
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2104
2105
/* Draw the check-box bitmap */
2106
if (!(cdrf & CDRF_DOERASE))
2107
{
2108
if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
2109
{
2110
UINT flags;
2111
2112
if ((get_button_type(style) == BS_RADIOBUTTON) ||
2113
(get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO;
2114
else if (state & BST_INDETERMINATE) flags = DFCS_BUTTON3STATE;
2115
else flags = DFCS_BUTTONCHECK;
2116
2117
if (state & (BST_CHECKED | BST_INDETERMINATE)) flags |= DFCS_CHECKED;
2118
if (state & BST_PUSHED) flags |= DFCS_PUSHED;
2119
if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
2120
2121
DrawFrameControl(hDC, &rbox, DFC_BUTTON, flags);
2122
}
2123
2124
if (dtFlags != (UINT)-1L) /* Something to draw */
2125
if (action == ODA_DRAWENTIRE) BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
2126
}
2127
2128
if (cdrf & CDRF_NOTIFYPOSTPAINT)
2129
{
2130
nmcd.dwDrawStage = CDDS_POSTPAINT;
2131
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2132
}
2133
if ((cdrf & CDRF_SKIPPOSTPAINT) || dtFlags == (UINT)-1L) goto cleanup;
2134
2135
/* ... and focus */
2136
if (action == ODA_FOCUS || (state & BST_FOCUS))
2137
{
2138
labelRect.left--;
2139
labelRect.right++;
2140
IntersectRect(&labelRect, &labelRect, &oldLabelRect);
2141
DrawFocusRect(hDC, &labelRect);
2142
}
2143
2144
cleanup:
2145
SelectClipRgn( hDC, hrgn );
2146
if (hrgn) DeleteObject( hrgn );
2147
}
2148
2149
2150
/**********************************************************************
2151
* BUTTON_CheckAutoRadioButton
2152
*
2153
* hwnd is checked, uncheck every other auto radio button in group
2154
*/
2155
static void BUTTON_CheckAutoRadioButton( HWND hwnd )
2156
{
2157
HWND parent, sibling, start;
2158
2159
parent = GetParent(hwnd);
2160
/* make sure that starting control is not disabled or invisible */
2161
start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE );
2162
do
2163
{
2164
if (!sibling) break;
2165
if ((hwnd != sibling) &&
2166
((GetWindowLongW( sibling, GWL_STYLE) & BS_TYPEMASK) == BS_AUTORADIOBUTTON))
2167
SendMessageW( sibling, BM_SETCHECK, BST_UNCHECKED, 0 );
2168
sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
2169
} while (sibling != start);
2170
}
2171
2172
2173
/**********************************************************************
2174
* Group Box Functions
2175
*/
2176
2177
static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
2178
{
2179
RECT labelRect, imageRect, textRect, rcFrame;
2180
HBRUSH hbr;
2181
HFONT hFont;
2182
UINT dtFlags;
2183
TEXTMETRICW tm;
2184
LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE );
2185
HWND parent;
2186
HRGN hrgn;
2187
2188
if ((hFont = infoPtr->font)) SelectObject( hDC, hFont );
2189
/* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
2190
parent = GetParent(infoPtr->hwnd);
2191
if (!parent) parent = infoPtr->hwnd;
2192
hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2193
if (!hbr) /* did the app forget to call defwindowproc ? */
2194
hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2195
GetClientRect(infoPtr->hwnd, &labelRect);
2196
rcFrame = labelRect;
2197
hrgn = set_control_clipping(hDC, &labelRect);
2198
2199
GetTextMetricsW (hDC, &tm);
2200
rcFrame.top += (tm.tmHeight / 2) - 1;
2201
DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
2202
2203
InflateRect(&labelRect, -7, 1);
2204
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
2205
2206
if (dtFlags != (UINT)-1)
2207
{
2208
/* Because buttons have CS_PARENTDC class style, there is a chance
2209
* that label will be drawn out of client rect.
2210
* But Windows doesn't clip label's rect, so do I.
2211
*/
2212
2213
/* There is 1-pixel margin at the left, right, and bottom */
2214
labelRect.left--;
2215
labelRect.right++;
2216
labelRect.bottom++;
2217
FillRect(hDC, &labelRect, hbr);
2218
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
2219
}
2220
SelectClipRgn( hDC, hrgn );
2221
if (hrgn) DeleteObject( hrgn );
2222
}
2223
2224
2225
/**********************************************************************
2226
* User Button Functions
2227
*/
2228
2229
static void UB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
2230
{
2231
RECT rc;
2232
HBRUSH hBrush;
2233
LRESULT cdrf;
2234
HFONT hFont;
2235
NMCUSTOMDRAW nmcd;
2236
LONG state = infoPtr->state;
2237
HWND parent;
2238
2239
GetClientRect( infoPtr->hwnd, &rc);
2240
2241
if ((hFont = infoPtr->font)) SelectObject( hDC, hFont );
2242
2243
parent = GetParent(infoPtr->hwnd);
2244
if (!parent) parent = infoPtr->hwnd;
2245
hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2246
if (!hBrush) /* did the app forget to call defwindowproc ? */
2247
hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2248
2249
if (action == ODA_FOCUS || (state & BST_FOCUS))
2250
{
2251
init_custom_draw(&nmcd, infoPtr, hDC, &rc);
2252
2253
/* Send erase notifications */
2254
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2255
if (cdrf & CDRF_SKIPDEFAULT) goto notify;
2256
}
2257
2258
FillRect( hDC, &rc, hBrush );
2259
if (action == ODA_FOCUS || (state & BST_FOCUS))
2260
{
2261
if (cdrf & CDRF_NOTIFYPOSTERASE)
2262
{
2263
nmcd.dwDrawStage = CDDS_POSTERASE;
2264
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2265
}
2266
2267
/* Send paint notifications */
2268
nmcd.dwDrawStage = CDDS_PREPAINT;
2269
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2270
if (cdrf & CDRF_SKIPDEFAULT) goto notify;
2271
if (cdrf & CDRF_NOTIFYPOSTPAINT)
2272
{
2273
nmcd.dwDrawStage = CDDS_POSTPAINT;
2274
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2275
}
2276
2277
if (!(cdrf & CDRF_SKIPPOSTPAINT))
2278
DrawFocusRect( hDC, &rc );
2279
}
2280
2281
notify:
2282
switch (action)
2283
{
2284
case ODA_FOCUS:
2285
BUTTON_NOTIFY_PARENT( infoPtr->hwnd, (state & BST_FOCUS) ? BN_SETFOCUS : BN_KILLFOCUS );
2286
break;
2287
2288
case ODA_SELECT:
2289
BUTTON_NOTIFY_PARENT( infoPtr->hwnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE );
2290
break;
2291
2292
default:
2293
break;
2294
}
2295
}
2296
2297
2298
/**********************************************************************
2299
* Ownerdrawn Button Functions
2300
*/
2301
2302
static void OB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
2303
{
2304
LONG state = infoPtr->state;
2305
DRAWITEMSTRUCT dis;
2306
LONG_PTR id = GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID );
2307
HWND parent;
2308
HFONT hFont;
2309
HRGN hrgn;
2310
2311
dis.CtlType = ODT_BUTTON;
2312
dis.CtlID = id;
2313
dis.itemID = 0;
2314
dis.itemAction = action;
2315
dis.itemState = ((state & BST_FOCUS) ? ODS_FOCUS : 0) |
2316
((state & BST_PUSHED) ? ODS_SELECTED : 0) |
2317
(IsWindowEnabled(infoPtr->hwnd) ? 0: ODS_DISABLED);
2318
dis.hwndItem = infoPtr->hwnd;
2319
dis.hDC = hDC;
2320
dis.itemData = 0;
2321
GetClientRect( infoPtr->hwnd, &dis.rcItem );
2322
2323
if ((hFont = infoPtr->font)) SelectObject( hDC, hFont );
2324
parent = GetParent(infoPtr->hwnd);
2325
if (!parent) parent = infoPtr->hwnd;
2326
SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd );
2327
2328
hrgn = set_control_clipping( hDC, &dis.rcItem );
2329
2330
SendMessageW( GetParent(infoPtr->hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
2331
SelectClipRgn( hDC, hrgn );
2332
if (hrgn) DeleteObject( hrgn );
2333
}
2334
2335
2336
/**********************************************************************
2337
* Split Button Functions
2338
*/
2339
static void SB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
2340
{
2341
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2342
LONG state = infoPtr->state;
2343
UINT dtFlags = (UINT)-1L;
2344
2345
RECT rc, push_rect, dropdown_rect;
2346
NMCUSTOMDRAW nmcd;
2347
HPEN pen, old_pen;
2348
HBRUSH old_brush;
2349
INT old_bk_mode;
2350
LRESULT cdrf;
2351
HWND parent;
2352
HRGN hrgn;
2353
2354
GetClientRect(infoPtr->hwnd, &rc);
2355
2356
/* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
2357
if (infoPtr->font) SelectObject(hDC, infoPtr->font);
2358
if (!(parent = GetParent(infoPtr->hwnd))) parent = infoPtr->hwnd;
2359
SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2360
2361
hrgn = set_control_clipping(hDC, &rc);
2362
2363
pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
2364
old_pen = SelectObject(hDC, pen);
2365
old_brush = SelectObject(hDC, GetSysColorBrush(COLOR_BTNFACE));
2366
old_bk_mode = SetBkMode(hDC, TRANSPARENT);
2367
2368
init_custom_draw(&nmcd, infoPtr, hDC, &rc);
2369
2370
/* Send erase notifications */
2371
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2372
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2373
2374
if (get_button_type(style) == BS_DEFSPLITBUTTON)
2375
{
2376
if (action != ODA_FOCUS)
2377
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
2378
InflateRect(&rc, -1, -1);
2379
/* The split will now be off by 1 pixel, but
2380
that's exactly what Windows does as well */
2381
}
2382
2383
get_split_button_rects(infoPtr, &rc, &push_rect, &dropdown_rect);
2384
if (infoPtr->split_style & BCSS_NOSPLIT)
2385
push_rect = rc;
2386
2387
/* Skip the frame drawing if only focus has changed */
2388
if (action != ODA_FOCUS)
2389
{
2390
UINT flags = DFCS_BUTTONPUSH;
2391
2392
if (style & BS_FLAT) flags |= DFCS_MONO;
2393
else if (state & BST_PUSHED)
2394
flags |= (get_button_type(style) == BS_DEFSPLITBUTTON)
2395
? DFCS_FLAT : DFCS_PUSHED;
2396
2397
if (state & (BST_CHECKED | BST_INDETERMINATE))
2398
flags |= DFCS_CHECKED;
2399
2400
if (infoPtr->split_style & BCSS_NOSPLIT)
2401
DrawFrameControl(hDC, &push_rect, DFC_BUTTON, flags);
2402
else
2403
{
2404
UINT dropdown_flags = flags & ~DFCS_CHECKED;
2405
2406
if (state & BST_DROPDOWNPUSHED)
2407
dropdown_flags = (dropdown_flags & ~DFCS_FLAT) | DFCS_PUSHED;
2408
2409
/* Adjust for shadow and draw order so it looks properly */
2410
if (infoPtr->split_style & BCSS_ALIGNLEFT)
2411
{
2412
dropdown_rect.right++;
2413
DrawFrameControl(hDC, &dropdown_rect, DFC_BUTTON, dropdown_flags);
2414
dropdown_rect.right--;
2415
DrawFrameControl(hDC, &push_rect, DFC_BUTTON, flags);
2416
}
2417
else
2418
{
2419
push_rect.right++;
2420
DrawFrameControl(hDC, &push_rect, DFC_BUTTON, flags);
2421
push_rect.right--;
2422
DrawFrameControl(hDC, &dropdown_rect, DFC_BUTTON, dropdown_flags);
2423
}
2424
}
2425
}
2426
2427
if (cdrf & CDRF_NOTIFYPOSTERASE)
2428
{
2429
nmcd.dwDrawStage = CDDS_POSTERASE;
2430
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2431
}
2432
2433
/* Send paint notifications */
2434
nmcd.dwDrawStage = CDDS_PREPAINT;
2435
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2436
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2437
2438
/* Shrink push button rect so that the content won't touch the surrounding frame */
2439
InflateRect(&push_rect, -2, -2);
2440
2441
if (!(cdrf & CDRF_DOERASE) && action != ODA_FOCUS)
2442
{
2443
COLORREF old_color = SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT));
2444
RECT label_rect = push_rect, image_rect, text_rect;
2445
2446
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &label_rect, &image_rect, &text_rect);
2447
2448
if (dtFlags != (UINT)-1L)
2449
BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &image_rect, &text_rect);
2450
2451
draw_split_button_dropdown_glyph(infoPtr, hDC, &dropdown_rect);
2452
SetTextColor(hDC, old_color);
2453
}
2454
2455
if (cdrf & CDRF_NOTIFYPOSTPAINT)
2456
{
2457
nmcd.dwDrawStage = CDDS_POSTPAINT;
2458
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2459
}
2460
if ((cdrf & CDRF_SKIPPOSTPAINT) || dtFlags == (UINT)-1L) goto cleanup;
2461
2462
if (action == ODA_FOCUS || (state & BST_FOCUS))
2463
DrawFocusRect(hDC, &push_rect);
2464
2465
cleanup:
2466
SelectObject(hDC, old_pen);
2467
SelectObject(hDC, old_brush);
2468
SetBkMode(hDC, old_bk_mode);
2469
SelectClipRgn(hDC, hrgn);
2470
if (hrgn) DeleteObject(hrgn);
2471
DeleteObject(pen);
2472
}
2473
2474
/* Given the full button rect of the split button, retrieve the push part and the dropdown part */
2475
static inline void get_split_button_rects(const BUTTON_INFO *infoPtr, const RECT *button_rect,
2476
RECT *push_rect, RECT *dropdown_rect)
2477
{
2478
*push_rect = *dropdown_rect = *button_rect;
2479
2480
/* The dropdown takes priority if the client rect is too small, it will only have a dropdown */
2481
if (infoPtr->split_style & BCSS_ALIGNLEFT)
2482
{
2483
dropdown_rect->right = min(button_rect->left + infoPtr->glyph_size.cx, button_rect->right);
2484
push_rect->left = dropdown_rect->right;
2485
}
2486
else
2487
{
2488
dropdown_rect->left = max(button_rect->right - infoPtr->glyph_size.cx, button_rect->left);
2489
push_rect->right = dropdown_rect->left;
2490
}
2491
}
2492
2493
/* Notify the parent if the point is within the dropdown and return TRUE (always notify if NULL) */
2494
static BOOL notify_split_button_dropdown(const BUTTON_INFO *infoPtr, const POINT *pt, HWND hwnd)
2495
{
2496
NMBCDROPDOWN nmbcd;
2497
2498
GetClientRect(hwnd, &nmbcd.rcButton);
2499
if (pt)
2500
{
2501
RECT push_rect, dropdown_rect;
2502
2503
get_split_button_rects(infoPtr, &nmbcd.rcButton, &push_rect, &dropdown_rect);
2504
if (!PtInRect(&dropdown_rect, *pt))
2505
return FALSE;
2506
2507
/* If it's already down (set manually via BCM_SETDROPDOWNSTATE), fake the notify */
2508
if (infoPtr->state & BST_DROPDOWNPUSHED)
2509
return TRUE;
2510
}
2511
SendMessageW(hwnd, BCM_SETDROPDOWNSTATE, TRUE, 0);
2512
2513
nmbcd.hdr.hwndFrom = hwnd;
2514
nmbcd.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
2515
nmbcd.hdr.code = BCN_DROPDOWN;
2516
SendMessageW(GetParent(hwnd), WM_NOTIFY, nmbcd.hdr.idFrom, (LPARAM)&nmbcd);
2517
2518
SendMessageW(hwnd, BCM_SETDROPDOWNSTATE, FALSE, 0);
2519
return TRUE;
2520
}
2521
2522
/* Draw the split button dropdown glyph or image */
2523
static void draw_split_button_dropdown_glyph(const BUTTON_INFO *infoPtr, HDC hdc, RECT *rect)
2524
{
2525
if (infoPtr->split_style & BCSS_IMAGE)
2526
{
2527
int w, h;
2528
2529
/* When the glyph is an image list, Windows is very buggy with BCSS_STRETCH,
2530
positions it weirdly and doesn't even stretch it, but instead extends the
2531
image, leaking into other images in the list (or black if none). Instead,
2532
we'll ignore this and just position it at center as without BCSS_STRETCH. */
2533
if (!ImageList_GetIconSize(infoPtr->glyph, &w, &h)) return;
2534
2535
ImageList_Draw(infoPtr->glyph,
2536
(ImageList_GetImageCount(infoPtr->glyph) == 1) ? 0 : get_draw_state(infoPtr) - 1,
2537
hdc, rect->left + (rect->right - rect->left - w) / 2,
2538
rect->top + (rect->bottom - rect->top - h) / 2, ILD_NORMAL);
2539
}
2540
else if (infoPtr->glyph_size.cy >= 0)
2541
{
2542
/* infoPtr->glyph is a character code from Marlett */
2543
HFONT font, old_font;
2544
LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
2545
L"Marlett" };
2546
if (infoPtr->glyph_size.cy)
2547
{
2548
/* BCSS_STRETCH preserves aspect ratio, uses minimum as size */
2549
if (infoPtr->split_style & BCSS_STRETCH)
2550
logfont.lfHeight = min(infoPtr->glyph_size.cx, infoPtr->glyph_size.cy);
2551
else
2552
{
2553
logfont.lfWidth = infoPtr->glyph_size.cx;
2554
logfont.lfHeight = infoPtr->glyph_size.cy;
2555
}
2556
}
2557
else logfont.lfHeight = infoPtr->glyph_size.cx;
2558
2559
if ((font = CreateFontIndirectW(&logfont)))
2560
{
2561
old_font = SelectObject(hdc, font);
2562
DrawTextW(hdc, (const WCHAR*)&infoPtr->glyph, 1, rect,
2563
DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP | DT_NOPREFIX);
2564
SelectObject(hdc, old_font);
2565
DeleteObject(font);
2566
}
2567
}
2568
}
2569
2570
2571
/**********************************************************************
2572
* Command Link Functions
2573
*/
2574
static void CL_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
2575
{
2576
LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2577
LONG state = infoPtr->state;
2578
2579
RECT rc, content_rect;
2580
NMCUSTOMDRAW nmcd;
2581
HPEN pen, old_pen;
2582
HBRUSH old_brush;
2583
INT old_bk_mode;
2584
LRESULT cdrf;
2585
HWND parent;
2586
HRGN hrgn;
2587
2588
GetClientRect(infoPtr->hwnd, &rc);
2589
2590
/* Command Links are not affected by the button's font, and are based
2591
on the default message font. Furthermore, they are not affected by
2592
any of the alignment styles (and always align with the top-left). */
2593
if (!(parent = GetParent(infoPtr->hwnd))) parent = infoPtr->hwnd;
2594
SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2595
2596
hrgn = set_control_clipping(hDC, &rc);
2597
2598
pen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
2599
old_pen = SelectObject(hDC, pen);
2600
old_brush = SelectObject(hDC, GetSysColorBrush(COLOR_BTNFACE));
2601
old_bk_mode = SetBkMode(hDC, TRANSPARENT);
2602
2603
init_custom_draw(&nmcd, infoPtr, hDC, &rc);
2604
2605
/* Send erase notifications */
2606
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2607
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2608
content_rect = rc;
2609
2610
if (get_button_type(style) == BS_DEFCOMMANDLINK)
2611
{
2612
if (action != ODA_FOCUS)
2613
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
2614
InflateRect(&rc, -1, -1);
2615
}
2616
2617
/* Skip the frame drawing if only focus has changed */
2618
if (action != ODA_FOCUS)
2619
{
2620
if (!(state & (BST_HOT | BST_PUSHED | BST_CHECKED | BST_INDETERMINATE)))
2621
FillRect(hDC, &rc, GetSysColorBrush(COLOR_BTNFACE));
2622
else
2623
{
2624
UINT flags = DFCS_BUTTONPUSH;
2625
2626
if (style & BS_FLAT) flags |= DFCS_MONO;
2627
else if (state & BST_PUSHED) flags |= DFCS_PUSHED;
2628
2629
if (state & (BST_CHECKED | BST_INDETERMINATE))
2630
flags |= DFCS_CHECKED;
2631
DrawFrameControl(hDC, &rc, DFC_BUTTON, flags);
2632
}
2633
}
2634
2635
if (cdrf & CDRF_NOTIFYPOSTERASE)
2636
{
2637
nmcd.dwDrawStage = CDDS_POSTERASE;
2638
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2639
}
2640
2641
/* Send paint notifications */
2642
nmcd.dwDrawStage = CDDS_PREPAINT;
2643
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2644
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2645
2646
if (!(cdrf & CDRF_DOERASE) && action != ODA_FOCUS)
2647
{
2648
UINT flags = IsWindowEnabled(infoPtr->hwnd) ? DSS_NORMAL : DSS_DISABLED;
2649
COLORREF old_color = SetTextColor(hDC, GetSysColor(flags == DSS_NORMAL ?
2650
COLOR_BTNTEXT : COLOR_GRAYTEXT));
2651
HIMAGELIST defimg = NULL;
2652
NONCLIENTMETRICSW ncm;
2653
UINT txt_h = 0;
2654
SIZE img_size;
2655
2656
/* Command Links ignore the margins of the image list or its alignment */
2657
if (infoPtr->u.image || infoPtr->imagelist.himl)
2658
img_size = BUTTON_GetImageSize(infoPtr);
2659
else
2660
{
2661
img_size.cx = img_size.cy = command_link_defglyph_size;
2662
defimg = ImageList_LoadImageW(COMCTL32_hModule, (LPCWSTR)MAKEINTRESOURCE(IDB_CMDLINK),
2663
img_size.cx, 3, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION);
2664
}
2665
2666
/* Shrink rect by the command link margin, except on bottom (just the frame) */
2667
InflateRect(&content_rect, -command_link_margin, -command_link_margin);
2668
content_rect.bottom += command_link_margin - 2;
2669
2670
ncm.cbSize = sizeof(ncm);
2671
if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2672
{
2673
LONG note_weight = ncm.lfMessageFont.lfWeight;
2674
RECT r = content_rect;
2675
WCHAR *text;
2676
HFONT font;
2677
2678
if (img_size.cx) r.left += img_size.cx + command_link_margin;
2679
2680
/* Draw the text */
2681
ncm.lfMessageFont.lfWeight = FW_BOLD;
2682
if ((font = CreateFontIndirectW(&ncm.lfMessageFont)))
2683
{
2684
if ((text = get_button_text(infoPtr)))
2685
{
2686
SelectObject(hDC, font);
2687
txt_h = DrawTextW(hDC, text, -1, &r,
2688
DT_TOP | DT_LEFT | DT_WORDBREAK | DT_END_ELLIPSIS);
2689
Free(text);
2690
}
2691
DeleteObject(font);
2692
}
2693
2694
/* Draw the note */
2695
ncm.lfMessageFont.lfWeight = note_weight;
2696
if (infoPtr->note && (font = CreateFontIndirectW(&ncm.lfMessageFont)))
2697
{
2698
r.top += txt_h + 2;
2699
SelectObject(hDC, font);
2700
DrawTextW(hDC, infoPtr->note, infoPtr->note_length, &r,
2701
DT_TOP | DT_LEFT | DT_WORDBREAK | DT_NOPREFIX);
2702
DeleteObject(font);
2703
}
2704
}
2705
2706
/* Position the image at the vertical center of the drawn text (not note) */
2707
txt_h = min(txt_h, content_rect.bottom - content_rect.top);
2708
if (img_size.cy < txt_h) content_rect.top += (txt_h - img_size.cy) / 2;
2709
2710
content_rect.right = content_rect.left + img_size.cx;
2711
content_rect.bottom = content_rect.top + img_size.cy;
2712
2713
if (defimg)
2714
{
2715
int i = 0;
2716
if (flags == DSS_DISABLED) i = 2;
2717
else if (state & BST_HOT) i = 1;
2718
2719
ImageList_Draw(defimg, i, hDC, content_rect.left, content_rect.top, ILD_NORMAL);
2720
ImageList_Destroy(defimg);
2721
}
2722
else
2723
BUTTON_DrawImage(infoPtr, hDC, NULL, flags, &content_rect);
2724
2725
SetTextColor(hDC, old_color);
2726
}
2727
2728
if (cdrf & CDRF_NOTIFYPOSTPAINT)
2729
{
2730
nmcd.dwDrawStage = CDDS_POSTPAINT;
2731
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2732
}
2733
if (cdrf & CDRF_SKIPPOSTPAINT) goto cleanup;
2734
2735
if (action == ODA_FOCUS || (state & BST_FOCUS))
2736
{
2737
InflateRect(&rc, -2, -2);
2738
DrawFocusRect(hDC, &rc);
2739
}
2740
2741
cleanup:
2742
SelectObject(hDC, old_pen);
2743
SelectObject(hDC, old_brush);
2744
SetBkMode(hDC, old_bk_mode);
2745
SelectClipRgn(hDC, hrgn);
2746
if (hrgn) DeleteObject(hrgn);
2747
DeleteObject(pen);
2748
}
2749
2750
2751
/**********************************************************************
2752
* Themed Paint Functions
2753
*/
2754
static void PB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, int state, UINT dtFlags, BOOL focused)
2755
{
2756
RECT bgRect, labelRect, imageRect, textRect, focusRect;
2757
NMCUSTOMDRAW nmcd;
2758
HBRUSH brush;
2759
LRESULT cdrf;
2760
HWND parent;
2761
2762
if (infoPtr->font) SelectObject(hDC, infoPtr->font);
2763
2764
GetClientRect(infoPtr->hwnd, &bgRect);
2765
GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &bgRect, &labelRect);
2766
focusRect = labelRect;
2767
2768
init_custom_draw(&nmcd, infoPtr, hDC, &bgRect);
2769
2770
parent = GetParent(infoPtr->hwnd);
2771
if (!parent) parent = infoPtr->hwnd;
2772
2773
/* Send erase notifications */
2774
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2775
if (cdrf & CDRF_SKIPDEFAULT) return;
2776
2777
if (IsThemeBackgroundPartiallyTransparent(theme, BP_PUSHBUTTON, state))
2778
{
2779
DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL);
2780
/* Tests show that the brush from WM_CTLCOLORBTN is used for filling background after a
2781
* DrawThemeParentBackground() call */
2782
brush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2783
FillRect(hDC, &bgRect, brush ? brush : GetSysColorBrush(COLOR_BTNFACE));
2784
}
2785
DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, state, &bgRect, NULL);
2786
2787
if (cdrf & CDRF_NOTIFYPOSTERASE)
2788
{
2789
nmcd.dwDrawStage = CDDS_POSTERASE;
2790
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2791
}
2792
2793
/* Send paint notifications */
2794
nmcd.dwDrawStage = CDDS_PREPAINT;
2795
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2796
if (cdrf & CDRF_SKIPDEFAULT) return;
2797
2798
if (!(cdrf & CDRF_DOERASE))
2799
{
2800
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
2801
if (dtFlags != (UINT)-1L)
2802
BUTTON_DrawThemedLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect, theme,
2803
BP_PUSHBUTTON, state);
2804
}
2805
2806
if (cdrf & CDRF_NOTIFYPOSTPAINT)
2807
{
2808
nmcd.dwDrawStage = CDDS_POSTPAINT;
2809
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2810
}
2811
if (cdrf & CDRF_SKIPPOSTPAINT) return;
2812
2813
if (focused) DrawFocusRect(hDC, &focusRect);
2814
}
2815
2816
static void CB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, int state, UINT dtFlags, BOOL focused)
2817
{
2818
RECT client_rect, content_rect, old_label_rect, label_rect, box_rect, image_rect, text_rect;
2819
HFONT font, hPrevFont = NULL;
2820
DWORD dwStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2821
LONG ex_style = GetWindowLongW(infoPtr->hwnd, GWL_EXSTYLE);
2822
UINT btn_type = get_button_type( dwStyle );
2823
int part = (btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON) ? BP_RADIOBUTTON : BP_CHECKBOX;
2824
NMCUSTOMDRAW nmcd;
2825
HBRUSH brush;
2826
LRESULT cdrf;
2827
LOGFONTW lf;
2828
HWND parent;
2829
BOOL created_font = FALSE;
2830
int text_offset;
2831
SIZE box_size;
2832
HRGN region;
2833
HRESULT hr;
2834
2835
if (dwStyle & BS_PUSHLIKE)
2836
{
2837
PB_ThemedPaint(theme, infoPtr, hDC, state, dtFlags, focused);
2838
return;
2839
}
2840
2841
hr = GetThemeFont(theme, hDC, part, state, TMT_FONT, &lf);
2842
if (SUCCEEDED(hr)) {
2843
font = CreateFontIndirectW(&lf);
2844
if (!font)
2845
TRACE("Failed to create font\n");
2846
else {
2847
TRACE("font = %s\n", debugstr_w(lf.lfFaceName));
2848
hPrevFont = SelectObject(hDC, font);
2849
created_font = TRUE;
2850
}
2851
} else {
2852
if (infoPtr->font) SelectObject(hDC, infoPtr->font);
2853
}
2854
2855
GetClientRect(infoPtr->hwnd, &client_rect);
2856
GetThemeBackgroundContentRect(theme, hDC, part, state, &client_rect, &content_rect);
2857
region = set_control_clipping(hDC, &client_rect);
2858
2859
if (FAILED(GetThemePartSize(theme, hDC, part, state, &content_rect, TS_DRAW, &box_size)))
2860
{
2861
box_size.cx = 12 * GetDpiForWindow(infoPtr->hwnd) / 96 + 1;
2862
box_size.cy = box_size.cx;
2863
}
2864
2865
GetCharWidthW(hDC, '0', '0', &text_offset);
2866
text_offset /= 2;
2867
2868
label_rect = content_rect;
2869
if (dwStyle & BS_LEFTTEXT || ex_style & WS_EX_RIGHT)
2870
label_rect.right -= box_size.cx + text_offset;
2871
else
2872
label_rect.left += box_size.cx + text_offset;
2873
2874
old_label_rect = label_rect;
2875
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &label_rect, &image_rect, &text_rect);
2876
box_rect = get_box_rect(dwStyle, ex_style, &content_rect, &label_rect, dtFlags != (UINT)-1L,
2877
box_size);
2878
2879
init_custom_draw(&nmcd, infoPtr, hDC, &client_rect);
2880
2881
parent = GetParent(infoPtr->hwnd);
2882
if (!parent) parent = infoPtr->hwnd;
2883
2884
/* Send erase notifications */
2885
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2886
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2887
2888
DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL);
2889
/* Tests show that the brush from WM_CTLCOLORSTATIC is used for filling background after a
2890
* DrawThemeParentBackground() call */
2891
brush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2892
FillRect(hDC, &client_rect, brush ? brush : GetSysColorBrush(COLOR_BTNFACE));
2893
2894
if (cdrf & CDRF_NOTIFYPOSTERASE)
2895
{
2896
nmcd.dwDrawStage = CDDS_POSTERASE;
2897
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2898
}
2899
2900
/* Send paint notifications */
2901
nmcd.dwDrawStage = CDDS_PREPAINT;
2902
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2903
if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
2904
2905
/* Draw label */
2906
if (!(cdrf & CDRF_DOERASE))
2907
{
2908
DrawThemeBackground(theme, hDC, part, state, &box_rect, NULL);
2909
if (dtFlags != (UINT)-1L)
2910
BUTTON_DrawThemedLabel(infoPtr, hDC, dtFlags, &image_rect, &text_rect, theme, part, state);
2911
}
2912
2913
if (cdrf & CDRF_NOTIFYPOSTPAINT)
2914
{
2915
nmcd.dwDrawStage = CDDS_POSTPAINT;
2916
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
2917
}
2918
if ((cdrf & CDRF_SKIPPOSTPAINT) || dtFlags == (UINT)-1L) goto cleanup;
2919
2920
if (focused)
2921
{
2922
label_rect.left--;
2923
label_rect.right++;
2924
IntersectRect(&label_rect, &label_rect, &old_label_rect);
2925
DrawFocusRect(hDC, &label_rect);
2926
}
2927
2928
cleanup:
2929
SelectClipRgn(hDC, region);
2930
if (region) DeleteObject(region);
2931
if (created_font) DeleteObject(font);
2932
if (hPrevFont) SelectObject(hDC, hPrevFont);
2933
}
2934
2935
static void GB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, int state, UINT dtFlags, BOOL focused)
2936
{
2937
RECT clientRect, contentRect, labelRect, imageRect, textRect, bgRect;
2938
HRGN region, textRegion = NULL;
2939
LOGFONTW lf;
2940
HFONT font, hPrevFont = NULL;
2941
BOOL created_font = FALSE;
2942
TEXTMETRICW textMetric;
2943
HBRUSH brush;
2944
HWND parent;
2945
HRESULT hr;
2946
LONG style;
2947
int part;
2948
2949
/* DrawThemeParentBackground() is used for filling content background. The brush from
2950
* WM_CTLCOLORSTATIC is used for filling text background */
2951
parent = GetParent(infoPtr->hwnd);
2952
if (!parent)
2953
parent = infoPtr->hwnd;
2954
brush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
2955
2956
hr = GetThemeFont(theme, hDC, BP_GROUPBOX, state, TMT_FONT, &lf);
2957
if (SUCCEEDED(hr)) {
2958
font = CreateFontIndirectW(&lf);
2959
if (!font)
2960
TRACE("Failed to create font\n");
2961
else {
2962
hPrevFont = SelectObject(hDC, font);
2963
created_font = TRUE;
2964
}
2965
} else {
2966
if (infoPtr->font)
2967
SelectObject(hDC, infoPtr->font);
2968
}
2969
2970
GetClientRect(infoPtr->hwnd, &clientRect);
2971
region = set_control_clipping(hDC, &clientRect);
2972
2973
bgRect = clientRect;
2974
GetTextMetricsW(hDC, &textMetric);
2975
bgRect.top += (textMetric.tmHeight / 2) - 1;
2976
2977
labelRect = clientRect;
2978
InflateRect(&labelRect, -7, 1);
2979
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
2980
if (dtFlags != (UINT)-1 && !show_image_only(infoPtr))
2981
{
2982
textRegion = CreateRectRgnIndirect(&textRect);
2983
ExtSelectClipRgn(hDC, textRegion, RGN_DIFF);
2984
}
2985
2986
style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE);
2987
if (style & BS_PUSHLIKE)
2988
{
2989
part = BP_PUSHBUTTON;
2990
}
2991
else
2992
{
2993
part = BP_GROUPBOX;
2994
GetThemeBackgroundContentRect(theme, hDC, part, state, &bgRect, &contentRect);
2995
ExcludeClipRect(hDC, contentRect.left, contentRect.top, contentRect.right, contentRect.bottom);
2996
}
2997
if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
2998
DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL);
2999
DrawThemeBackground(theme, hDC, part, state, &bgRect, NULL);
3000
3001
if (dtFlags != (UINT)-1)
3002
{
3003
if (textRegion)
3004
{
3005
SelectClipRgn(hDC, textRegion);
3006
DeleteObject(textRegion);
3007
}
3008
FillRect(hDC, &textRect, brush ? brush : GetSysColorBrush(COLOR_BTNFACE));
3009
BUTTON_DrawThemedLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect, theme, part, state);
3010
}
3011
3012
SelectClipRgn(hDC, region);
3013
if (region) DeleteObject(region);
3014
if (created_font) DeleteObject(font);
3015
if (hPrevFont) SelectObject(hDC, hPrevFont);
3016
}
3017
3018
static void SB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, int state, UINT dtFlags, BOOL focused)
3019
{
3020
RECT rc, content_rect, push_rect, dropdown_rect, focus_rect, label_rect, image_rect, text_rect;
3021
NMCUSTOMDRAW nmcd;
3022
LRESULT cdrf;
3023
HWND parent;
3024
3025
if (infoPtr->font) SelectObject(hDC, infoPtr->font);
3026
3027
GetClientRect(infoPtr->hwnd, &rc);
3028
init_custom_draw(&nmcd, infoPtr, hDC, &rc);
3029
3030
parent = GetParent(infoPtr->hwnd);
3031
if (!parent) parent = infoPtr->hwnd;
3032
3033
/* Send erase notifications */
3034
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3035
if (cdrf & CDRF_SKIPDEFAULT) return;
3036
3037
if (IsThemeBackgroundPartiallyTransparent(theme, BP_PUSHBUTTON, state))
3038
DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL);
3039
3040
/* The zone outside the content is ignored for the dropdown (draws over) */
3041
GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &rc, &content_rect);
3042
get_split_button_rects(infoPtr, &rc, &push_rect, &dropdown_rect);
3043
3044
if (infoPtr->split_style & BCSS_NOSPLIT)
3045
{
3046
push_rect = rc;
3047
DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, state, &rc, NULL);
3048
GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &push_rect, &focus_rect);
3049
}
3050
else
3051
{
3052
RECT r = { dropdown_rect.left, content_rect.top, dropdown_rect.right, content_rect.bottom };
3053
UINT edge = (infoPtr->split_style & BCSS_ALIGNLEFT) ? BF_RIGHT : BF_LEFT;
3054
const RECT *clip = NULL;
3055
3056
/* If only the dropdown is pressed, we need to draw it separately */
3057
if (state != PBS_PRESSED && (infoPtr->state & BST_DROPDOWNPUSHED))
3058
{
3059
DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, PBS_PRESSED, &rc, &dropdown_rect);
3060
clip = &push_rect;
3061
}
3062
DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, state, &rc, clip);
3063
3064
/* Draw the separator */
3065
DrawThemeEdge(theme, hDC, BP_PUSHBUTTON, state, &r, EDGE_ETCHED, edge, NULL);
3066
3067
/* The content rect should be the content area of the push button */
3068
GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &push_rect, &content_rect);
3069
focus_rect = content_rect;
3070
}
3071
3072
if (cdrf & CDRF_NOTIFYPOSTERASE)
3073
{
3074
nmcd.dwDrawStage = CDDS_POSTERASE;
3075
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3076
}
3077
3078
/* Send paint notifications */
3079
nmcd.dwDrawStage = CDDS_PREPAINT;
3080
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3081
if (cdrf & CDRF_SKIPDEFAULT) return;
3082
3083
if (!(cdrf & CDRF_DOERASE))
3084
{
3085
COLORREF old_color, color;
3086
INT old_bk_mode;
3087
3088
label_rect = content_rect;
3089
dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &label_rect, &image_rect, &text_rect);
3090
if (dtFlags != (UINT)-1L)
3091
BUTTON_DrawThemedLabel(infoPtr, hDC, dtFlags, &image_rect, &text_rect, theme,
3092
BP_PUSHBUTTON, state);
3093
3094
GetThemeColor(theme, BP_PUSHBUTTON, state, TMT_TEXTCOLOR, &color);
3095
old_bk_mode = SetBkMode(hDC, TRANSPARENT);
3096
old_color = SetTextColor(hDC, color);
3097
3098
draw_split_button_dropdown_glyph(infoPtr, hDC, &dropdown_rect);
3099
3100
SetTextColor(hDC, old_color);
3101
SetBkMode(hDC, old_bk_mode);
3102
}
3103
3104
if (cdrf & CDRF_NOTIFYPOSTPAINT)
3105
{
3106
nmcd.dwDrawStage = CDDS_POSTPAINT;
3107
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3108
}
3109
if (cdrf & CDRF_SKIPPOSTPAINT) return;
3110
3111
if (focused) DrawFocusRect(hDC, &focus_rect);
3112
}
3113
3114
static void CL_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, int state, UINT dtFlags, BOOL focused)
3115
{
3116
NMCUSTOMDRAW nmcd;
3117
LRESULT cdrf;
3118
HWND parent;
3119
int part;
3120
RECT rc;
3121
3122
if (infoPtr->font) SelectObject(hDC, infoPtr->font);
3123
3124
GetClientRect(infoPtr->hwnd, &rc);
3125
init_custom_draw(&nmcd, infoPtr, hDC, &rc);
3126
3127
parent = GetParent(infoPtr->hwnd);
3128
if (!parent) parent = infoPtr->hwnd;
3129
3130
/* Send erase notifications */
3131
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3132
if (cdrf & CDRF_SKIPDEFAULT) return;
3133
3134
part = GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & BS_PUSHLIKE ? BP_PUSHBUTTON : BP_COMMANDLINK;
3135
if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
3136
DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL);
3137
DrawThemeBackground(theme, hDC, part, state, &rc, NULL);
3138
3139
if (cdrf & CDRF_NOTIFYPOSTERASE)
3140
{
3141
nmcd.dwDrawStage = CDDS_POSTERASE;
3142
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3143
}
3144
3145
/* Send paint notifications */
3146
nmcd.dwDrawStage = CDDS_PREPAINT;
3147
cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3148
if (cdrf & CDRF_SKIPDEFAULT) return;
3149
3150
if (!(cdrf & CDRF_DOERASE))
3151
{
3152
RECT r, img_rect;
3153
UINT txt_h = 0;
3154
SIZE img_size;
3155
WCHAR *text;
3156
3157
GetThemeBackgroundContentRect(theme, hDC, part, state, &rc, &r);
3158
3159
/* The text alignment and styles are fixed and don't depend on button styles */
3160
dtFlags = DT_TOP | DT_LEFT | DT_WORDBREAK;
3161
3162
/* Command Links ignore the margins of the image list or its alignment */
3163
if (infoPtr->u.image || infoPtr->imagelist.himl)
3164
img_size = BUTTON_GetImageSize(infoPtr);
3165
else
3166
GetThemePartSize(theme, NULL, BP_COMMANDLINKGLYPH, state, NULL, TS_DRAW, &img_size);
3167
3168
img_rect = r;
3169
if (img_size.cx) r.left += img_size.cx + command_link_margin;
3170
3171
/* Draw the text */
3172
if ((text = get_button_text(infoPtr)))
3173
{
3174
UINT len = lstrlenW(text);
3175
RECT text_rect;
3176
3177
GetThemeTextExtent(theme, hDC, part, state, text, len, dtFlags | DT_END_ELLIPSIS, &r,
3178
&text_rect);
3179
DrawThemeText(theme, hDC, part, state, text, len, dtFlags | DT_END_ELLIPSIS, 0, &r);
3180
3181
txt_h = text_rect.bottom - text_rect.top;
3182
Free(text);
3183
}
3184
3185
/* Draw the note */
3186
if (infoPtr->note)
3187
{
3188
DTTOPTS opts;
3189
3190
r.top += txt_h;
3191
opts.dwSize = sizeof(opts);
3192
opts.dwFlags = DTT_FONTPROP;
3193
opts.iFontPropId = TMT_BODYFONT;
3194
DrawThemeTextEx(theme, hDC, part, state, infoPtr->note, infoPtr->note_length,
3195
dtFlags | DT_NOPREFIX, &r, &opts);
3196
}
3197
3198
/* Position the image at the vertical center of the drawn text (not note) */
3199
txt_h = min(txt_h, img_rect.bottom - img_rect.top);
3200
if (img_size.cy < txt_h) img_rect.top += (txt_h - img_size.cy) / 2;
3201
3202
img_rect.right = img_rect.left + img_size.cx;
3203
img_rect.bottom = img_rect.top + img_size.cy;
3204
3205
if (infoPtr->u.image || infoPtr->imagelist.himl)
3206
BUTTON_DrawImage(infoPtr, hDC, NULL,
3207
(state == CMDLS_DISABLED) ? DSS_DISABLED : DSS_NORMAL,
3208
&img_rect);
3209
else
3210
DrawThemeBackground(theme, hDC, BP_COMMANDLINKGLYPH, state, &img_rect, NULL);
3211
}
3212
3213
if (cdrf & CDRF_NOTIFYPOSTPAINT)
3214
{
3215
nmcd.dwDrawStage = CDDS_POSTPAINT;
3216
SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
3217
}
3218
if (cdrf & CDRF_SKIPPOSTPAINT) return;
3219
3220
if (focused)
3221
{
3222
MARGINS margins;
3223
3224
/* The focus rect has margins of a push button rather than command link... */
3225
GetThemeMargins(theme, hDC, BP_PUSHBUTTON, state, TMT_CONTENTMARGINS, NULL, &margins);
3226
3227
rc.left += margins.cxLeftWidth;
3228
rc.top += margins.cyTopHeight;
3229
rc.right -= margins.cxRightWidth;
3230
rc.bottom -= margins.cyBottomHeight;
3231
DrawFocusRect(hDC, &rc);
3232
}
3233
}
3234
3235
void BUTTON_Register(void)
3236
{
3237
WNDCLASSW wndClass;
3238
3239
memset(&wndClass, 0, sizeof(wndClass));
3240
wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC;
3241
wndClass.lpfnWndProc = BUTTON_WindowProc;
3242
wndClass.cbClsExtra = 0;
3243
wndClass.cbWndExtra = sizeof(BUTTON_INFO *);
3244
wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
3245
wndClass.hbrBackground = NULL;
3246
wndClass.lpszClassName = WC_BUTTONW;
3247
RegisterClassW(&wndClass);
3248
}
3249
3250