Create a color bitmap in CreateDIBitmap even with a black&white DC.
[wine/multimedia.git] / controls / button.c
blob20ac81596262fed98bc222b4cee218788806d3b8
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 ), hWnd);
222 break;
224 /* fall through */
225 case WM_LBUTTONDOWN:
226 SetCapture( hWnd );
227 SetFocus( hWnd );
228 SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
229 set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED );
230 break;
232 case WM_KEYUP:
233 if (wParam != VK_SPACE)
234 break;
235 /* fall through */
236 case WM_LBUTTONUP:
237 state = get_button_state( hWnd );
238 if (!(state & BUTTON_BTNPRESSED)) break;
239 state &= BUTTON_NSTATES;
240 set_button_state( hWnd, state );
241 if (!(state & BUTTON_HIGHLIGHTED))
243 ReleaseCapture();
244 break;
246 SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
247 ReleaseCapture();
248 GetClientRect( hWnd, &rect );
249 if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
251 state = get_button_state( hWnd );
252 switch(btn_type)
254 case BS_AUTOCHECKBOX:
255 SendMessageW( hWnd, BM_SETCHECK, !(state & BUTTON_CHECKED), 0 );
256 break;
257 case BS_AUTORADIOBUTTON:
258 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
259 break;
260 case BS_AUTO3STATE:
261 SendMessageW( hWnd, BM_SETCHECK,
262 (state & BUTTON_3STATE) ? 0 : ((state & 3) + 1), 0 );
263 break;
265 SendMessageW( GetParent(hWnd), WM_COMMAND,
266 MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_CLICKED ), hWnd);
268 break;
270 case WM_CAPTURECHANGED:
271 state = get_button_state( hWnd );
272 if (state & BUTTON_BTNPRESSED)
274 state &= BUTTON_NSTATES;
275 set_button_state( hWnd, state );
276 if (state & BUTTON_HIGHLIGHTED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
278 break;
280 case WM_MOUSEMOVE:
281 if (GetCapture() == hWnd)
283 GetClientRect( hWnd, &rect );
284 SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
286 break;
288 case WM_SETTEXT:
289 if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam );
290 else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam );
291 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
292 return 1; /* success. FIXME: check text length */
294 case WM_SETFONT:
295 set_button_font( hWnd, wParam );
296 if (lParam) paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
297 break;
299 case WM_GETFONT:
300 return get_button_font( hWnd );
302 case WM_SETFOCUS:
303 if ((btn_type == BS_RADIOBUTTON || btn_type == BS_AUTORADIOBUTTON) && (GetCapture() != hWnd) &&
304 !(SendMessageW(hWnd, BM_GETCHECK, 0, 0) & BST_CHECKED))
306 /* The notification is sent when the button (BS_AUTORADIOBUTTON)
307 is unchecked and the focus was not given by a mouse click. */
308 if (btn_type == BS_AUTORADIOBUTTON)
309 SendMessageW( hWnd, BM_SETCHECK, BUTTON_CHECKED, 0 );
310 SendMessageW( GetParent(hWnd), WM_COMMAND,
311 MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_CLICKED ), hWnd);
313 set_button_state( hWnd, get_button_state(hWnd) | BUTTON_HASFOCUS );
314 paint_button( hWnd, btn_type, ODA_FOCUS );
315 break;
317 case WM_KILLFOCUS:
318 set_button_state( hWnd, get_button_state(hWnd) & ~BUTTON_HASFOCUS );
319 paint_button( hWnd, btn_type, ODA_FOCUS );
320 InvalidateRect( hWnd, NULL, TRUE );
321 break;
323 case WM_SYSCOLORCHANGE:
324 InvalidateRect( hWnd, NULL, FALSE );
325 break;
327 case BM_SETSTYLE16:
328 case BM_SETSTYLE:
329 if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
330 btn_type = wParam & 0x0f;
331 style = (style & ~0x0f) | btn_type;
332 SetWindowLongA( hWnd, GWL_STYLE, style );
334 /* Only redraw if lParam flag is set.*/
335 if (lParam)
336 paint_button( hWnd, btn_type, ODA_DRAWENTIRE );
338 break;
340 case BM_CLICK:
341 SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
342 SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
343 break;
345 case BM_SETIMAGE:
346 /* Check that image format matches button style */
347 switch (style & (BS_BITMAP|BS_ICON))
349 case BS_BITMAP:
350 if (wParam != IMAGE_BITMAP) return 0;
351 break;
352 case BS_ICON:
353 if (wParam != IMAGE_ICON) return 0;
354 break;
355 default:
356 return 0;
358 oldHbitmap = SetWindowLongA( hWnd, HIMAGE_GWL_OFFSET, lParam );
359 InvalidateRect( hWnd, NULL, FALSE );
360 return oldHbitmap;
362 case BM_GETIMAGE:
363 return GetWindowLongA( hWnd, HIMAGE_GWL_OFFSET );
365 case BM_GETCHECK16:
366 case BM_GETCHECK:
367 return get_button_state( hWnd ) & 3;
369 case BM_SETCHECK16:
370 case BM_SETCHECK:
371 if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type];
372 state = get_button_state( hWnd );
373 if ((state & 3) != wParam)
375 if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON))
377 if (wParam) style |= WS_TABSTOP;
378 else style &= ~WS_TABSTOP;
379 SetWindowLongA( hWnd, GWL_STYLE, style );
381 set_button_state( hWnd, (state & ~3) | wParam );
382 paint_button( hWnd, btn_type, ODA_SELECT );
384 if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BUTTON_CHECKED) && (style & WS_CHILD))
385 BUTTON_CheckAutoRadioButton( hWnd );
386 break;
388 case BM_GETSTATE16:
389 case BM_GETSTATE:
390 return get_button_state( hWnd );
392 case BM_SETSTATE16:
393 case BM_SETSTATE:
394 state = get_button_state( hWnd );
395 if (wParam)
397 if (state & BUTTON_HIGHLIGHTED) break;
398 set_button_state( hWnd, state | BUTTON_HIGHLIGHTED );
400 else
402 if (!(state & BUTTON_HIGHLIGHTED)) break;
403 set_button_state( hWnd, state & ~BUTTON_HIGHLIGHTED );
405 paint_button( hWnd, btn_type, ODA_SELECT );
406 break;
408 case WM_NCHITTEST:
409 if(btn_type == BS_GROUPBOX) return HTTRANSPARENT;
410 /* fall through */
411 default:
412 return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
413 DefWindowProcA(hWnd, uMsg, wParam, lParam);
415 return 0;
418 /***********************************************************************
419 * ButtonWndProcW
420 * The button window procedure. This is just a wrapper which locks
421 * the passed HWND and calls the real window procedure (with a WND*
422 * pointer pointing to the locked windowstructure).
424 static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
426 if (!IsWindow( hWnd )) return 0;
427 return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, TRUE );
431 /***********************************************************************
432 * ButtonWndProcA
434 static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
436 if (!IsWindow( hWnd )) return 0;
437 return ButtonWndProc_common( hWnd, uMsg, wParam, lParam, FALSE );
441 /**********************************************************************
442 * Convert button styles to flags used by DrawText.
443 * TODO: handle WS_EX_RIGHT extended style.
445 static UINT BUTTON_BStoDT(DWORD style)
447 UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */
449 /* "Convert" pushlike buttons to pushbuttons */
450 if (style & BS_PUSHLIKE)
451 style &= ~0x0F;
453 if (!(style & BS_MULTILINE))
454 dtStyle |= DT_SINGLELINE;
455 else
456 dtStyle |= DT_WORDBREAK;
458 switch (style & BS_CENTER)
460 case BS_LEFT: /* DT_LEFT is 0 */ break;
461 case BS_RIGHT: dtStyle |= DT_RIGHT; break;
462 case BS_CENTER: dtStyle |= DT_CENTER; break;
463 default:
464 /* Pushbutton's text is centered by default */
465 if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER;
466 /* all other flavours have left aligned text */
469 /* DrawText ignores vertical alignment for multiline text,
470 * but we use these flags to align label manualy.
472 if (get_button_type(style) != BS_GROUPBOX)
474 switch (style & BS_VCENTER)
476 case BS_TOP: /* DT_TOP is 0 */ break;
477 case BS_BOTTOM: dtStyle |= DT_BOTTOM; break;
478 case BS_VCENTER: /* fall through */
479 default: dtStyle |= DT_VCENTER; break;
482 else
483 /* GroupBox's text is always single line and is top aligned. */
484 dtStyle |= DT_SINGLELINE;
486 return dtStyle;
489 /**********************************************************************
490 * BUTTON_CalcLabelRect
492 * Calculates label's rectangle depending on button style.
494 * Returns flags to be passed to DrawText.
495 * Calculated rectangle doesn't take into account button state
496 * (pushed, etc.). If there is nothing to draw (no text/image) output
497 * rectangle is empty, and return value is (UINT)-1.
499 static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc)
501 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
502 WCHAR *text;
503 ICONINFO iconInfo;
504 BITMAP bm;
505 UINT dtStyle = BUTTON_BStoDT(style);
506 RECT r = *rc;
507 INT n;
509 /* Calculate label rectangle according to label type */
510 switch (style & (BS_ICON|BS_BITMAP))
512 case BS_TEXT:
513 if (!(text = get_button_text( hwnd ))) goto empty_rect;
514 if (!text[0])
516 HeapFree( GetProcessHeap(), 0, text );
517 goto empty_rect;
519 DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT);
520 HeapFree( GetProcessHeap(), 0, text );
521 break;
523 case BS_ICON:
524 if (!GetIconInfo((HICON)GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo))
525 goto empty_rect;
527 GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
529 r.right = r.left + bm.bmWidth;
530 r.bottom = r.top + bm.bmHeight;
532 DeleteObject(iconInfo.hbmColor);
533 DeleteObject(iconInfo.hbmMask);
534 break;
536 case BS_BITMAP:
537 if (!GetObjectW( (HANDLE)GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm))
538 goto empty_rect;
540 r.right = r.left + bm.bmWidth;
541 r.bottom = r.top + bm.bmHeight;
542 break;
544 default:
545 empty_rect:
546 r.right = r.left;
547 r.bottom = r.top;
548 return (UINT)(LONG)-1;
551 /* Position label inside bounding rectangle according to
552 * alignment flags. (calculated rect is always left-top aligned).
553 * If label is aligned to any side - shift label in opposite
554 * direction to leave extra space for focus rectangle.
556 switch (dtStyle & (DT_CENTER|DT_RIGHT))
558 case DT_LEFT: r.left++; r.right++; break;
559 case DT_CENTER: n = r.right - r.left;
560 r.left = rc->left + ((rc->right - rc->left) - n) / 2;
561 r.right = r.left + n; break;
562 case DT_RIGHT: n = r.right - r.left;
563 r.right = rc->right - 1;
564 r.left = r.right - n;
565 break;
568 switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
570 case DT_TOP: r.top++; r.bottom++; break;
571 case DT_VCENTER: n = r.bottom - r.top;
572 r.top = rc->top + ((rc->bottom - rc->top) - n) / 2;
573 r.bottom = r.top + n; break;
574 case DT_BOTTOM: n = r.bottom - r.top;
575 r.bottom = rc->bottom - 1;
576 r.top = r.bottom - n;
577 break;
580 *rc = r;
581 return dtStyle;
585 /**********************************************************************
586 * BUTTON_DrawTextCallback
588 * Callback function used by DrawStateW function.
590 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
592 RECT rc;
593 rc.left = 0;
594 rc.top = 0;
595 rc.right = cx;
596 rc.bottom = cy;
598 DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
599 return TRUE;
603 /**********************************************************************
604 * BUTTON_DrawLabel
606 * Common function for drawing button label.
608 static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc)
610 DRAWSTATEPROC lpOutputProc = NULL;
611 LPARAM lp;
612 WPARAM wp = 0;
613 HBRUSH hbr = 0;
614 UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED;
615 LONG state = get_button_state( hwnd );
616 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
617 WCHAR *text = NULL;
619 /* Fixme: To draw disabled label in Win31 look-and-feel, we probably
620 * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
621 * I don't have Win31 on hand to verify that, so I leave it as is.
624 if ((style & BS_PUSHLIKE) && (state & BUTTON_3STATE))
626 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
627 flags |= DSS_MONO;
630 switch (style & (BS_ICON|BS_BITMAP))
632 case BS_TEXT:
633 /* DST_COMPLEX -- is 0 */
634 lpOutputProc = BUTTON_DrawTextCallback;
635 if (!(text = get_button_text( hwnd ))) return;
636 lp = (LPARAM)text;
637 wp = (WPARAM)dtFlags;
638 break;
640 case BS_ICON:
641 flags |= DST_ICON;
642 lp = GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET );
643 break;
645 case BS_BITMAP:
646 flags |= DST_BITMAP;
647 lp = GetWindowLongA( hwnd, HIMAGE_GWL_OFFSET );
648 break;
650 default:
651 return;
654 DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
655 rc->right - rc->left, rc->bottom - rc->top, flags);
656 if (text) HeapFree( GetProcessHeap(), 0, text );
659 /**********************************************************************
660 * Push Button Functions
662 static void PB_Paint( HWND hwnd, HDC hDC, UINT action )
664 RECT rc, focus_rect, r;
665 UINT dtFlags;
666 HRGN hRgn;
667 HPEN hOldPen;
668 HBRUSH hOldBrush;
669 INT oldBkMode;
670 COLORREF oldTxtColor;
671 HFONT hFont;
672 LONG state = get_button_state( hwnd );
673 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
674 BOOL pushedState = (state & BUTTON_HIGHLIGHTED);
676 GetClientRect( hwnd, &rc );
678 /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
679 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
680 SendMessageW( GetParent(hwnd), WM_CTLCOLORBTN, hDC, hwnd );
681 hOldPen = (HPEN)SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
682 hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
683 oldBkMode = SetBkMode(hDC, TRANSPARENT);
685 if ( TWEAK_WineLook == WIN31_LOOK)
687 COLORREF clr_wnd = GetSysColor(COLOR_WINDOW);
688 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
690 SetPixel( hDC, rc.left, rc.top, clr_wnd);
691 SetPixel( hDC, rc.left, rc.bottom-1, clr_wnd);
692 SetPixel( hDC, rc.right-1, rc.top, clr_wnd);
693 SetPixel( hDC, rc.right-1, rc.bottom-1, clr_wnd);
694 InflateRect( &rc, -1, -1 );
697 if (get_button_type(style) == BS_DEFPUSHBUTTON)
699 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
700 InflateRect( &rc, -1, -1 );
703 if (TWEAK_WineLook == WIN31_LOOK)
705 if (pushedState)
707 /* draw button shadow: */
708 SelectObject(hDC, GetSysColorBrush(COLOR_BTNSHADOW));
709 PatBlt(hDC, rc.left, rc.top, 1, rc.bottom-rc.top, PATCOPY );
710 PatBlt(hDC, rc.left, rc.top, rc.right-rc.left, 1, PATCOPY );
711 } else {
712 rc.right++, rc.bottom++;
713 DrawEdge( hDC, &rc, EDGE_RAISED, BF_RECT );
714 rc.right--, rc.bottom--;
717 else
719 UINT uState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
721 if (style & BS_FLAT)
722 uState |= DFCS_MONO;
723 else if (pushedState)
725 if (get_button_type(style) == BS_DEFPUSHBUTTON )
726 uState |= DFCS_FLAT;
727 else
728 uState |= DFCS_PUSHED;
731 if (state & (BUTTON_CHECKED | BUTTON_3STATE))
732 uState |= DFCS_CHECKED;
734 DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
736 focus_rect = rc;
739 /* draw button label */
740 r = rc;
741 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r);
743 if (dtFlags == (UINT)-1L)
744 goto cleanup;
746 if (pushedState)
747 OffsetRect(&r, 1, 1);
749 if(TWEAK_WineLook == WIN31_LOOK)
751 focus_rect = r;
752 InflateRect(&focus_rect, 2, 0);
755 hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
756 SelectClipRgn(hDC, hRgn);
758 oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
760 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r);
762 SetTextColor( hDC, oldTxtColor );
763 SelectClipRgn(hDC, 0);
764 DeleteObject(hRgn);
766 if (state & BUTTON_HASFOCUS)
768 InflateRect( &focus_rect, -1, -1 );
769 IntersectRect(&focus_rect, &focus_rect, &rc);
770 DrawFocusRect( hDC, &focus_rect );
773 cleanup:
774 SelectObject( hDC, hOldPen );
775 SelectObject( hDC, hOldBrush );
776 SetBkMode(hDC, oldBkMode);
779 /**********************************************************************
780 * Check Box & Radio Button Functions
783 static void CB_Paint( HWND hwnd, HDC hDC, UINT action )
785 RECT rbox, rtext, client;
786 HBRUSH hBrush;
787 int delta;
788 UINT dtFlags;
789 HRGN hRgn;
790 HFONT hFont;
791 LONG state = get_button_state( hwnd );
792 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
794 if (style & BS_PUSHLIKE)
796 PB_Paint( hwnd, hDC, action );
797 return;
800 GetClientRect(hwnd, &client);
801 rbox = rtext = client;
803 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
805 /* GetControlBrush16 sends WM_CTLCOLORBTN, plus it returns default brush
806 * if parent didn't return valid one. So we kill two hares at once
808 hBrush = GetControlBrush16( hwnd, hDC, CTLCOLOR_BTN );
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 = GetControlBrush16( hwnd, hDC, CTLCOLOR_STATIC );
956 GetClientRect( hwnd, &rc);
957 if (TWEAK_WineLook == WIN31_LOOK) {
958 HPEN hPrevPen = SelectObject( hDC,
959 GetSysColorPen(COLOR_WINDOWFRAME));
960 HBRUSH hPrevBrush = SelectObject( hDC,
961 GetStockObject(NULL_BRUSH) );
963 Rectangle( hDC, rc.left, rc.top + 2, rc.right - 1, rc.bottom - 1 );
964 SelectObject( hDC, hPrevBrush );
965 SelectObject( hDC, hPrevPen );
966 } else {
967 TEXTMETRICW tm;
968 rcFrame = rc;
970 GetTextMetricsW (hDC, &tm);
971 rcFrame.top += (tm.tmHeight / 2) - 1;
972 DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0));
975 InflateRect(&rc, -7, 1);
976 dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc);
978 if (dtFlags == (UINT)-1L)
979 return;
981 /* Because buttons have CS_PARENTDC class style, there is a chance
982 * that label will be drawn out of client rect.
983 * But Windows doesn't clip label's rect, so do I.
986 /* There is 1-pixel marging at the left, right, and bottom */
987 rc.left--; rc.right++; rc.bottom++;
988 FillRect(hDC, &rc, hbr);
989 rc.left++; rc.right--; rc.bottom--;
991 BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc);
995 /**********************************************************************
996 * User Button Functions
999 static void UB_Paint( HWND hwnd, HDC hDC, UINT action )
1001 RECT rc;
1002 HBRUSH hBrush;
1003 HFONT hFont;
1004 LONG state = get_button_state( hwnd );
1006 if (action == ODA_SELECT) return;
1008 GetClientRect( hwnd, &rc);
1010 if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont );
1011 hBrush = GetControlBrush16( hwnd, hDC, CTLCOLOR_BTN );
1013 FillRect( hDC, &rc, hBrush );
1014 if ((action == ODA_FOCUS) ||
1015 ((action == ODA_DRAWENTIRE) && (state & BUTTON_HASFOCUS)))
1016 DrawFocusRect( hDC, &rc );
1020 /**********************************************************************
1021 * Ownerdrawn Button Functions
1024 static void OB_Paint( HWND hwnd, HDC hDC, UINT action )
1026 LONG state = get_button_state( hwnd );
1027 DRAWITEMSTRUCT dis;
1028 HRGN clipRegion;
1029 RECT clipRect;
1030 UINT id = GetWindowLongA( hwnd, GWL_ID );
1032 dis.CtlType = ODT_BUTTON;
1033 dis.CtlID = id;
1034 dis.itemID = 0;
1035 dis.itemAction = action;
1036 dis.itemState = ((state & BUTTON_HASFOCUS) ? ODS_FOCUS : 0) |
1037 ((state & BUTTON_HIGHLIGHTED) ? ODS_SELECTED : 0) |
1038 (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED);
1039 dis.hwndItem = hwnd;
1040 dis.hDC = hDC;
1041 dis.itemData = 0;
1042 GetClientRect( hwnd, &dis.rcItem );
1044 clipRegion = CreateRectRgnIndirect(&dis.rcItem);
1045 if (GetClipRgn(hDC, clipRegion) != 1)
1047 DeleteObject(clipRegion);
1048 clipRegion=(HRGN)NULL;
1050 clipRect = dis.rcItem;
1051 DPtoLP(hDC, (LPPOINT) &clipRect, 2);
1052 IntersectClipRect(hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
1054 SetBkColor( hDC, GetSysColor( COLOR_BTNFACE ) );
1055 SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
1056 SelectClipRgn(hDC, clipRegion);