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
9 #include <stdlib.h> /* for abs() */
14 #include "wine/winuser16.h"
18 /* Note: under MS-Windows, state is a BYTE and this structure is
19 * only 3 bytes long. I don't think there are programs out there
20 * broken enough to rely on this :-)
24 WORD state
; /* Current state */
25 HFONT16 hFont
; /* Button font (or 0 for system font) */
26 HANDLE hImage
; /* Handle to the image or the icon */
29 /* Button state values */
30 #define BUTTON_UNCHECKED 0x00
31 #define BUTTON_CHECKED 0x01
32 #define BUTTON_3STATE 0x02
33 #define BUTTON_HIGHLIGHTED 0x04
34 #define BUTTON_HASFOCUS 0x08
35 #define BUTTON_NSTATES 0x0F
36 /* undocumented flags */
37 #define BUTTON_BTNPRESSED 0x40
38 #define BUTTON_UNKNOWN2 0x20
39 #define BUTTON_UNKNOWN3 0x10
41 static void PB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
);
42 static void CB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
);
43 static void GB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
);
44 static void UB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
);
45 static void OB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
);
46 static void BUTTON_CheckAutoRadioButton( WND
*wndPtr
);
47 static void BUTTON_DrawPushButton( WND
*wndPtr
, HDC hDC
, WORD action
, BOOL pushedState
);
48 static LRESULT WINAPI
ButtonWndProcA( HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
49 static LRESULT WINAPI
ButtonWndProcW( HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
51 #define MAX_BTN_TYPE 12
53 static const WORD maxCheckState
[MAX_BTN_TYPE
] =
55 BUTTON_UNCHECKED
, /* BS_PUSHBUTTON */
56 BUTTON_UNCHECKED
, /* BS_DEFPUSHBUTTON */
57 BUTTON_CHECKED
, /* BS_CHECKBOX */
58 BUTTON_CHECKED
, /* BS_AUTOCHECKBOX */
59 BUTTON_CHECKED
, /* BS_RADIOBUTTON */
60 BUTTON_3STATE
, /* BS_3STATE */
61 BUTTON_3STATE
, /* BS_AUTO3STATE */
62 BUTTON_UNCHECKED
, /* BS_GROUPBOX */
63 BUTTON_UNCHECKED
, /* BS_USERBUTTON */
64 BUTTON_CHECKED
, /* BS_AUTORADIOBUTTON */
65 BUTTON_UNCHECKED
, /* Not defined */
66 BUTTON_UNCHECKED
/* BS_OWNERDRAW */
69 typedef void (*pfPaint
)( WND
*wndPtr
, HDC hdc
, WORD action
);
71 static const pfPaint btnPaintFunc
[MAX_BTN_TYPE
] =
73 PB_Paint
, /* BS_PUSHBUTTON */
74 PB_Paint
, /* BS_DEFPUSHBUTTON */
75 CB_Paint
, /* BS_CHECKBOX */
76 CB_Paint
, /* BS_AUTOCHECKBOX */
77 CB_Paint
, /* BS_RADIOBUTTON */
78 CB_Paint
, /* BS_3STATE */
79 CB_Paint
, /* BS_AUTO3STATE */
80 GB_Paint
, /* BS_GROUPBOX */
81 UB_Paint
, /* BS_USERBUTTON */
82 CB_Paint
, /* BS_AUTORADIOBUTTON */
83 NULL
, /* Not defined */
84 OB_Paint
/* BS_OWNERDRAW */
87 #define PAINT_BUTTON(wndPtr,style,action) \
88 if (btnPaintFunc[style]) { \
89 HDC hdc = GetDC( (wndPtr)->hwndSelf ); \
90 (btnPaintFunc[style])(wndPtr,hdc,action); \
91 ReleaseDC( (wndPtr)->hwndSelf, hdc ); }
93 #define BUTTON_SEND_CTLCOLOR(wndPtr,hdc) \
94 SendMessageW( GetParent((wndPtr)->hwndSelf), WM_CTLCOLORBTN, \
95 (hdc), (wndPtr)->hwndSelf )
97 static HBITMAP hbitmapCheckBoxes
= 0;
98 static WORD checkBoxWidth
= 0, checkBoxHeight
= 0;
101 /*********************************************************************
102 * button class descriptor
104 const struct builtin_class_descr BUTTON_builtin_class
=
107 CS_GLOBALCLASS
| CS_DBLCLKS
| CS_VREDRAW
| CS_HREDRAW
| CS_PARENTDC
, /* style */
108 ButtonWndProcA
, /* procA */
109 ButtonWndProcW
, /* procW */
110 sizeof(BUTTONINFO
), /* extra */
111 IDC_ARROWA
, /* cursor */
116 /***********************************************************************
117 * ButtonWndProc_locked
119 * Called with window lock held.
121 static inline LRESULT WINAPI
ButtonWndProc_locked(WND
* wndPtr
, UINT uMsg
,
122 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
125 HWND hWnd
= wndPtr
->hwndSelf
;
127 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
128 LONG style
= wndPtr
->dwStyle
& 0x0f;
131 pt
.x
= LOWORD(lParam
);
132 pt
.y
= HIWORD(lParam
);
139 case BS_PUSHBUTTON
: return DLGC_BUTTON
| DLGC_UNDEFPUSHBUTTON
;
140 case BS_DEFPUSHBUTTON
: return DLGC_BUTTON
| DLGC_DEFPUSHBUTTON
;
142 case BS_AUTORADIOBUTTON
: return DLGC_BUTTON
| DLGC_RADIOBUTTON
;
143 default: return DLGC_BUTTON
;
147 PAINT_BUTTON( wndPtr
, style
, ODA_DRAWENTIRE
);
151 if (!hbitmapCheckBoxes
)
154 hbitmapCheckBoxes
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CHECKBOXES
));
155 GetObjectW( hbitmapCheckBoxes
, sizeof(bmp
), &bmp
);
156 checkBoxWidth
= bmp
.bmWidth
/ 4;
157 checkBoxHeight
= bmp
.bmHeight
/ 3;
159 if (style
< 0L || style
>= MAX_BTN_TYPE
)
160 return -1; /* abort */
161 infoPtr
->state
= BUTTON_UNCHECKED
;
170 if (btnPaintFunc
[style
])
173 HDC hdc
= wParam
? (HDC
)wParam
: BeginPaint( hWnd
, &ps
);
174 int nOldMode
= SetBkMode( hdc
, OPAQUE
);
175 (btnPaintFunc
[style
])( wndPtr
, hdc
, ODA_DRAWENTIRE
);
176 SetBkMode(hdc
, nOldMode
); /* reset painting mode */
177 if( !wParam
) EndPaint( hWnd
, &ps
);
182 if (wParam
== VK_SPACE
)
184 SendMessageW( hWnd
, BM_SETSTATE
, TRUE
, 0 );
185 infoPtr
->state
|= BUTTON_BTNPRESSED
;
189 case WM_LBUTTONDBLCLK
:
190 if(wndPtr
->dwStyle
& BS_NOTIFY
||
191 style
==BS_RADIOBUTTON
||
192 style
==BS_USERBUTTON
||
193 style
==BS_OWNERDRAW
) {
194 SendMessageW( GetParent(hWnd
), WM_COMMAND
,
195 MAKEWPARAM( wndPtr
->wIDmenu
, BN_DOUBLECLICKED
), hWnd
);
202 SendMessageW( hWnd
, BM_SETSTATE
, TRUE
, 0 );
203 infoPtr
->state
|= BUTTON_BTNPRESSED
;
207 if (wParam
!= VK_SPACE
)
211 if (!(infoPtr
->state
& BUTTON_BTNPRESSED
)) break;
212 infoPtr
->state
&= BUTTON_NSTATES
;
213 if (!(infoPtr
->state
& BUTTON_HIGHLIGHTED
)) {
217 SendMessageW( hWnd
, BM_SETSTATE
, FALSE
, 0 );
219 GetClientRect( hWnd
, &rect
);
220 if (uMsg
== WM_KEYUP
|| PtInRect( &rect
, pt
))
224 case BS_AUTOCHECKBOX
:
225 SendMessageW( hWnd
, BM_SETCHECK
,
226 !(infoPtr
->state
& BUTTON_CHECKED
), 0 );
228 case BS_AUTORADIOBUTTON
:
229 SendMessageW( hWnd
, BM_SETCHECK
, TRUE
, 0 );
232 SendMessageW( hWnd
, BM_SETCHECK
,
233 (infoPtr
->state
& BUTTON_3STATE
) ? 0 :
234 ((infoPtr
->state
& 3) + 1), 0 );
237 SendMessageW( GetParent(hWnd
), WM_COMMAND
,
238 MAKEWPARAM( wndPtr
->wIDmenu
, BN_CLICKED
), hWnd
);
242 case WM_CAPTURECHANGED
:
243 if (infoPtr
->state
& BUTTON_BTNPRESSED
) {
244 infoPtr
->state
&= BUTTON_NSTATES
;
245 if (infoPtr
->state
& BUTTON_HIGHLIGHTED
)
246 SendMessageW( hWnd
, BM_SETSTATE
, FALSE
, 0 );
251 if (GetCapture() == hWnd
)
253 GetClientRect( hWnd
, &rect
);
254 SendMessageW( hWnd
, BM_SETSTATE
, PtInRect(&rect
, pt
), 0 );
259 if (unicode
) DEFWND_SetTextW( wndPtr
, (LPCWSTR
)lParam
);
260 else DEFWND_SetTextA( wndPtr
, (LPCSTR
)lParam
);
261 if( wndPtr
->dwStyle
& WS_VISIBLE
)
262 PAINT_BUTTON( wndPtr
, style
, ODA_DRAWENTIRE
);
263 return 1; /* success. FIXME: check text length */
266 infoPtr
->hFont
= (HFONT16
)wParam
;
267 if (lParam
&& (wndPtr
->dwStyle
& WS_VISIBLE
))
268 PAINT_BUTTON( wndPtr
, style
, ODA_DRAWENTIRE
);
272 return infoPtr
->hFont
;
275 if ((style
== BS_RADIOBUTTON
|| style
== BS_AUTORADIOBUTTON
) && (GetCapture() != hWnd
) &&
276 !(SendMessageW(hWnd
, BM_GETCHECK
, 0, 0) & BST_CHECKED
))
278 /* The notification is sent when the button (BS_AUTORADIOBUTTON)
279 is unchecked and the focus was not given by a mouse click. */
280 if (style
== BS_AUTORADIOBUTTON
)
281 SendMessageW( hWnd
, BM_SETCHECK
, BUTTON_CHECKED
, 0 );
282 SendMessageW( GetParent(hWnd
), WM_COMMAND
,
283 MAKEWPARAM( wndPtr
->wIDmenu
, BN_CLICKED
), hWnd
);
285 infoPtr
->state
|= BUTTON_HASFOCUS
;
286 PAINT_BUTTON( wndPtr
, style
, ODA_FOCUS
);
290 infoPtr
->state
&= ~BUTTON_HASFOCUS
;
291 PAINT_BUTTON( wndPtr
, style
, ODA_FOCUS
);
292 InvalidateRect( hWnd
, NULL
, TRUE
);
295 case WM_SYSCOLORCHANGE
:
296 InvalidateRect( hWnd
, NULL
, FALSE
);
301 if ((wParam
& 0x0f) >= MAX_BTN_TYPE
) break;
302 wndPtr
->dwStyle
= (wndPtr
->dwStyle
& 0xfffffff0)
303 | (wParam
& 0x0000000f);
304 style
= wndPtr
->dwStyle
& 0x0000000f;
306 /* Only redraw if lParam flag is set.*/
308 PAINT_BUTTON( wndPtr
, style
, ODA_DRAWENTIRE
);
313 SendMessageW( hWnd
, WM_LBUTTONDOWN
, 0, 0 );
314 SendMessageW( hWnd
, WM_LBUTTONUP
, 0, 0 );
318 /* Check that image format confirm button style */
319 if ((wndPtr
->dwStyle
& (BS_BITMAP
|BS_ICON
)) == BS_BITMAP
)
321 if (wParam
!= (WPARAM
) IMAGE_BITMAP
)
324 else if ((wndPtr
->dwStyle
& (BS_BITMAP
|BS_ICON
)) == BS_ICON
)
326 if (wParam
!= (WPARAM
) IMAGE_ICON
)
331 oldHbitmap
= infoPtr
->hImage
;
332 infoPtr
->hImage
= (HANDLE
) lParam
;
333 InvalidateRect( hWnd
, NULL
, FALSE
);
337 if ((wndPtr
->dwStyle
& (BS_BITMAP
|BS_ICON
)) == BS_BITMAP
)
339 if (wParam
!= (WPARAM
) IMAGE_BITMAP
)
342 else if ((wndPtr
->dwStyle
& (BS_BITMAP
|BS_ICON
)) == BS_ICON
)
344 if (wParam
!= (WPARAM
) IMAGE_ICON
)
348 return infoPtr
->hImage
;
352 return infoPtr
->state
& 3;
356 if (wParam
> maxCheckState
[style
]) wParam
= maxCheckState
[style
];
357 if ((infoPtr
->state
& 3) != wParam
)
359 if ((style
== BS_RADIOBUTTON
) || (style
== BS_AUTORADIOBUTTON
))
362 wndPtr
->dwStyle
|= WS_TABSTOP
;
364 wndPtr
->dwStyle
&= ~WS_TABSTOP
;
366 infoPtr
->state
= (infoPtr
->state
& ~3) | wParam
;
367 PAINT_BUTTON( wndPtr
, style
, ODA_SELECT
);
369 if ((style
== BS_AUTORADIOBUTTON
) && (wParam
== BUTTON_CHECKED
))
370 BUTTON_CheckAutoRadioButton( wndPtr
);
375 return infoPtr
->state
;
381 if (infoPtr
->state
& BUTTON_HIGHLIGHTED
) break;
382 infoPtr
->state
|= BUTTON_HIGHLIGHTED
;
386 if (!(infoPtr
->state
& BUTTON_HIGHLIGHTED
)) break;
387 infoPtr
->state
&= ~BUTTON_HIGHLIGHTED
;
389 PAINT_BUTTON( wndPtr
, style
, ODA_SELECT
);
393 if(style
== BS_GROUPBOX
) return HTTRANSPARENT
;
396 return unicode
? DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
) :
397 DefWindowProcA(hWnd
, uMsg
, wParam
, lParam
);
402 /***********************************************************************
404 * The button window procedure. This is just a wrapper which locks
405 * the passed HWND and calls the real window procedure (with a WND*
406 * pointer pointing to the locked windowstructure).
408 static LRESULT WINAPI
ButtonWndProcW( HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
411 WND
*wndPtr
= WIN_FindWndPtr(hWnd
);
413 res
= ButtonWndProc_locked(wndPtr
,uMsg
,wParam
,lParam
,TRUE
);
415 WIN_ReleaseWndPtr(wndPtr
);
420 /***********************************************************************
423 static LRESULT WINAPI
ButtonWndProcA( HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
426 WND
*wndPtr
= WIN_FindWndPtr(hWnd
);
428 res
= ButtonWndProc_locked(wndPtr
,uMsg
,wParam
,lParam
,FALSE
);
430 WIN_ReleaseWndPtr(wndPtr
);
435 /**********************************************************************
436 * Push Button Functions
438 static void PB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
)
440 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
441 BOOL bHighLighted
= (infoPtr
->state
& BUTTON_HIGHLIGHTED
);
444 * Delegate this to the more generic pushbutton painting
447 BUTTON_DrawPushButton(wndPtr
,
453 /**********************************************************************
454 * Convert button styles to flags used by DrawText.
455 * TODO: handle WS_EX_RIGHT extended style.
457 static UINT
BUTTON_BStoDT(DWORD style
)
459 UINT dtStyle
= DT_NOCLIP
; /* We use SelectClipRgn to limit output */
461 /* "Convert" pushlike buttons to pushbuttons */
462 if (style
& BS_PUSHLIKE
)
465 if (!(style
& BS_MULTILINE
))
466 dtStyle
|= DT_SINGLELINE
;
468 dtStyle
|= DT_WORDBREAK
;
470 switch (style
& BS_CENTER
)
472 case BS_LEFT
: /* DT_LEFT is 0 */ break;
473 case BS_RIGHT
: dtStyle
|= DT_RIGHT
; break;
474 case BS_CENTER
: dtStyle
|= DT_CENTER
; break;
476 /* Pushbutton's text is centered by default */
477 if ((style
& 0x0F) <= BS_DEFPUSHBUTTON
)
478 dtStyle
|= DT_CENTER
;
479 /* all other flavours have left aligned text */
482 /* DrawText ignores vertical alignment for multiline text,
483 * but we use these flags to align label manualy.
485 if ((style
& 0x0F) != BS_GROUPBOX
)
487 switch (style
& BS_VCENTER
)
489 case BS_TOP
: /* DT_TOP is 0 */ break;
490 case BS_BOTTOM
: dtStyle
|= DT_BOTTOM
; break;
491 case BS_VCENTER
: /* fall through */
492 default: dtStyle
|= DT_VCENTER
; break;
496 /* GroupBox's text is always single line and is top aligned. */
497 dtStyle
|= DT_SINGLELINE
;
502 /**********************************************************************
503 * BUTTON_CalcLabelRect
505 * Calculates label's rectangle depending on button style.
507 * Returns flags to be passed to DrawText.
508 * Calculated rectangle doesn't take into account button state
509 * (pushed, etc.). If there is nothing to draw (no text/image) output
510 * rectangle is empty, and return value is (UINT)-1.
512 static UINT
BUTTON_CalcLabelRect(WND
*wndPtr
, HDC hdc
, RECT
*rc
)
514 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
517 UINT dtStyle
= BUTTON_BStoDT(wndPtr
->dwStyle
);
521 /* Calculate label rectangle according to label type */
522 switch (wndPtr
->dwStyle
& (BS_ICON
|BS_BITMAP
))
525 if (wndPtr
->text
&& wndPtr
->text
[0])
526 DrawTextW(hdc
, wndPtr
->text
, -1, &r
, dtStyle
| DT_CALCRECT
);
532 if (!GetIconInfo((HICON
)infoPtr
->hImage
, &iconInfo
))
535 GetObjectW (iconInfo
.hbmColor
, sizeof(BITMAP
), &bm
);
537 r
.right
= r
.left
+ bm
.bmWidth
;
538 r
.bottom
= r
.top
+ bm
.bmHeight
;
540 DeleteObject(iconInfo
.hbmColor
);
541 DeleteObject(iconInfo
.hbmMask
);
545 if (!GetObjectW (infoPtr
->hImage
, sizeof(BITMAP
), &bm
))
548 r
.right
= r
.left
+ bm
.bmWidth
;
549 r
.bottom
= r
.top
+ bm
.bmHeight
;
556 return (UINT
)(LONG
)-1;
559 /* Position label inside bounding rectangle according to
560 * alignment flags. (calculated rect is always left-top aligned).
561 * If label is aligned to any side - shift label in opposite
562 * direction to leave extra space for focus rectangle.
564 switch (dtStyle
& (DT_CENTER
|DT_RIGHT
))
566 case DT_LEFT
: r
.left
++; r
.right
++; break;
567 case DT_CENTER
: n
= r
.right
- r
.left
;
568 r
.left
= rc
->left
+ ((rc
->right
- rc
->left
) - n
) / 2;
569 r
.right
= r
.left
+ n
; break;
570 case DT_RIGHT
: n
= r
.right
- r
.left
;
571 r
.right
= rc
->right
- 1;
572 r
.left
= r
.right
- n
;
576 switch (dtStyle
& (DT_VCENTER
|DT_BOTTOM
))
578 case DT_TOP
: r
.top
++; r
.bottom
++; break;
579 case DT_VCENTER
: n
= r
.bottom
- r
.top
;
580 r
.top
= rc
->top
+ ((rc
->bottom
- rc
->top
) - n
) / 2;
581 r
.bottom
= r
.top
+ n
; break;
582 case DT_BOTTOM
: n
= r
.bottom
- r
.top
;
583 r
.bottom
= rc
->bottom
- 1;
584 r
.top
= r
.bottom
- n
;
593 /**********************************************************************
594 * BUTTON_DrawTextCallback
596 * Callback function used by DrawStateW function.
598 static BOOL CALLBACK
BUTTON_DrawTextCallback(HDC hdc
, LPARAM lp
, WPARAM wp
, int cx
, int cy
)
606 DrawTextW(hdc
, (LPCWSTR
)lp
, -1, &rc
, (UINT
)wp
);
611 /**********************************************************************
614 * Common function for drawing button label.
616 static void BUTTON_DrawLabel(WND
*wndPtr
, HDC hdc
, UINT dtFlags
, RECT
*rc
)
618 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
619 DRAWSTATEPROC lpOutputProc
= NULL
;
623 UINT flags
= IsWindowEnabled(wndPtr
->hwndSelf
) ? DSS_NORMAL
: DSS_DISABLED
;
625 /* Fixme: To draw disabled label in Win31 look-and-feel, we probably
626 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
627 * I don't have Win31 on hand to verify that, so I leave it as is.
630 if ((wndPtr
->dwStyle
& BS_PUSHLIKE
) && (infoPtr
->state
& BUTTON_3STATE
))
632 hbr
= GetSysColorBrush(COLOR_GRAYTEXT
);
636 switch (wndPtr
->dwStyle
& (BS_ICON
|BS_BITMAP
))
639 /* DST_COMPLEX -- is 0 */
640 lpOutputProc
= BUTTON_DrawTextCallback
;
641 lp
= (LPARAM
)wndPtr
->text
;
642 wp
= (WPARAM
)dtFlags
;
647 lp
= (LPARAM
)infoPtr
->hImage
;
652 lp
= (LPARAM
)infoPtr
->hImage
;
659 DrawStateW(hdc
, hbr
, lpOutputProc
, lp
, wp
, rc
->left
, rc
->top
,
660 rc
->right
- rc
->left
, rc
->bottom
- rc
->top
, flags
);
663 /**********************************************************************
664 * This method will actually do the drawing of the pushbutton
665 * depending on it's state and the pushedState parameter.
667 static void BUTTON_DrawPushButton(
673 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
674 RECT rc
, focus_rect
, r
;
680 COLORREF oldTxtColor
;
682 GetClientRect( wndPtr
->hwndSelf
, &rc
);
684 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
685 if (infoPtr
->hFont
) SelectObject( hDC
, infoPtr
->hFont
);
686 BUTTON_SEND_CTLCOLOR( wndPtr
, hDC
);
687 hOldPen
= (HPEN
)SelectObject(hDC
, GetSysColorPen(COLOR_WINDOWFRAME
));
688 hOldBrush
=(HBRUSH
)SelectObject(hDC
,GetSysColorBrush(COLOR_BTNFACE
));
689 oldBkMode
= SetBkMode(hDC
, TRANSPARENT
);
691 if ( TWEAK_WineLook
== WIN31_LOOK
)
693 COLORREF clr_wnd
= GetSysColor(COLOR_WINDOW
);
694 Rectangle(hDC
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
696 SetPixel( hDC
, rc
.left
, rc
.top
, clr_wnd
);
697 SetPixel( hDC
, rc
.left
, rc
.bottom
-1, clr_wnd
);
698 SetPixel( hDC
, rc
.right
-1, rc
.top
, clr_wnd
);
699 SetPixel( hDC
, rc
.right
-1, rc
.bottom
-1, clr_wnd
);
700 InflateRect( &rc
, -1, -1 );
703 if ((wndPtr
->dwStyle
& 0x000f) == BS_DEFPUSHBUTTON
)
705 Rectangle(hDC
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
706 InflateRect( &rc
, -1, -1 );
709 if (TWEAK_WineLook
== WIN31_LOOK
)
713 /* draw button shadow: */
714 SelectObject(hDC
, GetSysColorBrush(COLOR_BTNSHADOW
));
715 PatBlt(hDC
, rc
.left
, rc
.top
, 1, rc
.bottom
-rc
.top
, PATCOPY
);
716 PatBlt(hDC
, rc
.left
, rc
.top
, rc
.right
-rc
.left
, 1, PATCOPY
);
718 rc
.right
++, rc
.bottom
++;
719 DrawEdge( hDC
, &rc
, EDGE_RAISED
, BF_RECT
);
720 rc
.right
--, rc
.bottom
--;
725 UINT uState
= DFCS_BUTTONPUSH
| DFCS_ADJUSTRECT
;
727 if (wndPtr
->dwStyle
& BS_FLAT
)
729 else if (pushedState
)
731 if ( (wndPtr
->dwStyle
& 0x000f) == BS_DEFPUSHBUTTON
)
734 uState
|= DFCS_PUSHED
;
737 if (infoPtr
->state
& (BUTTON_CHECKED
| BUTTON_3STATE
))
738 uState
|= DFCS_CHECKED
;
740 DrawFrameControl( hDC
, &rc
, DFC_BUTTON
, uState
);
745 /* draw button label */
747 dtFlags
= BUTTON_CalcLabelRect(wndPtr
, hDC
, &r
);
749 if (dtFlags
== (UINT
)-1L)
753 OffsetRect(&r
, 1, 1);
755 if(TWEAK_WineLook
== WIN31_LOOK
)
758 InflateRect(&focus_rect
, 2, 0);
761 hRgn
= CreateRectRgn(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
762 SelectClipRgn(hDC
, hRgn
);
764 oldTxtColor
= SetTextColor( hDC
, GetSysColor(COLOR_BTNTEXT
) );
766 BUTTON_DrawLabel(wndPtr
, hDC
, dtFlags
, &r
);
768 SetTextColor( hDC
, oldTxtColor
);
769 SelectClipRgn(hDC
, 0);
772 if (infoPtr
->state
& BUTTON_HASFOCUS
)
774 InflateRect( &focus_rect
, -1, -1 );
775 IntersectRect(&focus_rect
, &focus_rect
, &rc
);
776 DrawFocusRect( hDC
, &focus_rect
);
780 SelectObject( hDC
, hOldPen
);
781 SelectObject( hDC
, hOldBrush
);
782 SetBkMode(hDC
, oldBkMode
);
785 /**********************************************************************
786 * Check Box & Radio Button Functions
789 static void CB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
)
791 RECT rbox
, rtext
, client
;
794 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
798 if (wndPtr
->dwStyle
& BS_PUSHLIKE
)
800 BOOL bHighLighted
= (infoPtr
->state
& BUTTON_HIGHLIGHTED
);
802 BUTTON_DrawPushButton(wndPtr
,
809 GetClientRect(wndPtr
->hwndSelf
, &client
);
810 rbox
= rtext
= client
;
812 if (infoPtr
->hFont
) SelectObject( hDC
, infoPtr
->hFont
);
814 /* GetControlBrush16 sends WM_CTLCOLORBTN, plus it returns default brush
815 * if parent didn't return valid one. So we kill two hares at once
817 hBrush
= GetControlBrush16( wndPtr
->hwndSelf
, hDC
, CTLCOLOR_BTN
);
819 if (wndPtr
->dwStyle
& BS_LEFTTEXT
)
821 /* magic +4 is what CTL3D expects */
823 rtext
.right
-= checkBoxWidth
+ 4;
824 rbox
.left
= rbox
.right
- checkBoxWidth
;
828 rtext
.left
+= checkBoxWidth
+ 4;
829 rbox
.right
= checkBoxWidth
;
832 /* Draw the check-box bitmap */
833 if (action
== ODA_DRAWENTIRE
|| action
== ODA_SELECT
)
835 /* Since WM_ERASEBKGND does nothing, first prepare background */
836 if (action
== ODA_SELECT
) FillRect( hDC
, &rbox
, hBrush
);
837 else FillRect( hDC
, &client
, hBrush
);
839 if( TWEAK_WineLook
== WIN31_LOOK
)
841 HDC hMemDC
= CreateCompatibleDC( hDC
);
843 delta
= (rbox
.bottom
- rbox
.top
- checkBoxHeight
) / 2;
845 /* Check in case the client area is smaller than the checkbox bitmap */
846 if (delta
< 0) delta
= 0;
848 if (infoPtr
->state
& BUTTON_HIGHLIGHTED
) x
+= 2 * checkBoxWidth
;
849 if (infoPtr
->state
& (BUTTON_CHECKED
| BUTTON_3STATE
)) x
+= checkBoxWidth
;
850 if (((wndPtr
->dwStyle
& 0x0f) == BS_RADIOBUTTON
) ||
851 ((wndPtr
->dwStyle
& 0x0f) == BS_AUTORADIOBUTTON
)) y
+= checkBoxHeight
;
852 else if (infoPtr
->state
& BUTTON_3STATE
) y
+= 2 * checkBoxHeight
;
854 /* The bitmap for the radio button is not aligned with the
855 * left of the window, it is 1 pixel off. */
856 if (((wndPtr
->dwStyle
& 0x0f) == BS_RADIOBUTTON
) ||
857 ((wndPtr
->dwStyle
& 0x0f) == BS_AUTORADIOBUTTON
))
860 SelectObject( hMemDC
, hbitmapCheckBoxes
);
861 BitBlt( hDC
, rbox
.left
, rbox
.top
+ delta
, checkBoxWidth
,
862 checkBoxHeight
, hMemDC
, x
, y
, SRCCOPY
);
869 if (((wndPtr
->dwStyle
& 0x0f) == BS_RADIOBUTTON
) ||
870 ((wndPtr
->dwStyle
& 0x0f) == BS_AUTORADIOBUTTON
)) state
= DFCS_BUTTONRADIO
;
871 else if (infoPtr
->state
& BUTTON_3STATE
) state
= DFCS_BUTTON3STATE
;
872 else state
= DFCS_BUTTONCHECK
;
874 if (infoPtr
->state
& (BUTTON_CHECKED
| BUTTON_3STATE
)) state
|= DFCS_CHECKED
;
876 if (infoPtr
->state
& BUTTON_HIGHLIGHTED
) state
|= DFCS_PUSHED
;
878 if (wndPtr
->dwStyle
& WS_DISABLED
) state
|= DFCS_INACTIVE
;
880 /* rbox must have the correct height */
881 delta
= rbox
.bottom
- rbox
.top
- checkBoxHeight
;
884 int ofs
= (abs(delta
) / 2);
885 rbox
.bottom
-= ofs
+ 1;
886 rbox
.top
= rbox
.bottom
- checkBoxHeight
;
890 int ofs
= (abs(delta
) / 2);
892 rbox
.bottom
= rbox
.top
+ checkBoxHeight
;
895 DrawFrameControl( hDC
, &rbox
, DFC_BUTTON
, state
);
901 dtFlags
= BUTTON_CalcLabelRect(wndPtr
, hDC
, &rtext
);
903 if (dtFlags
== (UINT
)-1L) /* Noting to draw */
905 hRgn
= CreateRectRgn(client
.left
, client
.top
, client
.right
, client
.bottom
);
906 SelectClipRgn(hDC
, hRgn
);
909 if (action
== ODA_DRAWENTIRE
)
910 BUTTON_DrawLabel(wndPtr
, hDC
, dtFlags
, &rtext
);
913 if ((action
== ODA_FOCUS
) ||
914 ((action
== ODA_DRAWENTIRE
) && (infoPtr
->state
& BUTTON_HASFOCUS
)))
918 IntersectRect(&rtext
, &rtext
, &client
);
919 DrawFocusRect( hDC
, &rtext
);
921 SelectClipRgn(hDC
, 0);
925 /**********************************************************************
926 * BUTTON_CheckAutoRadioButton
928 * wndPtr is checked, uncheck every other auto radio button in group
930 static void BUTTON_CheckAutoRadioButton( WND
*wndPtr
)
932 HWND parent
, sibling
, start
;
933 if (!(wndPtr
->dwStyle
& WS_CHILD
)) return;
934 parent
= wndPtr
->parent
->hwndSelf
;
935 /* assure that starting control is not disabled or invisible */
936 start
= sibling
= GetNextDlgGroupItem( parent
, wndPtr
->hwndSelf
, TRUE
);
941 tmpWnd
= WIN_FindWndPtr(sibling
);
942 if ((wndPtr
->hwndSelf
!= sibling
) &&
943 ((tmpWnd
->dwStyle
& 0x0f) == BS_AUTORADIOBUTTON
))
944 SendMessageW( sibling
, BM_SETCHECK
, BUTTON_UNCHECKED
, 0 );
945 sibling
= GetNextDlgGroupItem( parent
, sibling
, FALSE
);
946 WIN_ReleaseWndPtr(tmpWnd
);
947 } while (sibling
!= start
);
951 /**********************************************************************
952 * Group Box Functions
955 static void GB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
)
958 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
962 if (action
!= ODA_DRAWENTIRE
) return;
965 SelectObject (hDC
, infoPtr
->hFont
);
966 /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
967 hbr
= GetControlBrush16( wndPtr
->hwndSelf
, hDC
, CTLCOLOR_STATIC
);
970 GetClientRect( wndPtr
->hwndSelf
, &rc
);
971 if (TWEAK_WineLook
== WIN31_LOOK
) {
972 HPEN hPrevPen
= SelectObject( hDC
,
973 GetSysColorPen(COLOR_WINDOWFRAME
));
974 HBRUSH hPrevBrush
= SelectObject( hDC
,
975 GetStockObject(NULL_BRUSH
) );
977 Rectangle( hDC
, rc
.left
, rc
.top
+ 2, rc
.right
- 1, rc
.bottom
- 1 );
978 SelectObject( hDC
, hPrevBrush
);
979 SelectObject( hDC
, hPrevPen
);
984 GetTextMetricsW (hDC
, &tm
);
985 rcFrame
.top
+= (tm
.tmHeight
/ 2) - 1;
986 DrawEdge (hDC
, &rcFrame
, EDGE_ETCHED
, BF_RECT
|
987 ((wndPtr
->dwStyle
& BS_FLAT
) ? BF_FLAT
: 0));
990 InflateRect(&rc
, -7, 1);
991 dtFlags
= BUTTON_CalcLabelRect(wndPtr
, hDC
, &rc
);
993 if (dtFlags
== (UINT
)-1L)
996 /* Because buttons have CS_PARENTDC class style, there is a chance
997 * that label will be drawn out of client rect.
998 * But Windows doesn't clip label's rect, so do I.
1001 /* There is 1-pixel marging at the left, right, and bottom */
1002 rc
.left
--; rc
.right
++; rc
.bottom
++;
1003 FillRect(hDC
, &rc
, hbr
);
1004 rc
.left
++; rc
.right
--; rc
.bottom
--;
1006 BUTTON_DrawLabel(wndPtr
, hDC
, dtFlags
, &rc
);
1010 /**********************************************************************
1011 * User Button Functions
1014 static void UB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
)
1018 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
1020 if (action
== ODA_SELECT
) return;
1022 GetClientRect( wndPtr
->hwndSelf
, &rc
);
1024 if (infoPtr
->hFont
) SelectObject( hDC
, infoPtr
->hFont
);
1025 hBrush
= GetControlBrush16( wndPtr
->hwndSelf
, hDC
, CTLCOLOR_BTN
);
1027 FillRect( hDC
, &rc
, hBrush
);
1028 if ((action
== ODA_FOCUS
) ||
1029 ((action
== ODA_DRAWENTIRE
) && (infoPtr
->state
& BUTTON_HASFOCUS
)))
1030 DrawFocusRect( hDC
, &rc
);
1034 /**********************************************************************
1035 * Ownerdrawn Button Functions
1038 static void OB_Paint( WND
*wndPtr
, HDC hDC
, WORD action
)
1040 BUTTONINFO
*infoPtr
= (BUTTONINFO
*)wndPtr
->wExtra
;
1045 dis
.CtlType
= ODT_BUTTON
;
1046 dis
.CtlID
= wndPtr
->wIDmenu
;
1048 dis
.itemAction
= action
;
1049 dis
.itemState
= ((infoPtr
->state
& BUTTON_HASFOCUS
) ? ODS_FOCUS
: 0) |
1050 ((infoPtr
->state
& BUTTON_HIGHLIGHTED
) ? ODS_SELECTED
: 0) |
1051 (IsWindowEnabled(wndPtr
->hwndSelf
) ? 0: ODS_DISABLED
);
1052 dis
.hwndItem
= wndPtr
->hwndSelf
;
1055 GetClientRect( wndPtr
->hwndSelf
, &dis
.rcItem
);
1057 clipRegion
= CreateRectRgnIndirect(&dis
.rcItem
);
1058 if (GetClipRgn(hDC
, clipRegion
) != 1)
1060 DeleteObject(clipRegion
);
1061 clipRegion
=(HRGN
)NULL
;
1063 clipRect
= dis
.rcItem
;
1064 DPtoLP(hDC
, (LPPOINT
) &clipRect
, 2);
1065 IntersectClipRect(hDC
, clipRect
.left
, clipRect
.top
, clipRect
.right
, clipRect
.bottom
);
1067 SetBkColor( hDC
, GetSysColor( COLOR_BTNFACE
) );
1069 SendMessageW( GetParent(wndPtr
->hwndSelf
), WM_DRAWITEM
,
1070 wndPtr
->wIDmenu
, (LPARAM
)&dis
);
1072 SelectClipRgn(hDC
, clipRegion
);