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