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