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