[COMCTL32] -Implement BCM_GETTEXTMARGIN, BCM_SETTEXTMARGIN, BCM_SETIMAGELIST and...
[reactos.git] / reactos / dll / win32 / comctl32 / button.c
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
241 static inline LONG_PTR get_button_image(HWND hwnd)
242 {
243 return _GetButtonData(hwnd)->image;
244 }
245
246 static inline LONG_PTR set_button_image(HWND hwnd, LONG_PTR image)
247 {
248 PBUTTON_DATA data = _GetButtonData(hwnd);
249 LONG_PTR ret = data->image;
250 data->image = image;
251 return ret;
252 }
253
254 static inline LONG get_button_state( HWND hwnd )
255 {
256 return _GetButtonData(hwnd)->state;
257 }
258
259 static inline void set_button_state( HWND hwnd, LONG state )
260 {
261 _GetButtonData(hwnd)->state = state;
262 }
263
264 static __inline void set_ui_state( HWND hwnd, LONG flags )
265 {
266 _GetButtonData(hwnd)->ui_state = flags;
267 }
268
269 static __inline LONG get_ui_state( HWND hwnd )
270 {
271 return _GetButtonData(hwnd)->ui_state;
272 }
273
274 static inline HFONT get_button_font( HWND hwnd )
275 {
276 return (HFONT)_GetButtonData(hwnd)->font;
277 }
278
279 static inline void set_button_font( HWND hwnd, HFONT font )
280 {
281 _GetButtonData(hwnd)->font = font;
282 }
283
284 static inline UINT get_button_type( LONG window_style )
285 {
286 return (window_style & BS_TYPEMASK);
287 }
288
289 /* paint a button of any type */
290 static inline void paint_button( HWND hwnd, LONG style, UINT action )
291 {
292 HTHEME theme = GetWindowTheme(hwnd);
293 RECT rc;
294 HDC hdc = GetDC( hwnd );
295 /* GetDC appears to give a dc with a clip rect that includes the whoe parent, not sure if it is correct or not. */
296 GetClientRect(hwnd, &rc);
297 IntersectClipRect (hdc, rc.left, rc. top, rc.right, rc.bottom);
298 if (theme && BUTTON_PaintWithTheme(theme, hwnd, hdc, 0))
299 {
300 ReleaseDC( hwnd, hdc );
301 return;
302 }
303 if (btnPaintFunc[style] && IsWindowVisible(hwnd))
304 {
305 btnPaintFunc[style]( hwnd, hdc, action );
306 }
307 ReleaseDC( hwnd, hdc );
308 }
309
310 #endif
311
312
313 /* retrieve the button text; returned buffer must be freed by caller */
314 static inline WCHAR *get_button_text( HWND hwnd )
315 {
316 INT len = 512;
317 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
318 if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 );
319 return buffer;
320 }
321
322 #ifdef __REACTOS__
323 /* Retrieve the UI state for the control */
324 static BOOL button_update_uistate(HWND hwnd, BOOL unicode)
325 {
326 LONG flags, prevflags;
327
328 if (unicode)
329 flags = DefWindowProcW(hwnd, WM_QUERYUISTATE, 0, 0);
330 else
331 flags = DefWindowProcA(hwnd, WM_QUERYUISTATE, 0, 0);
332
333 prevflags = get_ui_state(hwnd);
334
335 if (prevflags != flags)
336 {
337 set_ui_state(hwnd, flags);
338 return TRUE;
339 }
340
341 return FALSE;
342 }
343 #endif
344
345 /***********************************************************************
346 * ButtonWndProc_common
347 */
348 LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
349 WPARAM wParam, LPARAM lParam, BOOL unicode )
350 {
351 RECT rect;
352 POINT pt;
353 LONG style = GetWindowLongPtrW( hWnd, GWL_STYLE );
354 UINT btn_type = get_button_type( style );
355 LONG state;
356 HANDLE oldHbitmap;
357 #if defined(__REACTOS__) && defined(_USER32_)
358 PWND pWnd;
359
360 pWnd = ValidateHwnd(hWnd);
361 if (pWnd)
362 {
363 if (!pWnd->fnid)
364 {
365 NtUserSetWindowFNID(hWnd, FNID_BUTTON);
366 }
367 else
368 {
369 if (pWnd->fnid != FNID_BUTTON)
370 {
371 ERR("Wrong window class for Button! fnId 0x%x\n",pWnd->fnid);
372 return 0;
373 }
374 }
375 }
376 else
377 return 0;
378 #else
379 if (!IsWindow( hWnd )) return 0;
380 #endif
381
382 pt.x = (short)LOWORD(lParam);
383 pt.y = (short)HIWORD(lParam);
384
385 #ifndef _USER32_
386 switch (uMsg)
387 {
388 case WM_NCCREATE:
389 {
390 PBUTTON_DATA data = HeapAlloc( GetProcessHeap(), 0, sizeof(BUTTON_DATA) );
391 if (!data)
392 {
393 ERR("Failed to alloc internal button data\n");
394 return -1;
395 }
396
397 memset(data, 0, sizeof(BUTTON_DATA));
398 SetRect(&data->rcTextMargin, 1,1,1,1);
399
400 _SetButtonData(hWnd, data);
401 break;
402 }
403 case WM_NCDESTROY:
404 {
405 PBUTTON_DATA data = _GetButtonData(hWnd);
406 if (!data)
407 {
408 ERR("No data");
409 return 0;
410 }
411 HeapFree( GetProcessHeap(), 0, data );
412 _SetButtonData(hWnd, NULL);
413 }
414 case WM_CREATE:
415 OpenThemeData(hWnd, WC_BUTTONW);
416 break;
417 case WM_DESTROY:
418 CloseThemeData (GetWindowTheme(hWnd));
419 break;
420 case WM_THEMECHANGED:
421 CloseThemeData (GetWindowTheme(hWnd));
422 OpenThemeData(hWnd, WC_BUTTONW);
423 InvalidateRect(hWnd, NULL, FALSE);
424 break;
425 case WM_MOUSEHOVER:
426 {
427 int state = (int)SendMessageW(hWnd, BM_GETSTATE, 0, 0);
428 set_button_state(hWnd, state|BST_HOT);
429 InvalidateRect(hWnd, NULL, FALSE);
430 break;
431 }
432 case WM_MOUSELEAVE:
433 {
434 int state = (int)SendMessageW(hWnd, BM_GETSTATE, 0, 0);
435 set_button_state(hWnd, state&(~BST_HOT));
436 InvalidateRect(hWnd, NULL, FALSE);
437 break;
438 }
439 case WM_MOUSEMOVE:
440 {
441 TRACKMOUSEEVENT mouse_event;
442 mouse_event.cbSize = sizeof(TRACKMOUSEEVENT);
443 mouse_event.dwFlags = TME_QUERY;
444 if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&(TME_HOVER|TME_LEAVE)))
445 {
446 mouse_event.dwFlags = TME_HOVER|TME_LEAVE;
447 mouse_event.hwndTrack = hWnd;
448 mouse_event.dwHoverTime = 1;
449 TrackMouseEvent(&mouse_event);
450 }
451 break;
452 }
453 case BCM_GETTEXTMARGIN:
454 {
455 RECT* prc = (RECT*)lParam;
456 PBUTTON_DATA data = _GetButtonData(hWnd);
457 if (!prc || !data)
458 return FALSE;
459 *prc = data->rcTextMargin;
460 return TRUE;
461 }
462 case BCM_SETTEXTMARGIN:
463 {
464 RECT* prc = (RECT*)lParam;
465 PBUTTON_DATA data = _GetButtonData(hWnd);
466 if (!prc || !data)
467 return FALSE;
468 data->rcTextMargin = *prc;
469 return TRUE;
470 }
471 case BCM_SETIMAGELIST:
472 {
473 BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam;
474 PBUTTON_DATA data = _GetButtonData(hWnd);
475 if (!data || !pimldata || !pimldata->himl)
476 return FALSE;
477 data->imlData = *pimldata;
478 return TRUE;
479 }
480 case BCM_GETIMAGELIST:
481 {
482 BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam;
483 PBUTTON_DATA data = _GetButtonData(hWnd);
484 if (!data|| !pimldata)
485 return FALSE;
486 *pimldata = data->imlData;
487 return TRUE;
488 }
489 }
490
491 if (!_GetButtonData(hWnd))
492 {
493 ERR("no data!\n");
494 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
495 DefWindowProcA(hWnd, uMsg, wParam, lParam);
496 }
497
498 #endif
499
500 switch (uMsg)
501 {
502 case WM_GETDLGCODE:
503 switch(btn_type)
504 {
505 case BS_USERBUTTON:
506 case BS_PUSHBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
507 case BS_DEFPUSHBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
508 case BS_RADIOBUTTON:
509 case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON;
510 case BS_GROUPBOX: return DLGC_STATIC;
511 default: return DLGC_BUTTON;
512 }
513
514 case WM_ENABLE:
515 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
516 break;
517
518 case WM_CREATE:
519 if (btn_type >= MAX_BTN_TYPE)
520 return -1; /* abort */
521
522 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
523 if (btn_type == BS_USERBUTTON )
524 {
525 style = (style & ~BS_TYPEMASK) | BS_PUSHBUTTON;
526 #ifdef __REACTOS__
527 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style );
528 #else
529 WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style );
530 #endif
531 }
532 set_button_state( hWnd, BST_UNCHECKED );
533 #ifdef __REACTOS__
534 button_update_uistate( hWnd, unicode );
535 #endif
536 return 0;
537
538 #if defined(__REACTOS__) && defined(_USER32_)
539 case WM_NCDESTROY:
540 NtUserSetWindowFNID(hWnd, FNID_DESTROY);
541 case WM_DESTROY:
542 break;
543 #endif
544 case WM_ERASEBKGND:
545 if (btn_type == BS_OWNERDRAW)
546 {
547 HDC hdc = (HDC)wParam;
548 RECT rc;
549 HBRUSH hBrush;
550 HWND parent = GetParent(hWnd);
551 if (!parent) parent = hWnd;
552 #if defined(__REACTOS__) && defined(_USER32_)
553 hBrush = GetControlColor( parent, hWnd, hdc, WM_CTLCOLORBTN);
554 #else
555 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd);
556 if (!hBrush) /* did the app forget to call defwindowproc ? */
557 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
558 (WPARAM)hdc, (LPARAM)hWnd);
559 #endif
560 GetClientRect(hWnd, &rc);
561 FillRect(hdc, &rc, hBrush);
562 }
563 return 1;
564
565 case WM_PRINTCLIENT:
566 case WM_PAINT:
567 {
568 PAINTSTRUCT ps;
569 HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
570 #ifndef _USER32_
571 HTHEME theme = GetWindowTheme(hWnd);
572 if (theme && BUTTON_PaintWithTheme(theme, hWnd, hdc, uMsg == WM_PRINTCLIENT ? lParam : 0))
573 {
574 if ( !wParam ) EndPaint( hWnd, &ps );
575 return 0;
576 }
577 #endif
578 if (btnPaintFunc[btn_type])
579 {
580 int nOldMode = SetBkMode( hdc, OPAQUE );
581 (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE );
582 SetBkMode(hdc, nOldMode); /* reset painting mode */
583 }
584 if ( !wParam ) EndPaint( hWnd, &ps );
585 break;
586 }
587
588 case WM_KEYDOWN:
589 if (wParam == VK_SPACE)
590 {
591 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
592 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
593 SetCapture( hWnd );
594 }
595 break;
596
597 case WM_LBUTTONDBLCLK:
598 if(style & BS_NOTIFY ||
599 btn_type == BS_RADIOBUTTON ||
600 btn_type == BS_USERBUTTON ||
601 btn_type == BS_OWNERDRAW)
602 {
603 BUTTON_NOTIFY_PARENT(hWnd, BN_DOUBLECLICKED);
604 break;
605 }
606 /* fall through */
607 case WM_LBUTTONDOWN:
608 SetCapture( hWnd );
609 SetFocus( hWnd );
610 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
611 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
612 break;
613
614 case WM_KEYUP:
615 if (wParam != VK_SPACE)
616 break;
617 /* fall through */
618 case WM_LBUTTONUP:
619 #ifdef _REACTOS_
620 BOOL TellParent = FALSE; //// ReactOS see note below.
621 #endif
622 state = get_button_state( hWnd );
623 if (!(state & BUTTON_BTNPRESSED)) break;
624 state &= BUTTON_NSTATES;
625 set_button_state( hWnd, state );
626 if (!(state & BST_PUSHED))
627 {
628 ReleaseCapture();
629 break;
630 }
631 SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
632 GetClientRect( hWnd, &rect );
633 if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
634 {
635 state = get_button_state( hWnd );
636 switch(btn_type)
637 {
638 case BS_AUTOCHECKBOX:
639 SendMessageW( hWnd, BM_SETCHECK, !(state & BST_CHECKED), 0 );
640 break;
641 case BS_AUTORADIOBUTTON:
642 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
643 break;
644 case BS_AUTO3STATE:
645 SendMessageW( hWnd, BM_SETCHECK,
646 (state & BST_INDETERMINATE) ? 0 : ((state & 3) + 1), 0 );
647 break;
648 }
649 #ifdef _REACTOS_
650 TellParent = TRUE; // <---- Fix CORE-10194, Notify parent after capture is released.
651 #else
652 ReleaseCapture();
653 BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED);
654 #endif
655 }
656 #ifdef _REACTOS_
657 ReleaseCapture();
658 if (TellParent) BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED);
659 #else
660 else
661 {
662 ReleaseCapture();
663 }
664 #endif
665 break;
666
667 case WM_CAPTURECHANGED:
668 TRACE("WM_CAPTURECHANGED %p\n", hWnd);
669 if (hWnd == (HWND)lParam) break;
670 state = get_button_state( hWnd );
671 if (state & BUTTON_BTNPRESSED)
672 {
673 state &= BUTTON_NSTATES;
674 set_button_state( hWnd, state );
675 if (state & BST_PUSHED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
676 }
677 break;
678
679 case WM_MOUSEMOVE:
680 if ((wParam & MK_LBUTTON) && GetCapture() == hWnd)
681 {
682 GetClientRect( hWnd, &rect );
683 SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
684 }
685 break;
686
687 case WM_SETTEXT:
688 {
689 /* Clear an old text here as Windows does */
690 //
691 // ReactOS Note :
692 // wine Bug: http://bugs.winehq.org/show_bug.cgi?id=25790
693 // Patch: http://source.winehq.org/patches/data/70889
694 // By: Alexander LAW, Replicate Windows behavior of WM_SETTEXT handler regarding WM_CTLCOLOR*
695 //
696 #ifdef __REACTOS__
697 if (style & WS_VISIBLE)
698 #else
699 if (IsWindowVisible(hWnd))
700 #endif
701 {
702 HDC hdc = GetDC(hWnd);
703 HBRUSH hbrush;
704 RECT client, rc;
705 HWND parent = GetParent(hWnd);
706 UINT message = (btn_type == BS_PUSHBUTTON ||
707 btn_type == BS_DEFPUSHBUTTON ||
708 btn_type == BS_PUSHLIKE ||
709 btn_type == BS_USERBUTTON ||
710 btn_type == BS_OWNERDRAW) ?
711 WM_CTLCOLORBTN : WM_CTLCOLORSTATIC;
712
713 if (!parent) parent = hWnd;
714 #if defined(__REACTOS__) && defined(_USER32_)
715 hbrush = GetControlColor(parent, hWnd, hdc, message);
716 #else
717 hbrush = (HBRUSH)SendMessageW(parent, message,
718 (WPARAM)hdc, (LPARAM)hWnd);
719 if (!hbrush) /* did the app forget to call DefWindowProc ? */
720 hbrush = (HBRUSH)DefWindowProcW(parent, message,
721 (WPARAM)hdc, (LPARAM)hWnd);
722 #endif
723
724 GetClientRect(hWnd, &client);
725 rc = client;
726 /* FIXME: check other BS_* handlers */
727 if (btn_type == BS_GROUPBOX)
728 InflateRect(&rc, -7, 1); /* GB_Paint does this */
729 BUTTON_CalcLabelRect(hWnd, hdc, &rc);
730 /* Clip by client rect bounds */
731 if (rc.right > client.right) rc.right = client.right;
732 if (rc.bottom > client.bottom) rc.bottom = client.bottom;
733 FillRect(hdc, &rc, hbrush);
734 ReleaseDC(hWnd, hdc);
735 }
736
737 if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
738 else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam );
739 if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */
740 InvalidateRect( hWnd, NULL, TRUE );
741 else
742 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
743 return 1; /* success. FIXME: check text length */
744 }
745
746 case WM_SETFONT:
747 set_button_font( hWnd, (HFONT)wParam );
748 if (lParam) InvalidateRect(hWnd, NULL, TRUE);
749 break;
750
751 case WM_GETFONT:
752 return (LRESULT)get_button_font( hWnd );
753
754 case WM_SETFOCUS:
755 TRACE("WM_SETFOCUS %p\n",hWnd);
756 set_button_state( hWnd, get_button_state(hWnd) | BST_FOCUS );
757 #ifndef _USER32_
758 if (btn_type != BS_OWNERDRAW)
759 InvalidateRect(hWnd, NULL, FALSE);
760 else
761 #endif
762 paint_button( hWnd, btn_type, ODA_FOCUS );
763 if (style & BS_NOTIFY)
764 BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS);
765 break;
766
767 case WM_KILLFOCUS:
768 TRACE("WM_KILLFOCUS %p\n",hWnd);
769 state = get_button_state( hWnd );
770 set_button_state( hWnd, state & ~BST_FOCUS );
771 #ifdef _USER32_
772 paint_button( hWnd, btn_type, ODA_FOCUS );
773 #endif
774
775 if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd)
776 ReleaseCapture();
777 if (style & BS_NOTIFY)
778 BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS);
779
780 InvalidateRect( hWnd, NULL, FALSE );
781 break;
782
783 case WM_SYSCOLORCHANGE:
784 InvalidateRect( hWnd, NULL, FALSE );
785 break;
786
787 case BM_SETSTYLE:
788 btn_type = wParam & BS_TYPEMASK;
789 style = (style & ~BS_TYPEMASK) | btn_type;
790 #ifdef __REACTOS__
791 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style);
792 #else
793 WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style );
794 #endif
795
796 /* Only redraw if lParam flag is set.*/
797 if (lParam)
798 InvalidateRect( hWnd, NULL, TRUE );
799
800 break;
801
802 case BM_CLICK:
803 #ifdef __REACTOS__
804 state = get_button_state(hWnd);
805 if (state & BUTTON_BMCLICK)
806 break;
807 set_button_state(hWnd, state | BUTTON_BMCLICK); // Tracked in STATE_GWL_OFFSET.
808 #endif
809 SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
810 SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
811 #ifdef __REACTOS__
812 state = get_button_state(hWnd);
813 if (!(state & BUTTON_BMCLICK)) break;
814 state &= ~BUTTON_BMCLICK;
815 set_button_state(hWnd, state);
816 #endif
817 break;
818
819 case BM_SETIMAGE:
820 /* Check that image format matches button style */
821 switch (style & (BS_BITMAP|BS_ICON))
822 {
823 case BS_BITMAP:
824 if (wParam != IMAGE_BITMAP) return 0;
825 break;
826 case BS_ICON:
827 if (wParam != IMAGE_ICON) return 0;
828 break;
829 default:
830 return 0;
831 }
832 #ifdef _USER32_
833 oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam );
834 #else
835 oldHbitmap = (HBITMAP)set_button_image(hWnd, lParam );
836 #endif
837 InvalidateRect( hWnd, NULL, FALSE );
838 return (LRESULT)oldHbitmap;
839
840 case BM_GETIMAGE:
841 #ifdef _USER32_
842 return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET );
843 #else
844 return get_button_image(hWnd);
845 #endif
846
847 case BM_GETCHECK:
848 return get_button_state( hWnd ) & 3;
849
850 case BM_SETCHECK:
851 if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
852 state = get_button_state( hWnd );
853 if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
854 {
855 #ifdef __REACTOS__
856 if (wParam) style |= WS_TABSTOP;
857 else style &= ~WS_TABSTOP;
858 NtUserAlterWindowStyle(hWnd, GWL_STYLE, style);
859 #else
860 if (wParam) WIN_SetStyle( hWnd, WS_TABSTOP, 0 );
861 else WIN_SetStyle( hWnd, 0, WS_TABSTOP );
862 #endif
863 }
864 if ((state & 3) != wParam)
865 {
866 set_button_state( hWnd, (state & ~3) | wParam );
867 #ifdef _USER32
868 paint_button( hWnd, btn_type, ODA_SELECT );
869 #else
870 InvalidateRect(hWnd, NULL, FALSE);
871 #endif
872 }
873 if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD))
874 BUTTON_CheckAutoRadioButton( hWnd );
875 break;
876
877 case BM_GETSTATE:
878 return get_button_state( hWnd );
879
880 case BM_SETSTATE:
881 state = get_button_state( hWnd );
882 if (wParam)
883 set_button_state( hWnd, state | BST_PUSHED );
884 else
885 set_button_state( hWnd, state & ~BST_PUSHED );
886
887 #ifdef _USER32_
888 paint_button( hWnd, btn_type, ODA_SELECT );
889 #else
890 InvalidateRect(hWnd, NULL, FALSE);
891 #endif
892 break;
893
894 #ifdef __REACTOS__
895 case WM_UPDATEUISTATE:
896 if (unicode)
897 DefWindowProcW(hWnd, uMsg, wParam, lParam);
898 else
899 DefWindowProcA(hWnd, uMsg, wParam, lParam);
900
901 if (button_update_uistate(hWnd, unicode))
902 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
903 break;
904 #endif
905
906 case WM_NCHITTEST:
907 if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
908 /* fall through */
909 default:
910 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
911 DefWindowProcA(hWnd, uMsg, wParam, lParam);
912 }
913 return 0;
914 }
915
916 #ifdef __REACTOS__
917
918 /***********************************************************************
919 * ButtonWndProcW
920 * The button window procedure. This is just a wrapper which locks
921 * the passed HWND and calls the real window procedure (with a WND*
922 * pointer pointing to the locked windowstructure).
923 */
924 LRESULT WINAPI ButtonWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
925 {
926 if (!IsWindow(hWnd)) return 0;
927 return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
928 }
929
930 /***********************************************************************
931 * ButtonWndProcA
932 */
933 LRESULT WINAPI ButtonWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
934 {
935 if (!IsWindow(hWnd)) return 0;
936 return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
937 }
938
939 #endif /* __REACTOS__ */
940
941 /**********************************************************************
942 * Convert button styles to flags used by DrawText.
943 */
944 static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style )
945 {
946 UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */
947
948 /* "Convert" pushlike buttons to pushbuttons */
949 if (style & BS_PUSHLIKE)
950 style &= ~BS_TYPEMASK;
951
952 if (!(style & BS_MULTILINE))
953 dtStyle |= DT_SINGLELINE;
954 else
955 dtStyle |= DT_WORDBREAK;
956
957 switch (style & BS_CENTER)
958 {
959 case BS_LEFT: /* DT_LEFT is 0 */ break;
960 case BS_RIGHT: dtStyle |= DT_RIGHT; break;
961 case BS_CENTER: dtStyle |= DT_CENTER; break;
962 default:
963 /* Pushbutton's text is centered by default */
964 if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
965 /* all other flavours have left aligned text */
966 }
967
968 if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER));
969
970 /* DrawText ignores vertical alignment for multiline text,
971 * but we use these flags to align label manually.
972 */
973 if (get_button_type(style) != BS_GROUPBOX)
974 {
975 switch (style & BS_VCENTER)
976 {
977 case BS_TOP: /* DT_TOP is 0 */ break;
978 case BS_BOTTOM: dtStyle |= DT_BOTTOM; break;
979 case BS_VCENTER: /* fall through */
980 default: dtStyle |= DT_VCENTER; break;
981 }
982 }
983 else
984 /* GroupBox's text is always single line and is top aligned. */
985 dtStyle |= DT_SINGLELINE;
986
987 return dtStyle;
988 }
989
990 /**********************************************************************
991 * BUTTON_CalcLabelRect
992 *
993 * Calculates label's rectangle depending on button style.
994 *
995 * Returns flags to be passed to DrawText.
996 * Calculated rectangle doesn't take into account button state
997 * (pushed, etc.). If there is nothing to draw (no text/image) output
998 * rectangle is empty, and return value is (UINT)-1.
999 */
1000 static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
1001 {
1002 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1003 LONG ex_style = GetWindowLongPtrW( hwnd, GWL_EXSTYLE );
1004 WCHAR *text;
1005 ICONINFO iconInfo;
1006 BITMAP bm;
1007 UINT dtStyle = BUTTON_BStoDT( style, ex_style );
1008 RECT r = *rc;
1009 INT n;
1010
1011 /* Calculate label rectangle according to label type */
1012 switch (style & (BS_ICON|BS_BITMAP))
1013 {
1014 case BS_TEXT:
1015 {
1016 HFONT hFont, hPrevFont = 0;
1017
1018 if (!(text = get_button_text( hwnd ))) goto empty_rect;
1019 if (!text[0])
1020 {
1021 HeapFree( GetProcessHeap(), 0, text );
1022 goto empty_rect;
1023 }
1024
1025 if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hdc, hFont );
1026 DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
1027 if (hPrevFont) SelectObject( hdc, hPrevFont );
1028 HeapFree( GetProcessHeap(), 0, text );
1029 #ifdef __REACTOS__
1030 if (get_ui_state(hwnd) & UISF_HIDEACCEL)
1031 dtStyle |= DT_HIDEPREFIX;
1032 #endif
1033 break;
1034 }
1035
1036 case BS_ICON:
1037 #ifdef _USER32_
1038 if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
1039 #else
1040 if (!GetIconInfo((HICON)get_button_image(hwnd), &iconInfo))
1041 #endif
1042 goto empty_rect;
1043
1044 GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
1045
1046 r.right = r.left + bm.bmWidth;
1047 r.bottom = r.top + bm.bmHeight;
1048
1049 DeleteObject(iconInfo.hbmColor);
1050 DeleteObject(iconInfo.hbmMask);
1051 break;
1052
1053 case BS_BITMAP:
1054 #ifdef _USER32_
1055 if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
1056 #else
1057 if (!GetObjectW( (HANDLE)get_button_image(hwnd), sizeof(BITMAP), &bm))
1058 #endif
1059 goto empty_rect;
1060
1061 r.right = r.left + bm.bmWidth;
1062 r.bottom = r.top + bm.bmHeight;
1063 break;
1064
1065 default:
1066 empty_rect:
1067 rc->right = r.left;
1068 rc->bottom = r.top;
1069 return (UINT)-1;
1070 }
1071
1072 /* Position label inside bounding rectangle according to
1073 * alignment flags. (calculated rect is always left-top aligned).
1074 * If label is aligned to any side - shift label in opposite
1075 * direction to leave extra space for focus rectangle.
1076 */
1077 switch (dtStyle & (DT_CENTER|DT_RIGHT))
1078 {
1079 case DT_LEFT: r.left++; r.right++; break;
1080 case DT_CENTER: n = r.right - r.left;
1081 r.left = rc->left + ((rc->right - rc->left) - n) / 2;
1082 r.right = r.left + n; break;
1083 case DT_RIGHT: n = r.right - r.left;
1084 r.right = rc->right - 1;
1085 r.left = r.right - n;
1086 break;
1087 }
1088
1089 switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
1090 {
1091 case DT_TOP: r.top++; r.bottom++; break;
1092 case DT_VCENTER: n = r.bottom - r.top;
1093 r.top = rc->top + ((rc->bottom - rc->top) - n) / 2;
1094 r.bottom = r.top + n; break;
1095 case DT_BOTTOM: n = r.bottom - r.top;
1096 r.bottom = rc->bottom - 1;
1097 r.top = r.bottom - n;
1098 break;
1099 }
1100
1101 *rc = r;
1102 return dtStyle;
1103 }
1104
1105
1106 /**********************************************************************
1107 * BUTTON_DrawTextCallback
1108 *
1109 * Callback function used by DrawStateW function.
1110 */
1111 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
1112 {
1113 RECT rc;
1114
1115 SetRect(&rc, 0, 0, cx, cy);
1116 DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
1117 return TRUE;
1118 }
1119
1120
1121 /**********************************************************************
1122 * BUTTON_DrawLabel
1123 *
1124 * Common function for drawing button label.
1125 */
1126 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc)
1127 {
1128 DRAWSTATEPROC lpOutputProc = NULL;
1129 LPARAM lp;
1130 WPARAM wp = 0;
1131 HBRUSH hbr = 0;
1132 UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
1133 LONG state = get_button_state( hwnd );
1134 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1135 WCHAR *text = NULL;
1136
1137 /* FIXME: To draw disabled label in Win31 look-and-feel, we probably
1138 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
1139 * I don't have Win31 on hand to verify that, so I leave it as is.
1140 */
1141
1142 if ((style & BS_PUSHLIKE) && (state & BST_INDETERMINATE))
1143 {
1144 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
1145 flags |= DSS_MONO;
1146 }
1147
1148 switch (style & (BS_ICON|BS_BITMAP))
1149 {
1150 case BS_TEXT:
1151 /* DST_COMPLEX -- is 0 */
1152 lpOutputProc = BUTTON_DrawTextCallback;
1153 if (!(text = get_button_text( hwnd ))) return;
1154 lp = (LPARAM)text;
1155 wp = (WPARAM)dtFlags;
1156
1157 #ifdef __REACTOS__
1158 if (dtFlags & DT_HIDEPREFIX)
1159 flags |= DSS_HIDEPREFIX;
1160 #endif
1161 break;
1162
1163 case BS_ICON:
1164 flags |= DST_ICON;
1165 #ifdef _USER32_
1166 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
1167 #else
1168 lp = get_button_image(hwnd);
1169 #endif
1170 break;
1171
1172 case BS_BITMAP:
1173 flags |= DST_BITMAP;
1174 #ifdef _USER32_
1175 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
1176 #else
1177 lp = get_button_image(hwnd);
1178 #endif
1179 break;
1180
1181 default:
1182 return;
1183 }
1184
1185 DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
1186 rc->right - rc->left, rc->bottom - rc->top, flags);
1187 HeapFree( GetProcessHeap(), 0, text );
1188 }
1189
1190 /**********************************************************************
1191 * Push Button Functions
1192 */
1193 static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
1194 {
1195 RECT rc, r;
1196 UINT dtFlags, uState;
1197 HPEN hOldPen;
1198 HBRUSH hOldBrush;
1199 INT oldBkMode;
1200 COLORREF oldTxtColor;
1201 HFONT hFont;
1202 LONG state = get_button_state( hwnd );
1203 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1204 BOOL pushedState = (state & BST_PUSHED);
1205 HWND parent;
1206 HRGN hrgn;
1207
1208 GetClientRect( hwnd, &rc );
1209
1210 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
1211 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1212 parent = GetParent(hwnd);
1213 if (!parent) parent = hwnd;
1214 #if defined(__REACTOS__) && defined(_USER32_)
1215 GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN);
1216 #else
1217 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
1218 #endif
1219
1220 hrgn = set_control_clipping( hDC, &rc );
1221 #ifdef __REACTOS__
1222 hOldPen = SelectObject(hDC, GetStockObject(DC_PEN));
1223 SetDCPenColor(hDC, GetSysColor(COLOR_WINDOWFRAME));
1224 #else
1225 hOldPen = SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
1226 #endif
1227 hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
1228 oldBkMode = SetBkMode(hDC, TRANSPARENT);
1229
1230 if (get_button_type(style) == BS_DEFPUSHBUTTON)
1231 {
1232 if (action != ODA_FOCUS)
1233 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
1234 InflateRect( &rc, -1, -1 );
1235 }
1236
1237 /* completely skip the drawing if only focus has changed */
1238 if (action == ODA_FOCUS) goto draw_focus;
1239
1240 uState = DFCS_BUTTONPUSH;
1241
1242 if (style & BS_FLAT)
1243 uState |= DFCS_MONO;
1244 else if (pushedState)
1245 {
1246 if (get_button_type(style) == BS_DEFPUSHBUTTON )
1247 uState |= DFCS_FLAT;
1248 else
1249 uState |= DFCS_PUSHED;
1250 }
1251
1252 if (state & (BST_CHECKED | BST_INDETERMINATE))
1253 uState |= DFCS_CHECKED;
1254
1255 DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
1256
1257 /* draw button label */
1258 r = rc;
1259 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
1260
1261 if (dtFlags == (UINT)-1L)
1262 goto cleanup;
1263
1264 if (pushedState)
1265 OffsetRect(&r, 1, 1);
1266
1267 oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
1268
1269 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
1270
1271 SetTextColor( hDC, oldTxtColor );
1272
1273 draw_focus:
1274 if (action == ODA_FOCUS || (state & BST_FOCUS))
1275 {
1276 #ifdef __REACTOS__
1277 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
1278 {
1279 #endif
1280 InflateRect( &rc, -2, -2 );
1281 DrawFocusRect( hDC, &rc );
1282 #ifdef __REACTOS__
1283 }
1284 #endif
1285 }
1286
1287 cleanup:
1288 SelectObject( hDC, hOldPen );
1289 SelectObject( hDC, hOldBrush );
1290 SetBkMode(hDC, oldBkMode);
1291 SelectClipRgn( hDC, hrgn );
1292 if (hrgn) DeleteObject( hrgn );
1293 }
1294
1295 /**********************************************************************
1296 * Check Box & Radio Button Functions
1297 */
1298
1299 static void CB_Paint( HWND hwnd, HDC hDC, UINT action )
1300 {
1301 RECT rbox, rtext, client;
1302 HBRUSH hBrush;
1303 int delta, text_offset, checkBoxWidth, checkBoxHeight;
1304 UINT dtFlags;
1305 HFONT hFont;
1306 LONG state = get_button_state( hwnd );
1307 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1308 LONG ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE );
1309 HWND parent;
1310 HRGN hrgn;
1311
1312 if (style & BS_PUSHLIKE)
1313 {
1314 PB_Paint( hwnd, hDC, action );
1315 return;
1316 }
1317
1318 GetClientRect(hwnd, &client);
1319 rbox = rtext = client;
1320
1321 checkBoxWidth = 12 * GetDeviceCaps( hDC, LOGPIXELSX ) / 96 + 1;
1322 checkBoxHeight = 12 * GetDeviceCaps( hDC, LOGPIXELSY ) / 96 + 1;
1323
1324 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1325 GetCharWidthW( hDC, '0', '0', &text_offset );
1326 text_offset /= 2;
1327
1328 parent = GetParent(hwnd);
1329 if (!parent) parent = hwnd;
1330 #if defined(__REACTOS__) && defined(_USER32_)
1331 hBrush = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC);
1332 #else
1333 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC,
1334 (WPARAM)hDC, (LPARAM)hwnd);
1335 if (!hBrush) /* did the app forget to call defwindowproc ? */
1336 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
1337 (WPARAM)hDC, (LPARAM)hwnd );
1338 #endif
1339 hrgn = set_control_clipping( hDC, &client );
1340
1341 if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT)
1342 {
1343 /* magic +4 is what CTL3D expects */
1344
1345 rtext.right -= checkBoxWidth + text_offset;;
1346 rbox.left = rbox.right - checkBoxWidth;
1347 }
1348 else
1349 {
1350 rtext.left += checkBoxWidth + text_offset;;
1351 rbox.right = checkBoxWidth;
1352 }
1353
1354 /* Since WM_ERASEBKGND does nothing, first prepare background */
1355 if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
1356 if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush );
1357
1358 /* Draw label */
1359 client = rtext;
1360 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext);
1361
1362 /* Only adjust rbox when rtext is valid */
1363 if (dtFlags != (UINT)-1L)
1364 {
1365 rbox.top = rtext.top;
1366 rbox.bottom = rtext.bottom;
1367 }
1368
1369 /* Draw the check-box bitmap */
1370 if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
1371 {
1372 UINT flags;
1373
1374 if ((get_button_type(style) == BS_RADIOBUTTON) ||
1375 (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO;
1376 else if (state & BST_INDETERMINATE) flags = DFCS_BUTTON3STATE;
1377 else flags = DFCS_BUTTONCHECK;
1378
1379 if (state & (BST_CHECKED | BST_INDETERMINATE)) flags |= DFCS_CHECKED;
1380 if (state & BST_PUSHED) flags |= DFCS_PUSHED;
1381
1382 if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
1383
1384 /* rbox must have the correct height */
1385 delta = rbox.bottom - rbox.top - checkBoxHeight;
1386
1387 if (style & BS_TOP) {
1388 if (delta > 0) {
1389 rbox.bottom = rbox.top + checkBoxHeight;
1390 } else {
1391 rbox.top -= -delta/2 + 1;
1392 rbox.bottom = rbox.top + checkBoxHeight;
1393 }
1394 } else if (style & BS_BOTTOM) {
1395 if (delta > 0) {
1396 rbox.top = rbox.bottom - checkBoxHeight;
1397 } else {
1398 rbox.bottom += -delta/2 + 1;
1399 rbox.top = rbox.bottom - checkBoxHeight;
1400 }
1401 } else { /* Default */
1402 if (delta > 0) {
1403 int ofs = (delta / 2);
1404 rbox.bottom -= ofs + 1;
1405 rbox.top = rbox.bottom - checkBoxHeight;
1406 } else if (delta < 0) {
1407 int ofs = (-delta / 2);
1408 rbox.top -= ofs + 1;
1409 rbox.bottom = rbox.top + checkBoxHeight;
1410 }
1411 }
1412
1413 DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags );
1414 }
1415
1416 if (dtFlags == (UINT)-1L) /* Noting to draw */
1417 return;
1418
1419 if (action == ODA_DRAWENTIRE)
1420 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext);
1421
1422 /* ... and focus */
1423 if (action == ODA_FOCUS || (state & BST_FOCUS))
1424 {
1425 #ifdef __REACTOS__
1426 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
1427 {
1428 #endif
1429 rtext.left--;
1430 rtext.right++;
1431 IntersectRect(&rtext, &rtext, &client);
1432 DrawFocusRect( hDC, &rtext );
1433 #ifdef __REACTOS__
1434 }
1435 #endif
1436 }
1437 SelectClipRgn( hDC, hrgn );
1438 if (hrgn) DeleteObject( hrgn );
1439 }
1440
1441
1442 /**********************************************************************
1443 * BUTTON_CheckAutoRadioButton
1444 *
1445 * hwnd is checked, uncheck every other auto radio button in group
1446 */
1447 static void BUTTON_CheckAutoRadioButton( HWND hwnd )
1448 {
1449 HWND parent, sibling, start;
1450
1451 parent = GetParent(hwnd);
1452 /* make sure that starting control is not disabled or invisible */
1453 start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE );
1454 do
1455 {
1456 if (!sibling) break;
1457 if ((hwnd != sibling) &&
1458 ((GetWindowLongPtrW( sibling, GWL_STYLE) & BS_TYPEMASK) == BS_AUTORADIOBUTTON))
1459 SendMessageW( sibling, BM_SETCHECK, BST_UNCHECKED, 0 );
1460 sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
1461 } while (sibling != start);
1462 }
1463
1464
1465 /**********************************************************************
1466 * Group Box Functions
1467 */
1468
1469 static void GB_Paint( HWND hwnd, HDC hDC, UINT action )
1470 {
1471 RECT rc, rcFrame;
1472 HBRUSH hbr;
1473 HFONT hFont;
1474 UINT dtFlags;
1475 TEXTMETRICW tm;
1476 LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE );
1477 HWND parent;
1478 HRGN hrgn;
1479
1480 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1481 /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
1482 parent = GetParent(hwnd);
1483 if (!parent) parent = hwnd;
1484 #if defined(__REACTOS__) && defined(_USER32_)
1485 hbr = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC);
1486 #else
1487 hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd);
1488 if (!hbr) /* did the app forget to call defwindowproc ? */
1489 hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
1490 (WPARAM)hDC, (LPARAM)hwnd);
1491 #endif
1492 GetClientRect( hwnd, &rc);
1493 rcFrame = rc;
1494 hrgn = set_control_clipping( hDC, &rc );
1495
1496 GetTextMetricsW (hDC, &tm);
1497 rcFrame.top += (tm.tmHeight / 2) - 1;
1498 DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
1499
1500 InflateRect(&rc, -7, 1);
1501 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc);
1502
1503 if (dtFlags != (UINT)-1L)
1504 {
1505 /* Because buttons have CS_PARENTDC class style, there is a chance
1506 * that label will be drawn out of client rect.
1507 * But Windows doesn't clip label's rect, so do I.
1508 */
1509
1510 /* There is 1-pixel margin at the left, right, and bottom */
1511 rc.left--; rc.right++; rc.bottom++;
1512 FillRect(hDC, &rc, hbr);
1513 rc.left++; rc.right--; rc.bottom--;
1514
1515 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc);
1516 }
1517 SelectClipRgn( hDC, hrgn );
1518 if (hrgn) DeleteObject( hrgn );
1519 }
1520
1521
1522 /**********************************************************************
1523 * User Button Functions
1524 */
1525
1526 static void UB_Paint( HWND hwnd, HDC hDC, UINT action )
1527 {
1528 RECT rc;
1529 HBRUSH hBrush;
1530 HFONT hFont;
1531 LONG state = get_button_state( hwnd );
1532 HWND parent;
1533
1534 GetClientRect( hwnd, &rc);
1535
1536 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1537
1538 parent = GetParent(hwnd);
1539 if (!parent) parent = hwnd;
1540 #if defined(__REACTOS__) && defined(_USER32_)
1541 hBrush = GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN);
1542 #else
1543 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd);
1544 if (!hBrush) /* did the app forget to call defwindowproc ? */
1545 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
1546 (WPARAM)hDC, (LPARAM)hwnd);
1547 #endif
1548
1549 FillRect( hDC, &rc, hBrush );
1550 if (action == ODA_FOCUS || (state & BST_FOCUS))
1551 #ifdef __REACTOS__
1552 {
1553 if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS))
1554 #endif
1555 DrawFocusRect( hDC, &rc );
1556 #ifdef __REACTOS__
1557 }
1558 #endif
1559
1560 switch (action)
1561 {
1562 case ODA_FOCUS:
1563 BUTTON_NOTIFY_PARENT( hwnd, (state & BST_FOCUS) ? BN_SETFOCUS : BN_KILLFOCUS );
1564 break;
1565
1566 case ODA_SELECT:
1567 BUTTON_NOTIFY_PARENT( hwnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE );
1568 break;
1569
1570 default:
1571 BUTTON_NOTIFY_PARENT( hwnd, BN_PAINT );
1572 break;
1573 }
1574 }
1575
1576
1577 /**********************************************************************
1578 * Ownerdrawn Button Functions
1579 */
1580
1581 static void OB_Paint( HWND hwnd, HDC hDC, UINT action )
1582 {
1583 LONG state = get_button_state( hwnd );
1584 DRAWITEMSTRUCT dis;
1585 LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID );
1586 HWND parent;
1587 HFONT hFont, hPrevFont = 0;
1588 HRGN hrgn;
1589
1590 dis.CtlType = ODT_BUTTON;
1591 dis.CtlID = id;
1592 dis.itemID = 0;
1593 dis.itemAction = action;
1594 dis.itemState = ((state & BST_FOCUS) ? ODS_FOCUS : 0) |
1595 ((state & BST_PUSHED) ? ODS_SELECTED : 0) |
1596 (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED);
1597 dis.hwndItem = hwnd;
1598 dis.hDC = hDC;
1599 dis.itemData = 0;
1600 GetClientRect( hwnd, &dis.rcItem );
1601
1602 if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont );
1603 parent = GetParent(hwnd);
1604 if (!parent) parent = hwnd;
1605 #if defined(__REACTOS__) && defined(_USER32_)
1606 GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN);
1607 #else
1608 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
1609 #endif
1610
1611 hrgn = set_control_clipping( hDC, &dis.rcItem );
1612
1613 SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
1614 if (hPrevFont) SelectObject(hDC, hPrevFont);
1615 SelectClipRgn( hDC, hrgn );
1616 if (hrgn) DeleteObject( hrgn );
1617 }
1618
1619 #ifndef _USER32_
1620 void BUTTON_Register()
1621 {
1622 WNDCLASSW wndClass;
1623
1624 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
1625 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC;
1626 wndClass.lpfnWndProc = ButtonWndProcW;
1627 wndClass.cbClsExtra = 0;
1628 wndClass.cbWndExtra = sizeof(PBUTTON_DATA);
1629 wndClass.hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
1630 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1631 wndClass.lpszClassName = WC_BUTTONW;
1632
1633 RegisterClassW(&wndClass);
1634 }
1635
1636 void BUTTON_Unregister()
1637 {
1638 UnregisterClassW(WC_BUTTONW, NULL);
1639 }
1640 #endif