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