1 /* File: button.c -- Button type widgets
3 * Copyright (C) 1993 Johannes Ruscheinski
4 * Copyright (C) 1993 David Metcalfe
5 * Copyright (C) 1994 Alexandre Julliard
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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.
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.
32 * - BS_NOTIFY: is it complete?
33 * - BS_RIGHTBUTTON: same as BS_LEFTTEXT
37 * - WM_CHAR: Checks a (manual or automatic) check box on '+' or '=', clears it on '-' key.
38 * - WM_SETFOCUS: For (manual or automatic) radio buttons, send the parent window BN_CLICKED
39 * - WM_NCCREATE: Turns any BS_OWNERDRAW button into a BS_PUSHBUTTON button.
50 * - BN_PUSHED/BN_HILITE
54 * - BN_UNPUSHED/BN_UNHILITE
57 * Structures/Macros/Definitions
60 * - Button_GetIdealSize
61 * - Button_GetImageList
62 * - Button_GetTextMargin
63 * - Button_SetImageList
64 * - Button_SetTextMargin
74 #include "wine/winuser16.h"
78 /* GetWindowLong offsets for window extra information */
79 #define STATE_GWL_OFFSET 0
80 #define HFONT_GWL_OFFSET (sizeof(LONG))
81 #define HIMAGE_GWL_OFFSET (HFONT_GWL_OFFSET+sizeof(HFONT))
82 #define NB_EXTRA_BYTES (HIMAGE_GWL_OFFSET+sizeof(HANDLE))
84 /* Button state values */
85 #define BUTTON_UNCHECKED 0x00
86 #define BUTTON_CHECKED 0x01
87 #define BUTTON_3STATE 0x02
88 #define BUTTON_HIGHLIGHTED 0x04
89 #define BUTTON_HASFOCUS 0x08
90 #define BUTTON_NSTATES 0x0F
91 /* undocumented flags */
92 #define BUTTON_BTNPRESSED 0x40
93 #define BUTTON_UNKNOWN2 0x20
94 #define BUTTON_UNKNOWN3 0x10
96 static UINT BUTTON_CalcLabelRect( HWND hwnd, HDC hdc, RECT *rc );
97 static void PB_Paint( HWND hwnd, HDC hDC, UINT action );
98 static void CB_Paint( HWND hwnd, HDC hDC, UINT action );
99 static void GB_Paint( HWND hwnd, HDC hDC, UINT action );
100 static void UB_Paint( HWND hwnd, HDC hDC, UINT action );
101 static void OB_Paint( HWND hwnd, HDC hDC, UINT action );
102 static void BUTTON_CheckAutoRadioButton( HWND hwnd );
103 static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
104 static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
106 #define MAX_BTN_TYPE 12
108 static const WORD maxCheckState[MAX_BTN_TYPE] =
110 BUTTON_UNCHECKED, /* BS_PUSHBUTTON */
111 BUTTON_UNCHECKED, /* BS_DEFPUSHBUTTON */
112 BUTTON_CHECKED, /* BS_CHECKBOX */
113 BUTTON_CHECKED, /* BS_AUTOCHECKBOX */
114 BUTTON_CHECKED, /* BS_RADIOBUTTON */
115 BUTTON_3STATE, /* BS_3STATE */
116 BUTTON_3STATE, /* BS_AUTO3STATE */
117 BUTTON_UNCHECKED, /* BS_GROUPBOX */
118 BUTTON_UNCHECKED, /* BS_USERBUTTON */
119 BUTTON_CHECKED, /* BS_AUTORADIOBUTTON */
120 BUTTON_UNCHECKED, /* Not defined */
121 BUTTON_UNCHECKED /* BS_OWNERDRAW */
124 typedef void (*pfPaint)( HWND hwnd, HDC hdc, UINT action );
126 static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
128 PB_Paint, /* BS_PUSHBUTTON */
129 PB_Paint, /* BS_DEFPUSHBUTTON */
130 CB_Paint, /* BS_CHECKBOX */
131 CB_Paint, /* BS_AUTOCHECKBOX */
132 CB_Paint, /* BS_RADIOBUTTON */
133 CB_Paint, /* BS_3STATE */
134 CB_Paint, /* BS_AUTO3STATE */
135 GB_Paint, /* BS_GROUPBOX */
136 UB_Paint, /* BS_USERBUTTON */
137 CB_Paint, /* BS_AUTORADIOBUTTON */
138 NULL, /* Not defined */
139 OB_Paint /* BS_OWNERDRAW */
142 static HBITMAP hbitmapCheckBoxes = 0;
143 static WORD checkBoxWidth = 0, checkBoxHeight = 0;
146 /*********************************************************************
147 * button class descriptor
149 const struct builtin_class_descr BUTTON_builtin_class =
152 CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */
153 ButtonWndProcA, /* procA */
154 ButtonWndProcW, /* procW */
155 NB_EXTRA_BYTES, /* extra */
156 IDC_ARROW, /* cursor */
161 inline static LONG get_button_state( HWND hwnd )
163 return GetWindowLongW( hwnd, STATE_GWL_OFFSET );
166 inline static void set_button_state( HWND hwnd, LONG state )
168 SetWindowLongW( hwnd, STATE_GWL_OFFSET, state );
171 inline static HFONT get_button_font( HWND hwnd )
173 return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET );
176 inline static void set_button_font( HWND hwnd, HFONT font )
178 SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font );
181 inline static UINT get_button_type( LONG window_style )
183 return (window_style & 0x0f);
186 /* paint a button of any type */
187 inline static void paint_button( HWND hwnd, LONG style, UINT action )
189 if (btnPaintFunc[style] && IsWindowVisible(hwnd))
191 HDC hdc = GetDC( hwnd );
192 btnPaintFunc[style]( hwnd, hdc, action );
193 ReleaseDC( hwnd, hdc );
197 /* retrieve the button text; returned buffer must be freed by caller */
198 inline static WCHAR *get_button_text( HWND hwnd )
201 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
202 if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 );
206 /***********************************************************************
207 * ButtonWndProc_common
209 static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
210 WPARAM wParam, LPARAM lParam, BOOL unicode )
214 LONG style = GetWindowLongW( hWnd, GWL_STYLE );
215 UINT btn_type = get_button_type( style );
219 pt.x = LOWORD(lParam);
220 pt.y = HIWORD(lParam);
227 case BS_AUTOCHECKBOX: return DLGC_BUTTON | DLGC_WANTCHARS;
228 case BS_AUTORADIOBUTTON: return DLGC_RADIOBUTTON;
229 case BS_CHECKBOX: return DLGC_BUTTON | DLGC_WANTCHARS;
230 case BS_DEFPUSHBUTTON: return DLGC_DEFPUSHBUTTON;
231 case BS_GROUPBOX: return DLGC_STATIC;
232 case BS_PUSHBUTTON: return DLGC_UNDEFPUSHBUTTON;
233 case BS_RADIOBUTTON: return DLGC_RADIOBUTTON;
234 default: return DLGC_BUTTON;
238 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
242 if (!hbitmapCheckBoxes)
245 hbitmapCheckBoxes = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CHECKBOXES));
246 GetObjectW( hbitmapCheckBoxes, sizeof(bmp), &bmp );
247 checkBoxWidth = bmp.bmWidth / 4;
248 checkBoxHeight = bmp.bmHeight / 3;
250 if (btn_type >= MAX_BTN_TYPE)
251 return -1; /* abort */
252 set_button_state( hWnd, BUTTON_UNCHECKED );
256 if (btn_type == BS_OWNERDRAW)
258 HDC hdc = (HDC)wParam;
261 HWND parent = GetParent(hWnd);
262 if (!parent) parent = hWnd;
263 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd);
264 if (!hBrush) /* did the app forget to call defwindowproc ? */
265 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
266 (WPARAM)hdc, (LPARAM)hWnd);
267 GetClientRect(hWnd, &rc);
268 FillRect(hdc, &rc, hBrush);
273 if (btnPaintFunc[btn_type])
276 HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
277 int nOldMode = SetBkMode( hdc, OPAQUE );
278 (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE );
279 SetBkMode(hdc, nOldMode); /* reset painting mode */
280 if( !wParam ) EndPaint( hWnd, &ps );
285 if (wParam == VK_SPACE)
287 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
288 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
292 case WM_LBUTTONDBLCLK:
293 if(style & BS_NOTIFY ||
294 btn_type == BS_RADIOBUTTON ||
295 btn_type == BS_USERBUTTON ||
296 btn_type == BS_OWNERDRAW)
298 SendMessageW( GetParent(hWnd), WM_COMMAND,
299 MAKEWPARAM( GetWindowLongPtrW(hWnd,GWLP_ID), BN_DOUBLECLICKED ),
307 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
308 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
312 if (wParam != VK_SPACE)
316 state = get_button_state( hWnd );
317 if (!(state & BUTTON_BTNPRESSED)) break;
318 state &= BUTTON_NSTATES;
319 set_button_state( hWnd, state );
320 if (!(state & BUTTON_HIGHLIGHTED))
325 SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
327 GetClientRect( hWnd, &rect );
328 if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
330 state = get_button_state( hWnd );
333 case BS_AUTOCHECKBOX:
334 SendMessageW( hWnd, BM_SETCHECK, !(state & BUTTON_CHECKED), 0 );
336 case BS_AUTORADIOBUTTON:
337 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
340 SendMessageW( hWnd, BM_SETCHECK,
341 (state & BUTTON_3STATE) ? 0 : ((state & 3) + 1), 0 );
344 SendMessageW( GetParent(hWnd), WM_COMMAND,
345 MAKEWPARAM( GetWindowLongPtrW(hWnd,GWLP_ID), BN_CLICKED ), (LPARAM)hWnd);
349 case WM_CAPTURECHANGED:
350 state = get_button_state( hWnd );
351 if (state & BUTTON_BTNPRESSED)
353 state &= BUTTON_NSTATES;
354 set_button_state( hWnd, state );
355 if (state & BUTTON_HIGHLIGHTED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
360 if ((wParam & MK_LBUTTON) && GetCapture() == hWnd)
362 GetClientRect( hWnd, &rect );
363 SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
369 /* Clear an old text here as Windows does */
370 HDC hdc = GetDC(hWnd);
373 HWND parent = GetParent(hWnd);
375 if (!parent) parent = hWnd;
376 hbrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC,
377 (WPARAM)hdc, (LPARAM)hWnd);
378 if (!hbrush) /* did the app forget to call DefWindowProc ? */
379 hbrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
380 (WPARAM)hdc, (LPARAM)hWnd);
382 GetClientRect(hWnd, &client);
384 BUTTON_CalcLabelRect(hWnd, hdc, &rc);
385 /* Clip by client rect bounds */
386 if (rc.right > client.right) rc.right = client.right;
387 if (rc.bottom > client.bottom) rc.bottom = client.bottom;
388 FillRect(hdc, &rc, hbrush);
389 ReleaseDC(hWnd, hdc);
391 if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
392 else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam );
393 if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */
394 InvalidateRect( hWnd, NULL, TRUE );
396 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
397 return 1; /* success. FIXME: check text length */
401 set_button_font( hWnd, (HFONT)wParam );
402 if (lParam) paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
406 return (LRESULT)get_button_font( hWnd );
409 set_button_state( hWnd, get_button_state(hWnd) | BUTTON_HASFOCUS );
410 paint_button( hWnd, btn_type, ODA_FOCUS );
414 state = get_button_state( hWnd );
415 set_button_state( hWnd, state & ~BUTTON_HASFOCUS );
416 paint_button( hWnd, btn_type, ODA_FOCUS );
418 if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd)
422 case WM_SYSCOLORCHANGE:
423 InvalidateRect( hWnd, NULL, FALSE );
428 if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
429 btn_type = wParam & 0x0f;
430 style = (style & ~0x0f) | btn_type;
431 SetWindowLongW( hWnd, GWL_STYLE, style );
433 /* Only redraw if lParam flag is set.*/
435 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
440 SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
441 SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
445 /* Check that image format matches button style */
446 switch (style & (BS_BITMAP|BS_ICON))
449 if (wParam != IMAGE_BITMAP) return 0;
452 if (wParam != IMAGE_ICON) return 0;
457 oldHbitmap = (HBITMAP)SetWindowLongW( hWnd, HIMAGE_GWL_OFFSET, lParam );
458 InvalidateRect( hWnd, NULL, FALSE );
459 return (LRESULT)oldHbitmap;
462 return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET );
466 return get_button_state( hWnd ) & 3;
470 if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
471 state = get_button_state( hWnd );
472 if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
474 if (wParam) style |= WS_TABSTOP;
475 else style &= ~WS_TABSTOP;
476 SetWindowLongW( hWnd, GWL_STYLE, style );
478 if ((state & 3) != wParam)
480 set_button_state( hWnd, (state & ~3) | wParam );
481 paint_button( hWnd, btn_type, ODA_SELECT );
483 if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BUTTON_CHECKED) && (style & WS_CHILD))
484 BUTTON_CheckAutoRadioButton( hWnd );
489 return get_button_state( hWnd );
493 state = get_button_state( hWnd );
496 if (state & BUTTON_HIGHLIGHTED) break;
497 set_button_state( hWnd, state | BUTTON_HIGHLIGHTED );
501 if (!(state & BUTTON_HIGHLIGHTED)) break;
502 set_button_state( hWnd, state & ~BUTTON_HIGHLIGHTED );
504 paint_button( hWnd, btn_type, ODA_SELECT );
508 if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
511 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
512 DefWindowProcA(hWnd, uMsg, wParam, lParam);
517 /***********************************************************************
519 * The button window procedure. This is just a wrapper which locks
520 * the passed HWND and calls the real window procedure (with a WND*
521 * pointer pointing to the locked windowstructure).
523 static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
525 if (!IsWindow( hWnd )) return 0;
526 return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, TRUE );
530 /***********************************************************************
533 static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
535 if (!IsWindow( hWnd )) return 0;
536 return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, FALSE );
540 /**********************************************************************
541 * Convert button styles to flags used by DrawText.
542 * TODO: handle WS_EX_RIGHT extended style.
544 static UINT BUTTON_BStoDT(DWORD style)
546 UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */
548 /* "Convert" pushlike buttons to pushbuttons */
549 if (style & BS_PUSHLIKE)
552 if (!(style & BS_MULTILINE))
553 dtStyle |= DT_SINGLELINE;
555 dtStyle |= DT_WORDBREAK;
557 switch (style & BS_CENTER)
559 case BS_LEFT: /* DT_LEFT is 0 */ break;
560 case BS_RIGHT: dtStyle |= DT_RIGHT; break;
561 case BS_CENTER: dtStyle |= DT_CENTER; break;
563 /* Pushbutton's text is centered by default */
564 if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
565 /* all other flavours have left aligned text */
568 /* DrawText ignores vertical alignment for multiline text,
569 * but we use these flags to align label manualy.
571 if (get_button_type(style) != BS_GROUPBOX)
573 switch (style & BS_VCENTER)
575 case BS_TOP: /* DT_TOP is 0 */ break;
576 case BS_BOTTOM: dtStyle |= DT_BOTTOM; break;
577 case BS_VCENTER: /* fall through */
578 default: dtStyle |= DT_VCENTER; break;
582 /* GroupBox's text is always single line and is top aligned. */
583 dtStyle |= DT_SINGLELINE;
588 /**********************************************************************
589 * BUTTON_CalcLabelRect
591 * Calculates label's rectangle depending on button style.
593 * Returns flags to be passed to DrawText.
594 * Calculated rectangle doesn't take into account button state
595 * (pushed, etc.). If there is nothing to draw (no text/image) output
596 * rectangle is empty, and return value is (UINT)-1.
598 static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
600 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
604 UINT dtStyle = BUTTON_BStoDT(style);
608 /* Calculate label rectangle according to label type */
609 switch (style & (BS_ICON|BS_BITMAP))
612 if (!(text = get_button_text( hwnd ))) goto empty_rect;
615 HeapFree( GetProcessHeap(), 0, text );
618 DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
619 HeapFree( GetProcessHeap(), 0, text );
623 if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
626 GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
628 r.right = r.left + bm.bmWidth;
629 r.bottom = r.top + bm.bmHeight;
631 DeleteObject(iconInfo.hbmColor);
632 DeleteObject(iconInfo.hbmMask);
636 if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
639 r.right = r.left + bm.bmWidth;
640 r.bottom = r.top + bm.bmHeight;
647 return (UINT)(LONG)-1;
650 /* Position label inside bounding rectangle according to
651 * alignment flags. (calculated rect is always left-top aligned).
652 * If label is aligned to any side - shift label in opposite
653 * direction to leave extra space for focus rectangle.
655 switch (dtStyle & (DT_CENTER|DT_RIGHT))
657 case DT_LEFT: r.left++; r.right++; break;
658 case DT_CENTER: n = r.right - r.left;
659 r.left = rc->left + ((rc->right - rc->left) - n) / 2;
660 r.right = r.left + n; break;
661 case DT_RIGHT: n = r.right - r.left;
662 r.right = rc->right - 1;
663 r.left = r.right - n;
667 switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
669 case DT_TOP: r.top++; r.bottom++; break;
670 case DT_VCENTER: n = r.bottom - r.top;
671 r.top = rc->top + ((rc->bottom - rc->top) - n) / 2;
672 r.bottom = r.top + n; break;
673 case DT_BOTTOM: n = r.bottom - r.top;
674 r.bottom = rc->bottom - 1;
675 r.top = r.bottom - n;
684 /**********************************************************************
685 * BUTTON_DrawTextCallback
687 * Callback function used by DrawStateW function.
689 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
697 DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
702 /**********************************************************************
705 * Common function for drawing button label.
707 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc)
709 DRAWSTATEPROC lpOutputProc = NULL;
713 UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
714 LONG state = get_button_state( hwnd );
715 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
718 /* FIXME: To draw disabled label in Win31 look-and-feel, we probably
719 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
720 * I don't have Win31 on hand to verify that, so I leave it as is.
723 if ((style & BS_PUSHLIKE) && (state & BUTTON_3STATE))
725 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
729 switch (style & (BS_ICON|BS_BITMAP))
732 /* DST_COMPLEX -- is 0 */
733 lpOutputProc = BUTTON_DrawTextCallback;
734 if (!(text = get_button_text( hwnd ))) return;
736 wp = (WPARAM)dtFlags;
741 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
746 lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET );
753 DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
754 rc->right - rc->left, rc->bottom - rc->top, flags);
755 if (text) HeapFree( GetProcessHeap(), 0, text );
758 /**********************************************************************
759 * Push Button Functions
761 static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
763 RECT rc, focus_rect, r;
764 UINT dtFlags, uState;
769 COLORREF oldTxtColor;
771 LONG state = get_button_state( hwnd );
772 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
773 BOOL pushedState = (state & BUTTON_HIGHLIGHTED);
776 GetClientRect( hwnd, &rc );
778 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
779 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
780 parent = GetParent(hwnd);
781 if (!parent) parent = hwnd;
782 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
783 hOldPen = (HPEN)SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME));
784 hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
785 oldBkMode = SetBkMode(hDC, TRANSPARENT);
787 if (get_button_type(style) == BS_DEFPUSHBUTTON)
789 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
790 InflateRect( &rc, -1, -1 );
793 uState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
797 else if (pushedState)
799 if (get_button_type(style) == BS_DEFPUSHBUTTON )
802 uState |= DFCS_PUSHED;
805 if (state & (BUTTON_CHECKED | BUTTON_3STATE))
806 uState |= DFCS_CHECKED;
808 DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
812 /* draw button label */
814 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
816 if (dtFlags == (UINT)-1L)
820 OffsetRect(&r, 1, 1);
822 hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
823 SelectClipRgn(hDC, hRgn);
825 oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
827 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
829 SetTextColor( hDC, oldTxtColor );
830 SelectClipRgn(hDC, 0);
833 if (state & BUTTON_HASFOCUS)
835 InflateRect( &focus_rect, -1, -1 );
836 IntersectRect(&focus_rect, &focus_rect, &rc);
837 DrawFocusRect( hDC, &focus_rect );
841 SelectObject( hDC, hOldPen );
842 SelectObject( hDC, hOldBrush );
843 SetBkMode(hDC, oldBkMode);
846 /**********************************************************************
847 * Check Box & Radio Button Functions
850 static void CB_Paint( HWND hwnd, HDC hDC, UINT action )
852 RECT rbox, rtext, client;
858 LONG state = get_button_state( hwnd );
859 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
862 if (style & BS_PUSHLIKE)
864 PB_Paint( hwnd, hDC, action );
868 GetClientRect(hwnd, &client);
869 rbox = rtext = client;
871 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
873 parent = GetParent(hwnd);
874 if (!parent) parent = hwnd;
875 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC,
876 (WPARAM)hDC, (LPARAM)hwnd);
877 if (!hBrush) /* did the app forget to call defwindowproc ? */
878 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
879 (WPARAM)hDC, (LPARAM)hwnd );
881 if (style & BS_LEFTTEXT)
883 /* magic +4 is what CTL3D expects */
885 rtext.right -= checkBoxWidth + 4;
886 rbox.left = rbox.right - checkBoxWidth;
890 rtext.left += checkBoxWidth + 4;
891 rbox.right = checkBoxWidth;
894 /* Since WM_ERASEBKGND does nothing, first prepare background */
895 if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
896 if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush );
900 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext);
902 rbox.top = rtext.top;
903 rbox.bottom = rtext.bottom;
904 /* Draw the check-box bitmap */
905 if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
909 if ((get_button_type(style) == BS_RADIOBUTTON) ||
910 (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO;
911 else if (state & BUTTON_3STATE) flags = DFCS_BUTTON3STATE;
912 else flags = DFCS_BUTTONCHECK;
914 if (state & (BUTTON_CHECKED | BUTTON_3STATE)) flags |= DFCS_CHECKED;
915 if (state & BUTTON_HIGHLIGHTED) flags |= DFCS_PUSHED;
917 if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
919 /* rbox must have the correct height */
920 delta = rbox.bottom - rbox.top - checkBoxHeight;
922 if (style & BS_TOP) {
924 rbox.bottom = rbox.top + checkBoxHeight;
926 rbox.top -= -delta/2 + 1;
927 rbox.bottom = rbox.top + checkBoxHeight;
929 } else if (style & BS_BOTTOM) {
931 rbox.top = rbox.bottom - checkBoxHeight;
933 rbox.bottom += -delta/2 + 1;
934 rbox.top = rbox.bottom -= checkBoxHeight;
936 } else { /* Default */
938 int ofs = (delta / 2);
939 rbox.bottom -= ofs + 1;
940 rbox.top = rbox.bottom - checkBoxHeight;
941 } else if (delta < 0) {
942 int ofs = (-delta / 2);
944 rbox.bottom = rbox.top + checkBoxHeight;
948 DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags );
951 if (dtFlags == (UINT)-1L) /* Noting to draw */
953 hRgn = CreateRectRgn(client.left, client.top, client.right, client.bottom);
954 SelectClipRgn(hDC, hRgn);
957 if (action == ODA_DRAWENTIRE)
958 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext);
961 if ((action == ODA_FOCUS) ||
962 ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
966 IntersectRect(&rtext, &rtext, &client);
967 DrawFocusRect( hDC, &rtext );
969 SelectClipRgn(hDC, 0);
973 /**********************************************************************
974 * BUTTON_CheckAutoRadioButton
976 * hwnd is checked, uncheck every other auto radio button in group
978 static void BUTTON_CheckAutoRadioButton( HWND hwnd )
980 HWND parent, sibling, start;
982 parent = GetParent(hwnd);
983 /* make sure that starting control is not disabled or invisible */
984 start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE );
988 if ((hwnd != sibling) &&
989 ((GetWindowLongW( sibling, GWL_STYLE) & 0x0f) == BS_AUTORADIOBUTTON))
990 SendMessageW( sibling, BM_SETCHECK, BUTTON_UNCHECKED, 0 );
991 sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
992 } while (sibling != start);
996 /**********************************************************************
997 * Group Box Functions
1000 static void GB_Paint( HWND hwnd, HDC hDC, UINT action )
1007 LONG style = GetWindowLongW( hwnd, GWL_STYLE );
1010 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1011 /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
1012 parent = GetParent(hwnd);
1013 if (!parent) parent = hwnd;
1014 hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd);
1015 if (!hbr) /* did the app forget to call defwindowproc ? */
1016 hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC,
1017 (WPARAM)hDC, (LPARAM)hwnd);
1019 GetClientRect( hwnd, &rc);
1022 GetTextMetricsW (hDC, &tm);
1023 rcFrame.top += (tm.tmHeight / 2) - 1;
1024 DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
1026 InflateRect(&rc, -7, 1);
1027 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc);
1029 if (dtFlags == (UINT)-1L)
1032 /* Because buttons have CS_PARENTDC class style, there is a chance
1033 * that label will be drawn out of client rect.
1034 * But Windows doesn't clip label's rect, so do I.
1037 /* There is 1-pixel marging at the left, right, and bottom */
1038 rc.left--; rc.right++; rc.bottom++;
1039 FillRect(hDC, &rc, hbr);
1040 rc.left++; rc.right--; rc.bottom--;
1042 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc);
1046 /**********************************************************************
1047 * User Button Functions
1050 static void UB_Paint( HWND hwnd, HDC hDC, UINT action )
1055 LONG state = get_button_state( hwnd );
1058 if (action == ODA_SELECT) return;
1060 GetClientRect( hwnd, &rc);
1062 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1064 parent = GetParent(hwnd);
1065 if (!parent) parent = hwnd;
1066 hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd);
1067 if (!hBrush) /* did the app forget to call defwindowproc ? */
1068 hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN,
1069 (WPARAM)hDC, (LPARAM)hwnd);
1071 FillRect( hDC, &rc, hBrush );
1072 if ((action == ODA_FOCUS) ||
1073 ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
1074 DrawFocusRect( hDC, &rc );
1078 /**********************************************************************
1079 * Ownerdrawn Button Functions
1082 static void OB_Paint( HWND hwnd, HDC hDC, UINT action )
1084 LONG state = get_button_state( hwnd );
1088 LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID );
1090 HFONT hFont, hPrevFont = 0;
1092 dis.CtlType = ODT_BUTTON;
1095 dis.itemAction = action;
1096 dis.itemState = ((state & BUTTON_HASFOCUS) ? ODS_FOCUS : 0) |
1097 ((state & BUTTON_HIGHLIGHTED) ? ODS_SELECTED : 0) |
1098 (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED);
1099 dis.hwndItem = hwnd;
1102 GetClientRect( hwnd, &dis.rcItem );
1104 clipRegion = CreateRectRgnIndirect(&dis.rcItem);
1105 if (GetClipRgn(hDC, clipRegion) != 1)
1107 DeleteObject(clipRegion);
1110 clipRect = dis.rcItem;
1111 DPtoLP(hDC, (LPPOINT) &clipRect, 2);
1112 IntersectClipRect(hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
1114 if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont );
1115 parent = GetParent(hwnd);
1116 if (!parent) parent = hwnd;
1117 SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd );
1118 SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
1119 if (hPrevFont) SelectObject(hDC, hPrevFont);
1120 SelectClipRgn(hDC, clipRegion);