More complete implementation of SHLWAPI_356.
[wine/multimedia.git] / controls / button.c
blob4edaaf5d8535d28848f873414eb3b7c1a9a67449
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
6 */
8 #include <string.h>
9 #include <stdlib.h>
11 #include "winbase.h"
12 #include "windef.h"
13 #include "wingdi.h"
14 #include "wine/winuser16.h"
15 #include "controls.h"
16 #include "user.h"
18 /* GetWindowLong offsets for window extra information */
19 #define STATE_GWL_OFFSET 0
20 #define HFONT_GWL_OFFSET (sizeof(LONG))
21 #define HIMAGE_GWL_OFFSET (2*sizeof(LONG))
22 #define NB_EXTRA_BYTES (3*sizeof(LONG))
24 /* Button state values */
25 #define BUTTON_UNCHECKED 0x00
26 #define BUTTON_CHECKED 0x01
27 #define BUTTON_3STATE 0x02
28 #define BUTTON_HIGHLIGHTED 0x04
29 #define BUTTON_HASFOCUS 0x08
30 #define BUTTON_NSTATES 0x0F
31 /* undocumented flags */
32 #define BUTTON_BTNPRESSED 0x40
33 #define BUTTON_UNKNOWN2 0x20
34 #define BUTTON_UNKNOWN3 0x10
36 static void PB_Paint( HWND hwnd, HDC hDC, UINT action );
37 static void CB_Paint( HWND hwnd, HDC hDC, UINT action );
38 static void GB_Paint( HWND hwnd, HDC hDC, UINT action );
39 static void UB_Paint( HWND hwnd, HDC hDC, UINT action );
40 static void OB_Paint( HWND hwnd, HDC hDC, UINT action );
41 static void BUTTON_CheckAutoRadioButton( HWND hwnd );
42 static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
43 static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
45 #define MAX_BTN_TYPE 12
47 static const WORD maxCheckState[MAX_BTN_TYPE] =
49 BUTTON_UNCHECKED, /* BS_PUSHBUTTON */
50 BUTTON_UNCHECKED, /* BS_DEFPUSHBUTTON */
51 BUTTON_CHECKED, /* BS_CHECKBOX */
52 BUTTON_CHECKED, /* BS_AUTOCHECKBOX */
53 BUTTON_CHECKED, /* BS_RADIOBUTTON */
54 BUTTON_3STATE, /* BS_3STATE */
55 BUTTON_3STATE, /* BS_AUTO3STATE */
56 BUTTON_UNCHECKED, /* BS_GROUPBOX */
57 BUTTON_UNCHECKED, /* BS_USERBUTTON */
58 BUTTON_CHECKED, /* BS_AUTORADIOBUTTON */
59 BUTTON_UNCHECKED, /* Not defined */
60 BUTTON_UNCHECKED /* BS_OWNERDRAW */
63 typedef void (*pfPaint)( HWND hwnd, HDC hdc, UINT action );
65 static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
67 PB_Paint, /* BS_PUSHBUTTON */
68 PB_Paint, /* BS_DEFPUSHBUTTON */
69 CB_Paint, /* BS_CHECKBOX */
70 CB_Paint, /* BS_AUTOCHECKBOX */
71 CB_Paint, /* BS_RADIOBUTTON */
72 CB_Paint, /* BS_3STATE */
73 CB_Paint, /* BS_AUTO3STATE */
74 GB_Paint, /* BS_GROUPBOX */
75 UB_Paint, /* BS_USERBUTTON */
76 CB_Paint, /* BS_AUTORADIOBUTTON */
77 NULL, /* Not defined */
78 OB_Paint /* BS_OWNERDRAW */
81 static HBITMAP hbitmapCheckBoxes = 0;
82 static WORD checkBoxWidth = 0, checkBoxHeight = 0;
85 /*********************************************************************
86 * button class descriptor
88 const struct builtin_class_descr BUTTON_builtin_class =
90 "Button", /* name */
91 CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */
92 ButtonWndProcA, /* procA */
93 ButtonWndProcW, /* procW */
94 NB_EXTRA_BYTES, /* extra */
95 IDC_ARROWA, /* cursor */
96 0 /* brush */
100 inline static LONG get_button_state( HWND hwnd )
102 return GetWindowLongA( hwnd, STATE_GWL_OFFSET );
105 inline static void set_button_state( HWND hwnd, LONG state )
107 SetWindowLongA( hwnd, STATE_GWL_OFFSET, state );
110 inline static HFONT get_button_font( HWND hwnd )
112 return GetWindowLongA( hwnd, HFONT_GWL_OFFSET );
115 inline static void set_button_font( HWND hwnd, HFONT font )
117 SetWindowLongA( hwnd, HFONT_GWL_OFFSET, font );
120 inline static UINT get_button_type( LONG window_style )
122 return (window_style & 0x0f);
125 /* paint a button of any type */
126 inline static void paint_button( HWND hwnd, LONG style, UINT action )
128 if (btnPaintFunc[style] && IsWindowVisible(hwnd))
130 HDC hdc = GetDC( hwnd );
131 btnPaintFunc[style]( hwnd, hdc, action );
132 ReleaseDC( hwnd, hdc );
136 /* retrieve the button text; returned buffer must be freed by caller */
137 inline static WCHAR *get_button_text( HWND hwnd )
139 INT len = GetWindowTextLengthW( hwnd );
140 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
141 if (buffer) GetWindowTextW( hwnd, buffer, len + 1 );
142 return buffer;
145 /***********************************************************************
146 * ButtonWndProc_common
148 static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg,
149 WPARAM wParam, LPARAM lParam, BOOL unicode )
151 RECT rect;
152 POINT pt;
153 LONG style = GetWindowLongA( hWnd, GWL_STYLE );
154 UINT btn_type = get_button_type( style );
155 LONG state;
156 HANDLE oldHbitmap;
158 pt.x = LOWORD(lParam);
159 pt.y = HIWORD(lParam);
161 switch (uMsg)
163 case WM_GETDLGCODE:
164 switch(btn_type)
166 case BS_PUSHBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
167 case BS_DEFPUSHBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
168 case BS_RADIOBUTTON:
169 case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON;
170 default: return DLGC_BUTTON;
173 case WM_ENABLE:
174 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
175 break;
177 case WM_CREATE:
178 if (!hbitmapCheckBoxes)
180 BITMAP bmp;
181 hbitmapCheckBoxes = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CHECKBOXES));
182 GetObjectW( hbitmapCheckBoxes, sizeof(bmp), &bmp );
183 checkBoxWidth = bmp.bmWidth / 4;
184 checkBoxHeight = bmp.bmHeight / 3;
186 if (btn_type < 0L || btn_type >= MAX_BTN_TYPE)
187 return -1; /* abort */
188 set_button_state( hWnd, BUTTON_UNCHECKED );
189 return 0;
191 case WM_ERASEBKGND:
192 return 1;
194 case WM_PAINT:
195 if (btnPaintFunc[btn_type])
197 PAINTSTRUCT ps;
198 HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps );
199 int nOldMode = SetBkMode( hdc, OPAQUE );
200 (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE );
201 SetBkMode(hdc, nOldMode); /* reset painting mode */
202 if( !wParam ) EndPaint( hWnd, &ps );
204 break;
206 case WM_KEYDOWN:
207 if (wParam == VK_SPACE)
209 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
210 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
212 break;
214 case WM_LBUTTONDBLCLK:
215 if(style & BS_NOTIFY ||
216 btn_type == BS_RADIOBUTTON ||
217 btn_type == BS_USERBUTTON ||
218 btn_type == BS_OWNERDRAW)
220 SendMessageW( GetParent(hWnd), WM_COMMAND,
221 MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_DOUBLECLICKED ),
222 (LPARAM)hWnd);
223 break;
225 /* fall through */
226 case WM_LBUTTONDOWN:
227 SetCapture( hWnd );
228 SetFocus( hWnd );
229 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
230 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
231 break;
233 case WM_KEYUP:
234 if (wParam != VK_SPACE)
235 break;
236 /* fall through */
237 case WM_LBUTTONUP:
238 state = get_button_state( hWnd );
239 if (!(state & BUTTON_BTNPRESSED)) break;
240 state &= BUTTON_NSTATES;
241 set_button_state( hWnd, state );
242 if (!(state & BUTTON_HIGHLIGHTED))
244 ReleaseCapture();
245 break;
247 SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
248 ReleaseCapture();
249 GetClientRect( hWnd, &rect );
250 if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
252 state = get_button_state( hWnd );
253 switch(btn_type)
255 case BS_AUTOCHECKBOX:
256 SendMessageW( hWnd, BM_SETCHECK, !(state & BUTTON_CHECKED), 0 );
257 break;
258 case BS_AUTORADIOBUTTON:
259 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
260 break;
261 case BS_AUTO3STATE:
262 SendMessageW( hWnd, BM_SETCHECK,
263 (state & BUTTON_3STATE) ? 0 : ((state & 3) + 1), 0 );
264 break;
266 SendMessageW( GetParent(hWnd), WM_COMMAND,
267 MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_CLICKED ), (LPARAM)hWnd);
269 break;
271 case WM_CAPTURECHANGED:
272 state = get_button_state( hWnd );
273 if (state & BUTTON_BTNPRESSED)
275 state &= BUTTON_NSTATES;
276 set_button_state( hWnd, state );
277 if (state & BUTTON_HIGHLIGHTED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
279 break;
281 case WM_MOUSEMOVE:
282 if ((wParam & MK_LBUTTON) && GetCapture() == hWnd)
284 GetClientRect( hWnd, &rect );
285 SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
287 break;
289 case WM_SETTEXT:
290 if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
291 else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam );
292 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
293 return 1; /* success. FIXME: check text length */
295 case WM_SETFONT:
296 set_button_font( hWnd, wParam );
297 if (lParam) paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
298 break;
300 case WM_GETFONT:
301 return get_button_font( hWnd );
303 case WM_SETFOCUS:
304 if ((btn_type == BS_RADIOBUTTON || btn_type == BS_AUTORADIOBUTTON) && (GetCapture() != hWnd) &&
305 !(SendMessageW(hWnd, BM_GETCHECK, 0, 0) & BST_CHECKED))
307 /* The notification is sent when the button (BS_AUTORADIOBUTTON)
308 is unchecked and the focus was not given by a mouse click. */
309 if (btn_type == BS_AUTORADIOBUTTON)
310 SendMessageW( hWnd, BM_SETCHECK, BUTTON_CHECKED, 0 );
311 SendMessageW( GetParent(hWnd), WM_COMMAND,
312 MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_CLICKED ), (LPARAM)hWnd);
314 set_button_state( hWnd, get_button_state(hWnd) | BUTTON_HASFOCUS );
315 paint_button( hWnd, btn_type, ODA_FOCUS );
316 break;
318 case WM_KILLFOCUS:
319 set_button_state( hWnd, get_button_state(hWnd) & ~BUTTON_HASFOCUS );
320 paint_button( hWnd, btn_type, ODA_FOCUS );
321 InvalidateRect( hWnd, NULL, TRUE );
322 break;
324 case WM_SYSCOLORCHANGE:
325 InvalidateRect( hWnd, NULL, FALSE );
326 break;
328 case BM_SETSTYLE16:
329 case BM_SETSTYLE:
330 if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
331 btn_type = wParam & 0x0f;
332 style = (style & ~0x0f) | btn_type;
333 SetWindowLongA( hWnd, GWL_STYLE, style );
335 /* Only redraw if lParam flag is set.*/
336 if (lParam)
337 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
339 break;
341 case BM_CLICK:
342 SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
343 SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
344 break;
346 case BM_SETIMAGE:
347 /* Check that image format matches button style */
348 switch (style & (BS_BITMAP|BS_ICON))
350 case BS_BITMAP:
351 if (wParam != IMAGE_BITMAP) return 0;
352 break;
353 case BS_ICON:
354 if (wParam != IMAGE_ICON) return 0;
355 break;
356 default:
357 return 0;
359 oldHbitmap = SetWindowLongA( hWnd, HIMAGE_GWL_OFFSET, lParam );
360 InvalidateRect( hWnd, NULL, FALSE );
361 return oldHbitmap;
363 case BM_GETIMAGE:
364 return GetWindowLongA( hWnd, HIMAGE_GWL_OFFSET );
366 case BM_GETCHECK16:
367 case BM_GETCHECK:
368 return get_button_state( hWnd ) & 3;
370 case BM_SETCHECK16:
371 case BM_SETCHECK:
372 if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
373 state = get_button_state( hWnd );
374 if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
376 if (wParam) style |= WS_TABSTOP;
377 else style &= ~WS_TABSTOP;
378 SetWindowLongA( hWnd, GWL_STYLE, style );
380 if ((state & 3) != wParam)
382 set_button_state( hWnd, (state & ~3) | wParam );
383 paint_button( hWnd, btn_type, ODA_SELECT );
385 if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BUTTON_CHECKED) && (style & WS_CHILD))
386 BUTTON_CheckAutoRadioButton( hWnd );
387 break;
389 case BM_GETSTATE16:
390 case BM_GETSTATE:
391 return get_button_state( hWnd );
393 case BM_SETSTATE16:
394 case BM_SETSTATE:
395 state = get_button_state( hWnd );
396 if (wParam)
398 if (state & BUTTON_HIGHLIGHTED) break;
399 set_button_state( hWnd, state | BUTTON_HIGHLIGHTED );
401 else
403 if (!(state & BUTTON_HIGHLIGHTED)) break;
404 set_button_state( hWnd, state & ~BUTTON_HIGHLIGHTED );
406 paint_button( hWnd, btn_type, ODA_SELECT );
407 break;
409 case WM_NCHITTEST:
410 if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
411 /* fall through */
412 default:
413 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
414 DefWindowProcA(hWnd, uMsg, wParam, lParam);
416 return 0;
419 /***********************************************************************
420 * ButtonWndProcW
421 * The button window procedure. This is just a wrapper which locks
422 * the passed HWND and calls the real window procedure (with a WND*
423 * pointer pointing to the locked windowstructure).
425 static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
427 if (!IsWindow( hWnd )) return 0;
428 return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, TRUE );
432 /***********************************************************************
433 * ButtonWndProcA
435 static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
437 if (!IsWindow( hWnd )) return 0;
438 return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, FALSE );
442 /**********************************************************************
443 * Convert button styles to flags used by DrawText.
444 * TODO: handle WS_EX_RIGHT extended style.
446 static UINT BUTTON_BStoDT(DWORD style)
448 UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */
450 /* "Convert" pushlike buttons to pushbuttons */
451 if (style & BS_PUSHLIKE)
452 style &= ~0x0F;
454 if (!(style & BS_MULTILINE))
455 dtStyle |= DT_SINGLELINE;
456 else
457 dtStyle |= DT_WORDBREAK;
459 switch (style & BS_CENTER)
461 case BS_LEFT: /* DT_LEFT is 0 */ break;
462 case BS_RIGHT: dtStyle |= DT_RIGHT; break;
463 case BS_CENTER: dtStyle |= DT_CENTER; break;
464 default:
465 /* Pushbutton's text is centered by default */
466 if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
467 /* all other flavours have left aligned text */
470 /* DrawText ignores vertical alignment for multiline text,
471 * but we use these flags to align label manualy.
473 if (get_button_type(style) != BS_GROUPBOX)
475 switch (style & BS_VCENTER)
477 case BS_TOP: /* DT_TOP is 0 */ break;
478 case BS_BOTTOM: dtStyle |= DT_BOTTOM; break;
479 case BS_VCENTER: /* fall through */
480 default: dtStyle |= DT_VCENTER; break;
483 else
484 /* GroupBox's text is always single line and is top aligned. */
485 dtStyle |= DT_SINGLELINE;
487 return dtStyle;
490 /**********************************************************************
491 * BUTTON_CalcLabelRect
493 * Calculates label's rectangle depending on button style.
495 * Returns flags to be passed to DrawText.
496 * Calculated rectangle doesn't take into account button state
497 * (pushed, etc.). If there is nothing to draw (no text/image) output
498 * rectangle is empty, and return value is (UINT)-1.
500 static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
502 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
503 WCHAR *text;
504 ICONINFO iconInfo;
505 BITMAP bm;
506 UINT dtStyle = BUTTON_BStoDT(style);
507 RECT r = *rc;
508 INT n;
510 /* Calculate label rectangle according to label type */
511 switch (style & (BS_ICON|BS_BITMAP))
513 case BS_TEXT:
514 if (!(text = get_button_text( hwnd ))) goto empty_rect;
515 if (!text[0])
517 HeapFree( GetProcessHeap(), 0, text );
518 goto empty_rect;
520 DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
521 HeapFree( GetProcessHeap(), 0, text );
522 break;
524 case BS_ICON:
525 if (!GetIconInfo((HICON)GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
526 goto empty_rect;
528 GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
530 r.right = r.left + bm.bmWidth;
531 r.bottom = r.top + bm.bmHeight;
533 DeleteObject(iconInfo.hbmColor);
534 DeleteObject(iconInfo.hbmMask);
535 break;
537 case BS_BITMAP:
538 if (!GetObjectW( (HANDLE)GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
539 goto empty_rect;
541 r.right = r.left + bm.bmWidth;
542 r.bottom = r.top + bm.bmHeight;
543 break;
545 default:
546 empty_rect:
547 r.right = r.left;
548 r.bottom = r.top;
549 return (UINT)(LONG)-1;
552 /* Position label inside bounding rectangle according to
553 * alignment flags. (calculated rect is always left-top aligned).
554 * If label is aligned to any side - shift label in opposite
555 * direction to leave extra space for focus rectangle.
557 switch (dtStyle & (DT_CENTER|DT_RIGHT))
559 case DT_LEFT: r.left++; r.right++; break;
560 case DT_CENTER: n = r.right - r.left;
561 r.left = rc->left + ((rc->right - rc->left) - n) / 2;
562 r.right = r.left + n; break;
563 case DT_RIGHT: n = r.right - r.left;
564 r.right = rc->right - 1;
565 r.left = r.right - n;
566 break;
569 switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
571 case DT_TOP: r.top++; r.bottom++; break;
572 case DT_VCENTER: n = r.bottom - r.top;
573 r.top = rc->top + ((rc->bottom - rc->top) - n) / 2;
574 r.bottom = r.top + n; break;
575 case DT_BOTTOM: n = r.bottom - r.top;
576 r.bottom = rc->bottom - 1;
577 r.top = r.bottom - n;
578 break;
581 *rc = r;
582 return dtStyle;
586 /**********************************************************************
587 * BUTTON_DrawTextCallback
589 * Callback function used by DrawStateW function.
591 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
593 RECT rc;
594 rc.left = 0;
595 rc.top = 0;
596 rc.right = cx;
597 rc.bottom = cy;
599 DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
600 return TRUE;
604 /**********************************************************************
605 * BUTTON_DrawLabel
607 * Common function for drawing button label.
609 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc)
611 DRAWSTATEPROC lpOutputProc = NULL;
612 LPARAM lp;
613 WPARAM wp = 0;
614 HBRUSH hbr = 0;
615 UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
616 LONG state = get_button_state( hwnd );
617 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
618 WCHAR *text = NULL;
620 /* FIXME: To draw disabled label in Win31 look-and-feel, we probably
621 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
622 * I don't have Win31 on hand to verify that, so I leave it as is.
625 if ((style & BS_PUSHLIKE) && (state & BUTTON_3STATE))
627 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
628 flags |= DSS_MONO;
631 switch (style & (BS_ICON|BS_BITMAP))
633 case BS_TEXT:
634 /* DST_COMPLEX -- is 0 */
635 lpOutputProc = BUTTON_DrawTextCallback;
636 if (!(text = get_button_text( hwnd ))) return;
637 lp = (LPARAM)text;
638 wp = (WPARAM)dtFlags;
639 break;
641 case BS_ICON:
642 flags |= DST_ICON;
643 lp = GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET );
644 break;
646 case BS_BITMAP:
647 flags |= DST_BITMAP;
648 lp = GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET );
649 break;
651 default:
652 return;
655 DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
656 rc->right - rc->left, rc->bottom - rc->top, flags);
657 if (text) HeapFree( GetProcessHeap(), 0, text );
660 /**********************************************************************
661 * Push Button Functions
663 static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
665 RECT rc, focus_rect, r;
666 UINT dtFlags;
667 HRGN hRgn;
668 HPEN hOldPen;
669 HBRUSH hOldBrush;
670 INT oldBkMode;
671 COLORREF oldTxtColor;
672 HFONT hFont;
673 LONG state = get_button_state( hwnd );
674 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
675 BOOL pushedState = (state & BUTTON_HIGHLIGHTED);
677 GetClientRect( hwnd, &rc );
679 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
680 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
681 SendMessageW( GetParent(hwnd), WM_CTLCOLORBTN, hDC, (LPARAM)hwnd );
682 hOldPen = (HPEN)SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
683 hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
684 oldBkMode = SetBkMode(hDC, TRANSPARENT);
686 if ( TWEAK_WineLook == WIN31_LOOK)
688 COLORREF clr_wnd = GetSysColor(COLOR_WINDOW);
689 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
691 SetPixel( hDC, rc.left, rc.top, clr_wnd);
692 SetPixel( hDC, rc.left, rc.bottom-1, clr_wnd);
693 SetPixel( hDC, rc.right-1, rc.top, clr_wnd);
694 SetPixel( hDC, rc.right-1, rc.bottom-1, clr_wnd);
695 InflateRect( &rc, -1, -1 );
698 if (get_button_type(style) == BS_DEFPUSHBUTTON)
700 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
701 InflateRect( &rc, -1, -1 );
704 if (TWEAK_WineLook == WIN31_LOOK)
706 if (pushedState)
708 /* draw button shadow: */
709 SelectObject(hDC, GetSysColorBrush(COLOR_BTNSHADOW));
710 PatBlt(hDC, rc.left, rc.top, 1, rc.bottom-rc.top, PATCOPY );
711 PatBlt(hDC, rc.left, rc.top, rc.right-rc.left, 1, PATCOPY );
712 } else {
713 rc.right++, rc.bottom++;
714 DrawEdge( hDC, &rc, EDGE_RAISED, BF_RECT );
715 rc.right--, rc.bottom--;
718 else
720 UINT uState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
722 if (style & BS_FLAT)
723 uState |= DFCS_MONO;
724 else if (pushedState)
726 if (get_button_type(style) == BS_DEFPUSHBUTTON )
727 uState |= DFCS_FLAT;
728 else
729 uState |= DFCS_PUSHED;
732 if (state & (BUTTON_CHECKED | BUTTON_3STATE))
733 uState |= DFCS_CHECKED;
735 DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
737 focus_rect = rc;
740 /* draw button label */
741 r = rc;
742 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
744 if (dtFlags == (UINT)-1L)
745 goto cleanup;
747 if (pushedState)
748 OffsetRect(&r, 1, 1);
750 if(TWEAK_WineLook == WIN31_LOOK)
752 focus_rect = r;
753 InflateRect(&focus_rect, 2, 0);
756 hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
757 SelectClipRgn(hDC, hRgn);
759 oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
761 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
763 SetTextColor( hDC, oldTxtColor );
764 SelectClipRgn(hDC, 0);
765 DeleteObject(hRgn);
767 if (state & BUTTON_HASFOCUS)
769 InflateRect( &focus_rect, -1, -1 );
770 IntersectRect(&focus_rect, &focus_rect, &rc);
771 DrawFocusRect( hDC, &focus_rect );
774 cleanup:
775 SelectObject( hDC, hOldPen );
776 SelectObject( hDC, hOldBrush );
777 SetBkMode(hDC, oldBkMode);
780 /**********************************************************************
781 * Check Box & Radio Button Functions
784 static void CB_Paint( HWND hwnd, HDC hDC, UINT action )
786 RECT rbox, rtext, client;
787 HBRUSH hBrush;
788 int delta;
789 UINT dtFlags;
790 HRGN hRgn;
791 HFONT hFont;
792 LONG state = get_button_state( hwnd );
793 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
795 if (style & BS_PUSHLIKE)
797 PB_Paint( hwnd, hDC, action );
798 return;
801 GetClientRect(hwnd, &client);
802 rbox = rtext = client;
804 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
806 hBrush = SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC, hDC, (LPARAM)hwnd );
807 if (!hBrush) /* did the app forget to call defwindowproc ? */
808 hBrush = DefWindowProcW( GetParent(hwnd), WM_CTLCOLORSTATIC, hDC, (LPARAM)hwnd );
810 if (style & BS_LEFTTEXT)
812 /* magic +4 is what CTL3D expects */
814 rtext.right -= checkBoxWidth + 4;
815 rbox.left = rbox.right - checkBoxWidth;
817 else
819 rtext.left += checkBoxWidth + 4;
820 rbox.right = checkBoxWidth;
823 /* Draw the check-box bitmap */
824 if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
826 /* Since WM_ERASEBKGND does nothing, first prepare background */
827 if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
828 else FillRect( hDC, &client, hBrush );
830 if( TWEAK_WineLook == WIN31_LOOK )
832 HDC hMemDC = CreateCompatibleDC( hDC );
833 int x = 0, y = 0;
834 delta = (rbox.bottom - rbox.top - checkBoxHeight) / 2;
836 /* Check in case the client area is smaller than the checkbox bitmap */
837 if (delta < 0) delta = 0;
839 if (state & BUTTON_HIGHLIGHTED) x += 2 * checkBoxWidth;
840 if (state & (BUTTON_CHECKED | BUTTON_3STATE)) x += checkBoxWidth;
841 if ((get_button_type(style) == BS_RADIOBUTTON) ||
842 (get_button_type(style) == BS_AUTORADIOBUTTON)) y += checkBoxHeight;
843 else if (state & BUTTON_3STATE) y += 2 * checkBoxHeight;
845 /* The bitmap for the radio button is not aligned with the
846 * left of the window, it is 1 pixel off. */
847 if ((get_button_type(style) == BS_RADIOBUTTON) ||
848 (get_button_type(style) == BS_AUTORADIOBUTTON))
849 rbox.left += 1;
851 SelectObject( hMemDC, hbitmapCheckBoxes );
852 BitBlt( hDC, rbox.left, rbox.top + delta, checkBoxWidth,
853 checkBoxHeight, hMemDC, x, y, SRCCOPY );
854 DeleteDC( hMemDC );
856 else
858 UINT flags;
860 if ((get_button_type(style) == BS_RADIOBUTTON) ||
861 (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO;
862 else if (state & BUTTON_3STATE) flags = DFCS_BUTTON3STATE;
863 else flags = DFCS_BUTTONCHECK;
865 if (state & (BUTTON_CHECKED | BUTTON_3STATE)) flags |= DFCS_CHECKED;
866 if (state & BUTTON_HIGHLIGHTED) flags |= DFCS_PUSHED;
868 if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
870 /* rbox must have the correct height */
871 delta = rbox.bottom - rbox.top - checkBoxHeight;
872 if (delta > 0)
874 int ofs = (abs(delta) / 2);
875 rbox.bottom -= ofs + 1;
876 rbox.top = rbox.bottom - checkBoxHeight;
878 else if (delta < 0)
880 int ofs = (abs(delta) / 2);
881 rbox.top -= ofs + 1;
882 rbox.bottom = rbox.top + checkBoxHeight;
885 DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags );
889 /* Draw label */
890 client = rtext;
891 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext);
893 if (dtFlags == (UINT)-1L) /* Noting to draw */
894 return;
895 hRgn = CreateRectRgn(client.left, client.top, client.right, client.bottom);
896 SelectClipRgn(hDC, hRgn);
897 DeleteObject(hRgn);
899 if (action == ODA_DRAWENTIRE)
900 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext);
902 /* ... and focus */
903 if ((action == ODA_FOCUS) ||
904 ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
906 rtext.left--;
907 rtext.right++;
908 IntersectRect(&rtext, &rtext, &client);
909 DrawFocusRect( hDC, &rtext );
911 SelectClipRgn(hDC, 0);
915 /**********************************************************************
916 * BUTTON_CheckAutoRadioButton
918 * hwnd is checked, uncheck every other auto radio button in group
920 static void BUTTON_CheckAutoRadioButton( HWND hwnd )
922 HWND parent, sibling, start;
924 parent = GetParent(hwnd);
925 /* make sure that starting control is not disabled or invisible */
926 start = sibling = GetNextDlgGroupItem( parent, hwnd, TRUE );
929 if (!sibling) break;
930 if ((hwnd != sibling) &&
931 ((GetWindowLongA( sibling, GWL_STYLE) & 0x0f) == BS_AUTORADIOBUTTON))
932 SendMessageW( sibling, BM_SETCHECK, BUTTON_UNCHECKED, 0 );
933 sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
934 } while (sibling != start);
938 /**********************************************************************
939 * Group Box Functions
942 static void GB_Paint( HWND hwnd, HDC hDC, UINT action )
944 RECT rc, rcFrame;
945 HBRUSH hbr;
946 HFONT hFont;
947 UINT dtFlags;
948 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
950 if (action != ODA_DRAWENTIRE) return;
952 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
953 /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
954 hbr = SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC, hDC, (LPARAM)hwnd );
955 if (!hbr) /* did the app forget to call defwindowproc ? */
956 hbr = DefWindowProcW( GetParent(hwnd), WM_CTLCOLORSTATIC, hDC, (LPARAM)hwnd );
958 GetClientRect( hwnd, &rc);
959 if (TWEAK_WineLook == WIN31_LOOK) {
960 HPEN hPrevPen = SelectObject( hDC,
961 GetSysColorPen(COLOR_WINDOWFRAME));
962 HBRUSH hPrevBrush = SelectObject( hDC,
963 GetStockObject(NULL_BRUSH) );
965 Rectangle( hDC, rc.left, rc.top + 2, rc.right - 1, rc.bottom - 1 );
966 SelectObject( hDC, hPrevBrush );
967 SelectObject( hDC, hPrevPen );
968 } else {
969 TEXTMETRICW tm;
970 rcFrame = rc;
972 GetTextMetricsW (hDC, &tm);
973 rcFrame.top += (tm.tmHeight / 2) - 1;
974 DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
977 InflateRect(&rc, -7, 1);
978 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc);
980 if (dtFlags == (UINT)-1L)
981 return;
983 /* Because buttons have CS_PARENTDC class style, there is a chance
984 * that label will be drawn out of client rect.
985 * But Windows doesn't clip label's rect, so do I.
988 /* There is 1-pixel marging at the left, right, and bottom */
989 rc.left--; rc.right++; rc.bottom++;
990 FillRect(hDC, &rc, hbr);
991 rc.left++; rc.right--; rc.bottom--;
993 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc);
997 /**********************************************************************
998 * User Button Functions
1001 static void UB_Paint( HWND hwnd, HDC hDC, UINT action )
1003 RECT rc;
1004 HBRUSH hBrush;
1005 HFONT hFont;
1006 LONG state = get_button_state( hwnd );
1008 if (action == ODA_SELECT) return;
1010 GetClientRect( hwnd, &rc);
1012 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1014 hBrush = SendMessageW( GetParent(hwnd), WM_CTLCOLORBTN, hDC, (LPARAM)hwnd );
1015 if (!hBrush) /* did the app forget to call defwindowproc ? */
1016 hBrush = DefWindowProcW( GetParent(hwnd), WM_CTLCOLORBTN, hDC, (LPARAM)hwnd );
1018 FillRect( hDC, &rc, hBrush );
1019 if ((action == ODA_FOCUS) ||
1020 ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
1021 DrawFocusRect( hDC, &rc );
1025 /**********************************************************************
1026 * Ownerdrawn Button Functions
1029 static void OB_Paint( HWND hwnd, HDC hDC, UINT action )
1031 LONG state = get_button_state( hwnd );
1032 DRAWITEMSTRUCT dis;
1033 HRGN clipRegion;
1034 RECT clipRect;
1035 UINT id = GetWindowLongA( hwnd, GWL_ID );
1037 dis.CtlType = ODT_BUTTON;
1038 dis.CtlID = id;
1039 dis.itemID = 0;
1040 dis.itemAction = action;
1041 dis.itemState = ((state & BUTTON_HASFOCUS) ? ODS_FOCUS : 0) |
1042 ((state & BUTTON_HIGHLIGHTED) ? ODS_SELECTED : 0) |
1043 (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED);
1044 dis.hwndItem = hwnd;
1045 dis.hDC = hDC;
1046 dis.itemData = 0;
1047 GetClientRect( hwnd, &dis.rcItem );
1049 clipRegion = CreateRectRgnIndirect(&dis.rcItem);
1050 if (GetClipRgn(hDC, clipRegion) != 1)
1052 DeleteObject(clipRegion);
1053 clipRegion=(HRGN)NULL;
1055 clipRect = dis.rcItem;
1056 DPtoLP(hDC, (LPPOINT) &clipRect, 2);
1057 IntersectClipRect(hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
1059 SetBkColor( hDC, GetSysColor( COLOR_BTNFACE ) );
1060 SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
1061 SelectClipRgn(hDC, clipRegion);