xref: /reactos/dll/win32/comctl32/button.c (revision d5b576b2)
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, int index)
399 {
400     SIZE ImageSize;
401     int left, top, count;
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.cx) / 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.cx) / 2;
430         top = prc->bottom - pimlData->margin.bottom - ImageSize.cy;
431         prc->bottom = top - pimlData->margin.top;
432     }
433     else if (pimlData->uAlign == BUTTON_IMAGELIST_ALIGN_CENTER)
434     {
435         left = prc->left + (prc->right - prc->left - ImageSize.cx) / 2;
436         top = prc->top + (prc->bottom - prc->top - ImageSize.cy) / 2;
437     }
438 
439     if (bOnlyCalc)
440         return TRUE;
441 
442     count = ImageList_GetImageCount(pimlData->himl);
443 
444     if (count == 1)
445         index = 0;
446     else if (index >= count)
447         return TRUE;
448 
449     ImageList_Draw(pimlData->himl, index, hDC, left, top, 0);
450 
451     return TRUE;
452 }
453 
454 DWORD BUTTON_SendCustomDraw(HWND hwnd, HDC hDC, DWORD dwDrawStage, RECT* prc)
455 {
456     NMCUSTOMDRAW nmcs;
457     LONG state = get_button_state( hwnd );
458 
459     nmcs.hdr.hwndFrom = hwnd;
460     nmcs.hdr.idFrom   = GetWindowLongPtrW (hwnd, GWLP_ID);
461     nmcs.hdr.code     = NM_CUSTOMDRAW ;
462     nmcs.dwDrawStage  = dwDrawStage;
463     nmcs.hdc          = hDC;
464     nmcs.rc           = *prc;
465     nmcs.dwItemSpec   = 0;
466     nmcs.uItemState   = 0;
467     nmcs.lItemlParam  = 0;
468     if(!IsWindowEnabled(hwnd))
469         nmcs.uItemState |= CDIS_DISABLED;
470     if (state & (BST_CHECKED | BST_INDETERMINATE))
471         nmcs.uItemState |= CDIS_CHECKED;
472     if (state & BST_FOCUS)
473         nmcs.uItemState |= CDIS_FOCUS;
474     if (state & BST_PUSHED)
475         nmcs.uItemState |= CDIS_SELECTED;
476     if (!(get_ui_state(hwnd) & UISF_HIDEACCEL))
477         nmcs.uItemState |= CDIS_SHOWKEYBOARDCUES;
478 
479     return SendMessageW(GetParent(hwnd), WM_NOTIFY, nmcs.hdr.idFrom, (LPARAM)&nmcs);
480 }
481 
482 #endif
483 
484 
485 /* retrieve the button text; returned buffer must be freed by caller */
486 inline WCHAR *get_button_text( HWND hwnd )
487 {
488     INT len = 512;
489     WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
490     if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 );
491     return buffer;
492 }
493 
494 #ifdef __REACTOS__
495 /* Retrieve the UI state for the control */
496 static BOOL button_update_uistate(HWND hwnd, BOOL unicode)
497 {
498     LONG flags, prevflags;
499 
500     if (unicode)
501         flags = DefWindowProcW(hwnd, WM_QUERYUISTATE, 0, 0);
502     else
503         flags = DefWindowProcA(hwnd, WM_QUERYUISTATE, 0, 0);
504 
505     prevflags = get_ui_state(hwnd);
506 
507     if (prevflags != flags)
508     {
509         set_ui_state(hwnd, flags);
510         return TRUE;
511     }
512 
513     return FALSE;
514 }
515 #endif
516 
517 /***********************************************************************
518  *           ButtonWndProc_common
519  */
520 LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
521                                   WPARAM wParam, LPARAM lParam, BOOL unicode )
522 {
523     RECT rect;
524     POINT pt;
525     LONG style = GetWindowLongPtrW( hWnd, GWL_STYLE );
526     UINT btn_type = get_button_type( style );
527     LONG state;
528     HANDLE oldHbitmap;
529 #if defined(__REACTOS__) && defined(_USER32_)
530     PWND pWnd;
531 
532     pWnd = ValidateHwnd(hWnd);
533     if (pWnd)
534     {
535        if (!pWnd->fnid)
536        {
537           NtUserSetWindowFNID(hWnd, FNID_BUTTON);
538        }
539        else
540        {
541           if (pWnd->fnid != FNID_BUTTON)
542           {
543              ERR("Wrong window class for Button! fnId 0x%x\n",pWnd->fnid);
544              return 0;
545           }
546        }
547     }
548     else
549        return 0;
550 #else
551     if (!IsWindow( hWnd )) return 0;
552 #endif
553 
554     pt.x = (short)LOWORD(lParam);
555     pt.y = (short)HIWORD(lParam);
556 
557 #ifndef _USER32_
558     switch (uMsg)
559     {
560         case WM_NCCREATE:
561         {
562             PBUTTON_DATA data = HeapAlloc( GetProcessHeap(), 0, sizeof(BUTTON_DATA) );
563             if (!data)
564             {
565                 ERR("Failed to alloc internal button data\n");
566                 return -1;
567             }
568 
569             memset(data, 0, sizeof(BUTTON_DATA));
570             SetRect(&data->rcTextMargin, 1,1,1,1);
571 
572             _SetButtonData(hWnd, data);
573             break;
574         }
575         case WM_NCDESTROY:
576         {
577             PBUTTON_DATA data = _GetButtonData(hWnd);
578             if (!data)
579             {
580                 ERR("No data");
581                 return 0;
582             }
583             HeapFree( GetProcessHeap(), 0, data );
584             _SetButtonData(hWnd, NULL);
585         }
586         case WM_CREATE:
587             OpenThemeData(hWnd, WC_BUTTONW);
588             break;
589         case WM_DESTROY:
590             CloseThemeData (GetWindowTheme(hWnd));
591             break;
592         case WM_THEMECHANGED:
593             CloseThemeData (GetWindowTheme(hWnd));
594             OpenThemeData(hWnd, WC_BUTTONW);
595             InvalidateRect(hWnd, NULL, TRUE);
596             break;
597         case WM_MOUSELEAVE:
598         {
599             state = get_button_state( hWnd );
600             if (state & BST_HOT)
601             {
602                 NMBCHOTITEM nmhotitem;
603 
604                 state &= ~BST_HOT;
605                 set_button_state(hWnd, state);
606 
607                 nmhotitem.hdr.hwndFrom = hWnd;
608                 nmhotitem.hdr.idFrom   = GetWindowLongPtrW (hWnd, GWLP_ID);
609                 nmhotitem.hdr.code     = BCN_HOTITEMCHANGE;
610                 nmhotitem.dwFlags      = HICF_LEAVING;
611                 SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem);
612 
613                 InvalidateRect(hWnd, NULL, TRUE);
614             }
615             break;
616         }
617         case WM_MOUSEMOVE:
618         {
619             TRACKMOUSEEVENT mouse_event;
620             state = get_button_state( hWnd );
621             if ((state & BST_HOT) == 0)
622             {
623                 NMBCHOTITEM nmhotitem;
624 
625                 state |= BST_HOT;
626                 set_button_state(hWnd, state);
627 
628                 nmhotitem.hdr.hwndFrom = hWnd;
629                 nmhotitem.hdr.idFrom   = GetWindowLongPtrW (hWnd, GWLP_ID);
630                 nmhotitem.hdr.code     = BCN_HOTITEMCHANGE;
631                 nmhotitem.dwFlags      = HICF_ENTERING;
632                 SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem);
633 
634                 InvalidateRect(hWnd, NULL, TRUE);
635             }
636 
637             mouse_event.cbSize = sizeof(TRACKMOUSEEVENT);
638             mouse_event.dwFlags = TME_QUERY;
639             if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&TME_LEAVE))
640             {
641                 mouse_event.dwFlags = TME_LEAVE;
642                 mouse_event.hwndTrack = hWnd;
643                 mouse_event.dwHoverTime = 1;
644                 TrackMouseEvent(&mouse_event);
645             }
646             break;
647         }
648         case BCM_GETTEXTMARGIN:
649         {
650             RECT* prc = (RECT*)lParam;
651             PBUTTON_DATA data = _GetButtonData(hWnd);
652             if (!prc || !data)
653                 return FALSE;
654             *prc = data->rcTextMargin;
655             return TRUE;
656         }
657         case BCM_SETTEXTMARGIN:
658         {
659             RECT* prc = (RECT*)lParam;
660             PBUTTON_DATA data = _GetButtonData(hWnd);
661             if (!prc || !data)
662                 return FALSE;
663             data->rcTextMargin = *prc;
664             return TRUE;
665         }
666         case BCM_SETIMAGELIST:
667         {
668             BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam;
669             PBUTTON_DATA data = _GetButtonData(hWnd);
670             if (!data || !pimldata || !pimldata->himl)
671                 return FALSE;
672             data->imlData = *pimldata;
673             return TRUE;
674         }
675         case BCM_GETIMAGELIST:
676         {
677             BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam;
678             PBUTTON_DATA data = _GetButtonData(hWnd);
679             if (!data|| !pimldata)
680                 return FALSE;
681             *pimldata = data->imlData;
682             return TRUE;
683         }
684         case BCM_GETIDEALSIZE:
685         {
686             HTHEME theme = GetWindowTheme(hWnd);
687             BOOL ret = FALSE;
688             SIZE* pSize = (SIZE*)lParam;
689 
690             if (btn_type == BS_PUSHBUTTON ||
691                 btn_type == BS_DEFPUSHBUTTON ||
692                 btn_type == BS_USERBUTTON)
693             {
694                 ret = BUTTON_GetIdealSize(theme, hWnd, pSize);
695             }
696 
697             if (!ret)
698             {
699                 GetClientRect(hWnd, &rect);
700                 pSize->cx = rect.right;
701                 pSize->cy = rect.bottom;
702             }
703 
704             return TRUE;
705         }
706     }
707 
708     if (!_GetButtonData(hWnd))
709     {
710         ERR("no data!\n");
711         return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
712                          DefWindowProcA(hWnd, uMsg, wParam, lParam);
713     }
714 
715 #endif
716 
717     switch (uMsg)
718     {
719     case WM_GETDLGCODE:
720         switch(btn_type)
721         {
722         case BS_USERBUTTON:
723         case BS_PUSHBUTTON:      return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
724         case BS_DEFPUSHBUTTON:   return DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
725         case BS_RADIOBUTTON:
726         case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON;
727         case BS_GROUPBOX:        return DLGC_STATIC;
728         default:                 return DLGC_BUTTON;
729         }
730 
731     case WM_ENABLE:
732         paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
733         break;
734 
735     case WM_CREATE:
736         if (btn_type >= MAX_BTN_TYPE)
737             return -1; /* abort */
738 
739         /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
740         if (btn_type == BS_USERBUTTON )
741         {
742             style = (style & ~BS_TYPEMASK) | BS_PUSHBUTTON;
743 #ifdef __REACTOS__
744             NtUserAlterWindowStyle(hWnd, GWL_STYLE, style );
745 #else
746             WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style );
747 #endif
748         }
749         set_button_state( hWnd, BST_UNCHECKED );
750 #ifdef __REACTOS__
751         button_update_uistate( hWnd, unicode );
752 #endif
753         return 0;
754 
755 #if defined(__REACTOS__) && defined(_USER32_)
756     case WM_NCDESTROY:
757         NtUserSetWindowFNID(hWnd, FNID_DESTROY);
758     case WM_DESTROY:
759         break;
760 #endif
761     case WM_ERASEBKGND:
762         if (btn_type == BS_OWNERDRAW)
763         {
764             HDC hdc = (HDC)wParam;
765             RECT rc;
766             HBRUSH hBrush;
767             HWND parent = GetParent(hWnd);
768             if (!parent) parent = hWnd;
769 #if defined(__REACTOS__) && defined(_USER32_)
770             hBrush = GetControlColor( parent, hWnd, hdc, WM_CTLCOLORBTN);
771 #else
772             hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd);
773             if (!hBrush) /* did the app forget to call defwindowproc ? */
774                 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
775                                                 (WPARAM)hdc, (LPARAM)hWnd);
776 #endif
777             GetClientRect(hWnd, &rc);
778             FillRect(hdc, &rc, hBrush);
779         }
780         return 1;
781 
782     case WM_PRINTCLIENT:
783     case WM_PAINT:
784     {
785         PAINTSTRUCT ps;
786         HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
787 #ifndef _USER32_
788         HTHEME theme = GetWindowTheme(hWnd);
789         if (theme && BUTTON_PaintWithTheme(theme, hWnd, hdc, uMsg == WM_PRINTCLIENT ? lParam : 0))
790         {
791             if ( !wParam ) EndPaint( hWnd, &ps );
792             return 0;
793         }
794 #endif
795         if (btnPaintFunc[btn_type])
796         {
797             int nOldMode = SetBkMode( hdc, OPAQUE );
798             (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE );
799             SetBkMode(hdc, nOldMode); /*  reset painting mode */
800         }
801         if ( !wParam ) EndPaint( hWnd, &ps );
802         break;
803     }
804 
805     case WM_KEYDOWN:
806 	if (wParam == VK_SPACE)
807 	{
808 	    SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
809             set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
810             SetCapture( hWnd );
811 	}
812 	break;
813 
814     case WM_LBUTTONDBLCLK:
815         if(style & BS_NOTIFY ||
816            btn_type == BS_RADIOBUTTON ||
817            btn_type == BS_USERBUTTON ||
818            btn_type == BS_OWNERDRAW)
819         {
820             BUTTON_NOTIFY_PARENT(hWnd, BN_DOUBLECLICKED);
821             break;
822         }
823         /* fall through */
824     case WM_LBUTTONDOWN:
825         SetCapture( hWnd );
826         SetFocus( hWnd );
827         set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
828         SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
829         break;
830 
831     case WM_KEYUP:
832 	if (wParam != VK_SPACE)
833 	    break;
834 	/* fall through */
835     case WM_LBUTTONUP:
836 #ifdef _REACTOS_
837         BOOL TellParent = FALSE; //// ReactOS see note below.
838 #endif
839         state = get_button_state( hWnd );
840         if (!(state & BUTTON_BTNPRESSED)) break;
841         state &= BUTTON_NSTATES;
842         set_button_state( hWnd, state );
843         if (!(state & BST_PUSHED))
844         {
845             ReleaseCapture();
846             break;
847         }
848         SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
849         GetClientRect( hWnd, &rect );
850 	if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
851         {
852             state = get_button_state( hWnd );
853             switch(btn_type)
854             {
855             case BS_AUTOCHECKBOX:
856                 SendMessageW( hWnd, BM_SETCHECK, !(state & BST_CHECKED), 0 );
857                 break;
858             case BS_AUTORADIOBUTTON:
859                 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
860                 break;
861             case BS_AUTO3STATE:
862                 SendMessageW( hWnd, BM_SETCHECK,
863                                 (state & BST_INDETERMINATE) ? 0 : ((state & 3) + 1), 0 );
864                 break;
865             }
866 #ifdef _REACTOS_
867             TellParent = TRUE; // <---- Fix CORE-10194, Notify parent after capture is released.
868 #else
869             ReleaseCapture();
870             BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED);
871 #endif
872         }
873 #ifdef _REACTOS_
874         ReleaseCapture();
875         if (TellParent) BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED);
876 #else
877         else
878         {
879             ReleaseCapture();
880         }
881 #endif
882         break;
883 
884     case WM_CAPTURECHANGED:
885         TRACE("WM_CAPTURECHANGED %p\n", hWnd);
886         if (hWnd == (HWND)lParam) break;
887         state = get_button_state( hWnd );
888         if (state & BUTTON_BTNPRESSED)
889         {
890             state &= BUTTON_NSTATES;
891             set_button_state( hWnd, state );
892             if (state & BST_PUSHED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
893         }
894         break;
895 
896     case WM_MOUSEMOVE:
897         if ((wParam & MK_LBUTTON) && GetCapture() == hWnd)
898         {
899             GetClientRect( hWnd, &rect );
900             SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
901         }
902         break;
903 
904     case WM_SETTEXT:
905     {
906         /* Clear an old text here as Windows does */
907 //
908 // ReactOS Note :
909 // wine Bug: http://bugs.winehq.org/show_bug.cgi?id=25790
910 // Patch: http://source.winehq.org/patches/data/70889
911 // By: Alexander LAW, Replicate Windows behavior of WM_SETTEXT handler regarding WM_CTLCOLOR*
912 //
913 #ifdef __REACTOS__
914         if (style & WS_VISIBLE)
915 #else
916         if (IsWindowVisible(hWnd))
917 #endif
918         {
919             HDC hdc = GetDC(hWnd);
920             HBRUSH hbrush;
921             RECT client, rc;
922             HWND parent = GetParent(hWnd);
923             UINT message = (btn_type == BS_PUSHBUTTON ||
924                             btn_type == BS_DEFPUSHBUTTON ||
925                             btn_type == BS_PUSHLIKE ||
926                             btn_type == BS_USERBUTTON ||
927                             btn_type == BS_OWNERDRAW) ?
928                             WM_CTLCOLORBTN : WM_CTLCOLORSTATIC;
929 
930             if (!parent) parent = hWnd;
931 #if defined(__REACTOS__) && defined(_USER32_)
932             hbrush = GetControlColor(parent, hWnd, hdc, message);
933 #else
934             hbrush = (HBRUSH)SendMessageW(parent, message,
935                                           (WPARAM)hdc, (LPARAM)hWnd);
936             if (!hbrush) /* did the app forget to call DefWindowProc ? */
937                 hbrush = (HBRUSH)DefWindowProcW(parent, message,
938                                                 (WPARAM)hdc, (LPARAM)hWnd);
939 #endif
940 
941             GetClientRect(hWnd, &client);
942             rc = client;
943             /* FIXME: check other BS_* handlers */
944             if (btn_type == BS_GROUPBOX)
945                 InflateRect(&rc, -7, 1); /* GB_Paint does this */
946             BUTTON_CalcLabelRect(hWnd, hdc, &rc);
947             /* Clip by client rect bounds */
948             if (rc.right > client.right) rc.right = client.right;
949             if (rc.bottom > client.bottom) rc.bottom = client.bottom;
950             FillRect(hdc, &rc, hbrush);
951             ReleaseDC(hWnd, hdc);
952         }
953 
954         if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
955         else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam );
956         if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */
957             InvalidateRect( hWnd, NULL, TRUE );
958         else
959             paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
960         return 1; /* success. FIXME: check text length */
961     }
962 
963     case WM_SETFONT:
964         set_button_font( hWnd, (HFONT)wParam );
965         if (lParam) InvalidateRect(hWnd, NULL, TRUE);
966         break;
967 
968     case WM_GETFONT:
969         return (LRESULT)get_button_font( hWnd );
970 
971     case WM_SETFOCUS:
972         TRACE("WM_SETFOCUS %p\n",hWnd);
973         set_button_state( hWnd, get_button_state(hWnd) | BST_FOCUS );
974 #ifndef _USER32_
975         if (btn_type != BS_OWNERDRAW)
976             InvalidateRect(hWnd, NULL, FALSE);
977         else
978 #endif
979         paint_button( hWnd, btn_type, ODA_FOCUS );
980         if (style & BS_NOTIFY)
981             BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS);
982         break;
983 
984     case WM_KILLFOCUS:
985         TRACE("WM_KILLFOCUS %p\n",hWnd);
986         state = get_button_state( hWnd );
987         set_button_state( hWnd, state & ~BST_FOCUS );
988 #ifdef _USER32_
989 	paint_button( hWnd, btn_type, ODA_FOCUS );
990 #endif
991 
992         if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd)
993             ReleaseCapture();
994         if (style & BS_NOTIFY)
995             BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS);
996 
997         InvalidateRect( hWnd, NULL, FALSE );
998         break;
999 
1000     case WM_SYSCOLORCHANGE:
1001         InvalidateRect( hWnd, NULL, FALSE );
1002         break;
1003 
1004     case BM_SETSTYLE:
1005         btn_type = wParam & BS_TYPEMASK;
1006         style = (style & ~BS_TYPEMASK) | btn_type;
1007 #ifdef __REACTOS__
1008         NtUserAlterWindowStyle(hWnd, GWL_STYLE, style);
1009 #else
1010         WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style );
1011 #endif
1012 
1013         /* Only redraw if lParam flag is set.*/
1014         if (lParam)
1015             InvalidateRect( hWnd, NULL, TRUE );
1016 
1017         break;
1018 
1019     case BM_CLICK:
1020 #ifdef __REACTOS__
1021         state = get_button_state(hWnd);
1022         if (state & BUTTON_BMCLICK)
1023            break;
1024         set_button_state(hWnd, state | BUTTON_BMCLICK); // Tracked in STATE_GWL_OFFSET.
1025 #endif
1026 	SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
1027 	SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
1028 #ifdef __REACTOS__
1029         state = get_button_state(hWnd);
1030         if (!(state & BUTTON_BMCLICK)) break;
1031         state &= ~BUTTON_BMCLICK;
1032         set_button_state(hWnd, state);
1033 #endif
1034 	break;
1035 
1036     case BM_SETIMAGE:
1037         /* Check that image format matches button style */
1038         switch (style & (BS_BITMAP|BS_ICON))
1039         {
1040         case BS_BITMAP:
1041             if (wParam != IMAGE_BITMAP) return 0;
1042             break;
1043         case BS_ICON:
1044             if (wParam != IMAGE_ICON) return 0;
1045             break;
1046         default:
1047             return 0;
1048         }
1049 #ifdef _USER32_
1050         oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam );
1051 #else
1052         oldHbitmap = (HBITMAP)set_button_image(hWnd, lParam );
1053 #endif
1054 	InvalidateRect( hWnd, NULL, FALSE );
1055 	return (LRESULT)oldHbitmap;
1056 
1057     case BM_GETIMAGE:
1058 #ifdef _USER32_
1059         return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET );
1060 #else
1061         return get_button_image(hWnd);
1062 #endif
1063 
1064     case BM_GETCHECK:
1065         return get_button_state( hWnd ) & 3;
1066 
1067     case BM_SETCHECK:
1068         if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
1069         state = get_button_state( hWnd );
1070         if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
1071         {
1072 #ifdef __REACTOS__
1073             if (wParam) style |= WS_TABSTOP;
1074             else style &= ~WS_TABSTOP;
1075             NtUserAlterWindowStyle(hWnd, GWL_STYLE, style);
1076 #else
1077             if (wParam) WIN_SetStyle( hWnd, WS_TABSTOP, 0 );
1078             else WIN_SetStyle( hWnd, 0, WS_TABSTOP );
1079 #endif
1080         }
1081         if ((state & 3) != wParam)
1082         {
1083             set_button_state( hWnd, (state & ~3) | wParam );
1084 #ifdef _USER32
1085             paint_button( hWnd, btn_type, ODA_SELECT );
1086 #else
1087             InvalidateRect(hWnd, NULL, FALSE);
1088 #endif
1089         }
1090         if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD))
1091             BUTTON_CheckAutoRadioButton( hWnd );
1092         break;
1093 
1094     case BM_GETSTATE:
1095         return get_button_state( hWnd );
1096 
1097     case BM_SETSTATE:
1098         state = get_button_state( hWnd );
1099         if (wParam)
1100             set_button_state( hWnd, state | BST_PUSHED );
1101         else
1102             set_button_state( hWnd, state & ~BST_PUSHED );
1103 
1104 #ifdef _USER32_
1105         paint_button( hWnd, btn_type, ODA_SELECT );
1106 #else
1107         InvalidateRect(hWnd, NULL, FALSE);
1108 #endif
1109         break;
1110 
1111 #ifdef __REACTOS__
1112     case WM_UPDATEUISTATE:
1113         if (unicode)
1114             DefWindowProcW(hWnd, uMsg, wParam, lParam);
1115         else
1116             DefWindowProcA(hWnd, uMsg, wParam, lParam);
1117 
1118         if (button_update_uistate(hWnd, unicode))
1119             paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
1120         break;
1121 #endif
1122 
1123     case WM_NCHITTEST:
1124         if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
1125         /* fall through */
1126     default:
1127         return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
1128                          DefWindowProcA(hWnd, uMsg, wParam, lParam);
1129     }
1130     return 0;
1131 }
1132 
1133 #ifdef __REACTOS__
1134 
1135 /***********************************************************************
1136  *           ButtonWndProcW
1137  * The button window procedure. This is just a wrapper which locks
1138  * the passed HWND and calls the real window procedure (with a WND*
1139  * pointer pointing to the locked windowstructure).
1140  */
1141 LRESULT WINAPI ButtonWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1142 {
1143     if (!IsWindow(hWnd)) return 0;
1144     return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
1145 }
1146 
1147 /***********************************************************************
1148  *           ButtonWndProcA
1149  */
1150 LRESULT WINAPI ButtonWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1151 {
1152     if (!IsWindow(hWnd)) return 0;
1153     return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
1154 }
1155 
1156 #endif /* __REACTOS__ */
1157 
1158 /**********************************************************************
1159  * Convert button styles to flags used by DrawText.
1160  */
1161 static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style )
1162 {
1163    UINT dtStyle = DT_NOCLIP;  /* We use SelectClipRgn to limit output */
1164 
1165    /* "Convert" pushlike buttons to pushbuttons */
1166    if (style & BS_PUSHLIKE)
1167       style &= ~BS_TYPEMASK;
1168 
1169    if (!(style & BS_MULTILINE))
1170       dtStyle |= DT_SINGLELINE;
1171    else
1172       dtStyle |= DT_WORDBREAK;
1173 
1174    switch (style & BS_CENTER)
1175    {
1176       case BS_LEFT:   /* DT_LEFT is 0 */    break;
1177       case BS_RIGHT:  dtStyle |= DT_RIGHT;  break;
1178       case BS_CENTER: dtStyle |= DT_CENTER; break;
1179       default:
1180          /* Pushbutton's text is centered by default */
1181          if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
1182          /* all other flavours have left aligned text */
1183    }
1184 
1185    if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER));
1186 
1187    /* DrawText ignores vertical alignment for multiline text,
1188     * but we use these flags to align label manually.
1189     */
1190    if (get_button_type(style) != BS_GROUPBOX)
1191    {
1192       switch (style & BS_VCENTER)
1193       {
1194          case BS_TOP:     /* DT_TOP is 0 */      break;
1195          case BS_BOTTOM:  dtStyle |= DT_BOTTOM;  break;
1196          case BS_VCENTER: /* fall through */
1197          default:         dtStyle |= DT_VCENTER; break;
1198       }
1199    }
1200    else
1201       /* GroupBox's text is always single line and is top aligned. */
1202       dtStyle |= DT_SINGLELINE;
1203 
1204    return dtStyle;
1205 }
1206 
1207 /**********************************************************************
1208  *       BUTTON_CalcLabelRect
1209  *
1210  *   Calculates label's rectangle depending on button style.
1211  *
1212  * Returns flags to be passed to DrawText.
1213  * Calculated rectangle doesn't take into account button state
1214  * (pushed, etc.). If there is nothing to draw (no text/image) output
1215  * rectangle is empty, and return value is (UINT)-1.
1216  */
1217 static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
1218 {
1219    LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1220    LONG ex_style = GetWindowLongPtrW( hwnd, GWL_EXSTYLE );
1221    WCHAR *text;
1222    ICONINFO    iconInfo;
1223    BITMAP      bm;
1224    UINT        dtStyle = BUTTON_BStoDT( style, ex_style );
1225    RECT        r = *rc;
1226    INT         n;
1227 #ifdef __REACTOS__
1228    PBUTTON_DATA pdata = _GetButtonData(hwnd);
1229 #endif
1230 
1231 #ifndef _USER32_
1232     BOOL bHasIml = BUTTON_DrawIml(hdc, &pdata->imlData, &r, TRUE, 0);
1233 #endif
1234 
1235    /* Calculate label rectangle according to label type */
1236    switch (style & (BS_ICON|BS_BITMAP))
1237    {
1238       case BS_TEXT:
1239       {
1240           HFONT hFont, hPrevFont = 0;
1241 
1242           if (!(text = get_button_text( hwnd ))) goto empty_rect;
1243           if (!text[0])
1244           {
1245               HeapFree( GetProcessHeap(), 0, text );
1246               goto empty_rect;
1247           }
1248 
1249           if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hdc, hFont );
1250           DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
1251           if (hPrevFont) SelectObject( hdc, hPrevFont );
1252           HeapFree( GetProcessHeap(), 0, text );
1253 #ifdef __REACTOS__
1254           if (get_ui_state(hwnd) & UISF_HIDEACCEL)
1255               dtStyle |= DT_HIDEPREFIX;
1256 #endif
1257           break;
1258       }
1259 
1260       case BS_ICON:
1261 #ifdef _USER32_
1262          if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
1263 #else
1264          if (!GetIconInfo((HICON)get_button_image(hwnd), &iconInfo))
1265 #endif
1266             goto empty_rect;
1267 
1268          GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
1269 
1270          r.right  = r.left + bm.bmWidth;
1271          r.bottom = r.top  + bm.bmHeight;
1272 
1273          DeleteObject(iconInfo.hbmColor);
1274          DeleteObject(iconInfo.hbmMask);
1275          break;
1276 
1277       case BS_BITMAP:
1278 #ifdef _USER32_
1279          if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
1280 #else
1281          if (!GetObjectW( (HANDLE)get_button_image(hwnd), sizeof(BITMAP), &bm))
1282 #endif
1283             goto empty_rect;
1284 
1285          r.right  = r.left + bm.bmWidth;
1286          r.bottom = r.top  + bm.bmHeight;
1287          break;
1288 
1289       default:
1290       empty_rect:
1291 #ifndef _USER32_
1292          if (bHasIml)
1293              break;
1294 #endif
1295          rc->right = r.left;
1296          rc->bottom = r.top;
1297          return (UINT)-1;
1298    }
1299 
1300 #ifndef _USER32_
1301    if (bHasIml)
1302    {
1303      if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT)
1304          r.left = pdata->imlData.margin.left;
1305      else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT)
1306          r.right = pdata->imlData.margin.right;
1307      else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_TOP)
1308          r.top = pdata->imlData.margin.top;
1309      else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM)
1310          r.bottom = pdata->imlData.margin.bottom;
1311    }
1312 #endif
1313 
1314    /* Position label inside bounding rectangle according to
1315     * alignment flags. (calculated rect is always left-top aligned).
1316     * If label is aligned to any side - shift label in opposite
1317     * direction to leave extra space for focus rectangle.
1318     */
1319    switch (dtStyle & (DT_CENTER|DT_RIGHT))
1320    {
1321       case DT_LEFT:    r.left++;  r.right++;  break;
1322       case DT_CENTER:  n = r.right - r.left;
1323                        r.left   = rc->left + ((rc->right - rc->left) - n) / 2;
1324                        r.right  = r.left + n; break;
1325       case DT_RIGHT:   n = r.right - r.left;
1326                        r.right  = rc->right - 1;
1327                        r.left   = r.right - n;
1328                        break;
1329    }
1330 
1331    switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
1332    {
1333       case DT_TOP:     r.top++;  r.bottom++;  break;
1334       case DT_VCENTER: n = r.bottom - r.top;
1335                        r.top    = rc->top + ((rc->bottom - rc->top) - n) / 2;
1336                        r.bottom = r.top + n;  break;
1337       case DT_BOTTOM:  n = r.bottom - r.top;
1338                        r.bottom = rc->bottom - 1;
1339                        r.top    = r.bottom - n;
1340                        break;
1341    }
1342 
1343    *rc = r;
1344    return dtStyle;
1345 }
1346 
1347 
1348 /**********************************************************************
1349  *       BUTTON_DrawTextCallback
1350  *
1351  *   Callback function used by DrawStateW function.
1352  */
1353 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
1354 {
1355    RECT rc;
1356 
1357    SetRect(&rc, 0, 0, cx, cy);
1358    DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
1359    return TRUE;
1360 }
1361 
1362 
1363 /**********************************************************************
1364  *       BUTTON_DrawLabel
1365  *
1366  *   Common function for drawing button label.
1367  */
1368  #if defined(_USER32_)
1369 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc)
1370 #else
1371 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc)
1372 #endif
1373 {
1374    DRAWSTATEPROC lpOutputProc = NULL;
1375    LPARAM lp;
1376    WPARAM wp = 0;
1377    HBRUSH hbr = 0;
1378    UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
1379    LONG state = get_button_state( hwnd );
1380    LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1381    WCHAR *text = NULL;
1382 
1383    /* FIXME: To draw disabled label in Win31 look-and-feel, we probably
1384     * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
1385     * I don't have Win31 on hand to verify that, so I leave it as is.
1386     */
1387 
1388 #ifndef _USER32_
1389     PBUTTON_DATA pdata = _GetButtonData(hwnd);
1390     BUTTON_DrawIml(hdc, &pdata->imlData, rc, FALSE, 0);
1391 #endif
1392 
1393    if ((style & BS_PUSHLIKE) && (state & BST_INDETERMINATE))
1394    {
1395       hbr = GetSysColorBrush(COLOR_GRAYTEXT);
1396       flags |= DSS_MONO;
1397    }
1398 
1399    switch (style & (BS_ICON|BS_BITMAP))
1400    {
1401       case BS_TEXT:
1402          /* DST_COMPLEX -- is 0 */
1403          lpOutputProc = BUTTON_DrawTextCallback;
1404          if (!(text = get_button_text( hwnd ))) return;
1405          lp = (LPARAM)text;
1406          wp = (WPARAM)dtFlags;
1407 
1408 #ifdef __REACTOS__
1409          if (dtFlags & DT_HIDEPREFIX)
1410              flags |= DSS_HIDEPREFIX;
1411 #endif
1412          break;
1413 
1414       case BS_ICON:
1415          flags |= DST_ICON;
1416 #ifdef _USER32_
1417          lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
1418 #else
1419          lp = get_button_image(hwnd);
1420 #endif
1421          break;
1422 
1423       case BS_BITMAP:
1424          flags |= DST_BITMAP;
1425 #ifdef _USER32_
1426          lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
1427 #else
1428          lp = get_button_image(hwnd);
1429 #endif
1430          break;
1431 
1432       default:
1433          return;
1434    }
1435 
1436    DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
1437               rc->right - rc->left, rc->bottom - rc->top, flags);
1438    HeapFree( GetProcessHeap(), 0, text );
1439 }
1440 
1441 /**********************************************************************
1442  *       Push Button Functions
1443  */
1444 static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
1445 {
1446     RECT     rc, r;
1447     UINT     dtFlags, uState;
1448     HPEN     hOldPen;
1449     HBRUSH   hOldBrush;
1450     INT      oldBkMode;
1451     COLORREF oldTxtColor;
1452     HFONT hFont;
1453     LONG state = get_button_state( hwnd );
1454     LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1455     BOOL pushedState = (state & BST_PUSHED);
1456     HWND parent;
1457     HRGN hrgn;
1458 #ifndef _USER32_
1459     DWORD cdrf;
1460 #endif
1461 
1462     GetClientRect( hwnd, &rc );
1463 
1464     /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
1465     if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1466     parent = GetParent(hwnd);
1467     if (!parent) parent = hwnd;
1468 #if defined(__REACTOS__) && defined(_USER32_)
1469     GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN);
1470 #else
1471     SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
1472 #endif
1473 
1474     hrgn = set_control_clipping( hDC, &rc );
1475 #ifdef __REACTOS__
1476     hOldPen = SelectObject(hDC, GetStockObject(DC_PEN));
1477     SetDCPenColor(hDC, GetSysColor(COLOR_WINDOWFRAME));
1478 #else
1479     hOldPen = SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
1480 #endif
1481     hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
1482     oldBkMode = SetBkMode(hDC, TRANSPARENT);
1483 
1484     /* completely skip the drawing if only focus has changed */
1485     if (action == ODA_FOCUS) goto draw_focus;
1486 
1487 #ifndef _USER32_
1488     cdrf = BUTTON_SendCustomDraw(hwnd, hDC, CDDS_PREERASE, &rc);
1489     if (cdrf == CDRF_SKIPDEFAULT)
1490         goto cleanup;
1491 #endif
1492 
1493     if (get_button_type(style) == BS_DEFPUSHBUTTON)
1494     {
1495         if (action != ODA_FOCUS)
1496             Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
1497 	InflateRect( &rc, -1, -1 );
1498     }
1499 
1500     uState = DFCS_BUTTONPUSH;
1501 
1502     if (style & BS_FLAT)
1503         uState |= DFCS_MONO;
1504     else if (pushedState)
1505     {
1506 	if (get_button_type(style) == BS_DEFPUSHBUTTON )
1507 	    uState |= DFCS_FLAT;
1508 	else
1509 	    uState |= DFCS_PUSHED;
1510     }
1511 
1512     if (state & (BST_CHECKED | BST_INDETERMINATE))
1513         uState |= DFCS_CHECKED;
1514 
1515     DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
1516 
1517 #ifndef _USER32_
1518     if (cdrf == CDRF_NOTIFYPOSTERASE)
1519         BUTTON_SendCustomDraw(hwnd, hDC, CDDS_POSTERASE, &rc);
1520 
1521     cdrf = BUTTON_SendCustomDraw(hwnd, hDC, CDDS_PREPAINT, &rc);
1522     if (cdrf == CDRF_SKIPDEFAULT)
1523         goto cleanup;
1524 #endif
1525 
1526     /* draw button label */
1527     r = rc;
1528     dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
1529 
1530     if (dtFlags == (UINT)-1L)
1531        goto cleanup;
1532 
1533     if (pushedState)
1534        OffsetRect(&r, 1, 1);
1535 
1536     oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
1537 
1538     BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
1539 
1540     SetTextColor( hDC, oldTxtColor );
1541 
1542 #ifndef _USER32_
1543     if (cdrf == CDRF_NOTIFYPOSTPAINT)
1544         BUTTON_SendCustomDraw(hwnd, hDC, CDDS_POSTPAINT, &rc);
1545 #endif
1546 
1547 draw_focus:
1548     if (action == ODA_FOCUS || (state & BST_FOCUS))
1549     {
1550 #ifdef __REACTOS__
1551         if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
1552         {
1553 #endif
1554             InflateRect( &rc, -2, -2 );
1555             DrawFocusRect( hDC, &rc );
1556 #ifdef __REACTOS__
1557         }
1558 #endif
1559     }
1560 
1561  cleanup:
1562     SelectObject( hDC, hOldPen );
1563     SelectObject( hDC, hOldBrush );
1564     SetBkMode(hDC, oldBkMode);
1565     SelectClipRgn( hDC, hrgn );
1566     if (hrgn) DeleteObject( hrgn );
1567 }
1568 
1569 /**********************************************************************
1570  *       Check Box & Radio Button Functions
1571  */
1572 
1573 static void CB_Paint( HWND hwnd, HDC hDC, UINT action )
1574 {
1575     RECT rbox, rtext, client;
1576     HBRUSH hBrush;
1577     int delta, text_offset, checkBoxWidth, checkBoxHeight;
1578     UINT dtFlags;
1579     HFONT hFont;
1580     LONG state = get_button_state( hwnd );
1581     LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1582     LONG ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE );
1583     HWND parent;
1584     HRGN hrgn;
1585 
1586     if (style & BS_PUSHLIKE)
1587     {
1588         PB_Paint( hwnd, hDC, action );
1589 	return;
1590     }
1591 
1592     GetClientRect(hwnd, &client);
1593     rbox = rtext = client;
1594 
1595     checkBoxWidth  = 12 * GetDeviceCaps( hDC, LOGPIXELSX ) / 96 + 1;
1596     checkBoxHeight = 12 * GetDeviceCaps( hDC, LOGPIXELSY ) / 96 + 1;
1597 
1598     if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1599     GetCharWidthW( hDC, '0', '0', &text_offset );
1600     text_offset /= 2;
1601 
1602     parent = GetParent(hwnd);
1603     if (!parent) parent = hwnd;
1604 #if defined(__REACTOS__) && defined(_USER32_)
1605     hBrush = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC);
1606 #else
1607     hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC,
1608 				  (WPARAM)hDC, (LPARAM)hwnd);
1609     if (!hBrush) /* did the app forget to call defwindowproc ? */
1610         hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
1611 					(WPARAM)hDC, (LPARAM)hwnd );
1612 #endif
1613     hrgn = set_control_clipping( hDC, &client );
1614 
1615     if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT)
1616     {
1617 	/* magic +4 is what CTL3D expects */
1618 
1619         rtext.right -= checkBoxWidth + text_offset;;
1620         rbox.left = rbox.right - checkBoxWidth;
1621     }
1622     else
1623     {
1624         rtext.left += checkBoxWidth + text_offset;;
1625         rbox.right = checkBoxWidth;
1626     }
1627 
1628     /* Since WM_ERASEBKGND does nothing, first prepare background */
1629     if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
1630     if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush );
1631 
1632     /* Draw label */
1633     client = rtext;
1634     dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext);
1635 
1636     /* Only adjust rbox when rtext is valid */
1637     if (dtFlags != (UINT)-1L)
1638     {
1639 	rbox.top = rtext.top;
1640 	rbox.bottom = rtext.bottom;
1641     }
1642 
1643     /* Draw the check-box bitmap */
1644     if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
1645     {
1646 	UINT flags;
1647 
1648 	if ((get_button_type(style) == BS_RADIOBUTTON) ||
1649 	    (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO;
1650 	else if (state & BST_INDETERMINATE) flags = DFCS_BUTTON3STATE;
1651 	else flags = DFCS_BUTTONCHECK;
1652 
1653 	if (state & (BST_CHECKED | BST_INDETERMINATE)) flags |= DFCS_CHECKED;
1654 	if (state & BST_PUSHED) flags |= DFCS_PUSHED;
1655 
1656 	if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
1657 
1658 	/* rbox must have the correct height */
1659 	delta = rbox.bottom - rbox.top - checkBoxHeight;
1660 
1661 	if (style & BS_TOP) {
1662 	    if (delta > 0) {
1663 		rbox.bottom = rbox.top + checkBoxHeight;
1664 	    } else {
1665 		rbox.top -= -delta/2 + 1;
1666 		rbox.bottom = rbox.top + checkBoxHeight;
1667 	    }
1668 	} else if (style & BS_BOTTOM) {
1669 	    if (delta > 0) {
1670 		rbox.top = rbox.bottom - checkBoxHeight;
1671 	    } else {
1672 		rbox.bottom += -delta/2 + 1;
1673 		rbox.top = rbox.bottom - checkBoxHeight;
1674 	    }
1675 	} else { /* Default */
1676 	    if (delta > 0) {
1677 		int ofs = (delta / 2);
1678 		rbox.bottom -= ofs + 1;
1679 		rbox.top = rbox.bottom - checkBoxHeight;
1680 	    } else if (delta < 0) {
1681 		int ofs = (-delta / 2);
1682 		rbox.top -= ofs + 1;
1683 		rbox.bottom = rbox.top + checkBoxHeight;
1684 	    }
1685 	}
1686 
1687 	DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags );
1688     }
1689 
1690     if (dtFlags == (UINT)-1L) /* Noting to draw */
1691 	return;
1692 
1693     if (action == ODA_DRAWENTIRE)
1694 	BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext);
1695 
1696     /* ... and focus */
1697     if (action == ODA_FOCUS || (state & BST_FOCUS))
1698     {
1699 #ifdef __REACTOS__
1700         if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
1701         {
1702 #endif
1703             rtext.left--;
1704             rtext.right++;
1705             IntersectRect(&rtext, &rtext, &client);
1706             DrawFocusRect( hDC, &rtext );
1707 #ifdef __REACTOS__
1708         }
1709 #endif
1710     }
1711     SelectClipRgn( hDC, hrgn );
1712     if (hrgn) DeleteObject( hrgn );
1713 }
1714 
1715 
1716 /**********************************************************************
1717  *       BUTTON_CheckAutoRadioButton
1718  *
1719  * hwnd is checked, uncheck every other auto radio button in group
1720  */
1721 static void BUTTON_CheckAutoRadioButton( HWND hwnd )
1722 {
1723     HWND parent, sibling, start;
1724 
1725     parent = GetParent(hwnd);
1726     /* make sure that starting control is not disabled or invisible */
1727     start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE );
1728     do
1729     {
1730         if (!sibling) break;
1731         if ((hwnd != sibling) &&
1732             ((GetWindowLongPtrW( sibling, GWL_STYLE) & BS_TYPEMASK) == BS_AUTORADIOBUTTON))
1733             SendMessageW( sibling, BM_SETCHECK, BST_UNCHECKED, 0 );
1734         sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
1735     } while (sibling != start);
1736 }
1737 
1738 
1739 /**********************************************************************
1740  *       Group Box Functions
1741  */
1742 
1743 static void GB_Paint( HWND hwnd, HDC hDC, UINT action )
1744 {
1745     RECT rc, rcFrame;
1746     HBRUSH hbr;
1747     HFONT hFont;
1748     UINT dtFlags;
1749     TEXTMETRICW tm;
1750     LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1751     HWND parent;
1752     HRGN hrgn;
1753 
1754     if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1755     /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
1756     parent = GetParent(hwnd);
1757     if (!parent) parent = hwnd;
1758 #if defined(__REACTOS__) && defined(_USER32_)
1759     hbr = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC);
1760 #else
1761     hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd);
1762     if (!hbr) /* did the app forget to call defwindowproc ? */
1763         hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
1764 				     (WPARAM)hDC, (LPARAM)hwnd);
1765 #endif
1766     GetClientRect( hwnd, &rc);
1767     rcFrame = rc;
1768     hrgn = set_control_clipping( hDC, &rc );
1769 
1770     GetTextMetricsW (hDC, &tm);
1771     rcFrame.top += (tm.tmHeight / 2) - 1;
1772     DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
1773 
1774     InflateRect(&rc, -7, 1);
1775     dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc);
1776 
1777     if (dtFlags != (UINT)-1L)
1778     {
1779         /* Because buttons have CS_PARENTDC class style, there is a chance
1780          * that label will be drawn out of client rect.
1781          * But Windows doesn't clip label's rect, so do I.
1782          */
1783 
1784         /* There is 1-pixel margin at the left, right, and bottom */
1785         rc.left--; rc.right++; rc.bottom++;
1786         FillRect(hDC, &rc, hbr);
1787         rc.left++; rc.right--; rc.bottom--;
1788 
1789         BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc);
1790     }
1791     SelectClipRgn( hDC, hrgn );
1792     if (hrgn) DeleteObject( hrgn );
1793 }
1794 
1795 
1796 /**********************************************************************
1797  *       User Button Functions
1798  */
1799 
1800 static void UB_Paint( HWND hwnd, HDC hDC, UINT action )
1801 {
1802     RECT rc;
1803     HBRUSH hBrush;
1804     HFONT hFont;
1805     LONG state = get_button_state( hwnd );
1806     HWND parent;
1807 
1808     GetClientRect( hwnd, &rc);
1809 
1810     if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1811 
1812     parent = GetParent(hwnd);
1813     if (!parent) parent = hwnd;
1814 #if defined(__REACTOS__) && defined(_USER32_)
1815     hBrush = GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN);
1816 #else
1817     hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd);
1818     if (!hBrush) /* did the app forget to call defwindowproc ? */
1819         hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
1820 					(WPARAM)hDC, (LPARAM)hwnd);
1821 #endif
1822 
1823     FillRect( hDC, &rc, hBrush );
1824     if (action == ODA_FOCUS || (state & BST_FOCUS))
1825 #ifdef __REACTOS__
1826     {
1827         if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
1828 #endif
1829             DrawFocusRect( hDC, &rc );
1830 #ifdef __REACTOS__
1831     }
1832 #endif
1833 
1834     switch (action)
1835     {
1836     case ODA_FOCUS:
1837         BUTTON_NOTIFY_PARENT( hwnd, (state & BST_FOCUS) ? BN_SETFOCUS : BN_KILLFOCUS );
1838         break;
1839 
1840     case ODA_SELECT:
1841         BUTTON_NOTIFY_PARENT( hwnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE );
1842         break;
1843 
1844     default:
1845         BUTTON_NOTIFY_PARENT( hwnd, BN_PAINT );
1846         break;
1847     }
1848 }
1849 
1850 
1851 /**********************************************************************
1852  *       Ownerdrawn Button Functions
1853  */
1854 
1855 static void OB_Paint( HWND hwnd, HDC hDC, UINT action )
1856 {
1857     LONG state = get_button_state( hwnd );
1858     DRAWITEMSTRUCT dis;
1859     LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID );
1860     HWND parent;
1861     HFONT hFont, hPrevFont = 0;
1862     HRGN hrgn;
1863 
1864     dis.CtlType    = ODT_BUTTON;
1865     dis.CtlID      = id;
1866     dis.itemID     = 0;
1867     dis.itemAction = action;
1868     dis.itemState  = ((state & BST_FOCUS) ? ODS_FOCUS : 0) |
1869                      ((state & BST_PUSHED) ? ODS_SELECTED : 0) |
1870                      (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED);
1871     dis.hwndItem   = hwnd;
1872     dis.hDC        = hDC;
1873     dis.itemData   = 0;
1874     GetClientRect( hwnd, &dis.rcItem );
1875 
1876     if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont );
1877     parent = GetParent(hwnd);
1878     if (!parent) parent = hwnd;
1879 #if defined(__REACTOS__) && defined(_USER32_)
1880     GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN);
1881 #else
1882     SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
1883 #endif
1884 
1885     hrgn = set_control_clipping( hDC, &dis.rcItem );
1886 
1887     SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
1888     if (hPrevFont) SelectObject(hDC, hPrevFont);
1889     SelectClipRgn( hDC, hrgn );
1890     if (hrgn) DeleteObject( hrgn );
1891 }
1892 
1893 #ifndef _USER32_
1894 void BUTTON_Register()
1895 {
1896     WNDCLASSW wndClass;
1897 
1898     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
1899     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC;
1900     wndClass.lpfnWndProc   = ButtonWndProcW;
1901     wndClass.cbClsExtra    = 0;
1902     wndClass.cbWndExtra    = sizeof(PBUTTON_DATA);
1903     wndClass.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
1904     wndClass.hbrBackground = 0;
1905     wndClass.lpszClassName = WC_BUTTONW;
1906 
1907     RegisterClassW(&wndClass);
1908 }
1909 
1910 void BUTTON_Unregister()
1911 {
1912     UnregisterClassW(WC_BUTTONW, NULL);
1913 }
1914 #endif