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