Remove previously disabled code.
[wine] / controls / button.c
1 /* File: button.c -- Button type widgets
2  *
3  * Copyright (C) 1993 Johannes Ruscheinski
4  * Copyright (C) 1993 David Metcalfe
5  * Copyright (C) 1994 Alexandre Julliard
6  */
7
8 #include <string.h>
9 #include <stdlib.h>     /* for abs() */
10 #include "win.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"
17
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 :-)
21  */
22 typedef struct
23 {
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 */
27 } BUTTONINFO;
28
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
40
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 );
50
51 #define MAX_BTN_TYPE  12
52
53 static const WORD maxCheckState[MAX_BTN_TYPE] =
54 {
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 */
67 };
68
69 typedef void (*pfPaint)( WND *wndPtr, HDC hdc, WORD action );
70
71 static const pfPaint btnPaintFunc[MAX_BTN_TYPE] =
72 {
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 */
85 };
86
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 ); }
92
93 #define BUTTON_SEND_CTLCOLOR(wndPtr,hdc) \
94     SendMessageW( GetParent((wndPtr)->hwndSelf), WM_CTLCOLORBTN, \
95                     (hdc), (wndPtr)->hwndSelf )
96
97 static HBITMAP hbitmapCheckBoxes = 0;
98 static WORD checkBoxWidth = 0, checkBoxHeight = 0;
99
100
101 /*********************************************************************
102  * button class descriptor
103  */
104 const struct builtin_class_descr BUTTON_builtin_class =
105 {
106     "Button",            /* name */
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 */
112     0                    /* brush */
113 };
114
115
116 /***********************************************************************
117  *           ButtonWndProc_locked
118  * 
119  * Called with window lock held.
120  */
121 static inline LRESULT WINAPI ButtonWndProc_locked(WND* wndPtr, UINT uMsg,
122                                                   WPARAM wParam, LPARAM lParam, BOOL unicode )
123 {
124     RECT rect;
125     HWND        hWnd = wndPtr->hwndSelf;
126     POINT pt;
127     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
128     LONG style = wndPtr->dwStyle & 0x0f;
129     HANDLE oldHbitmap;
130
131     pt.x = LOWORD(lParam);
132     pt.y = HIWORD(lParam);
133
134     switch (uMsg)
135     {
136     case WM_GETDLGCODE:
137         switch(style)
138         {
139         case BS_PUSHBUTTON:      return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
140         case BS_DEFPUSHBUTTON:   return DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
141         case BS_RADIOBUTTON:
142         case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON;
143         default:                 return DLGC_BUTTON;
144         }
145
146     case WM_ENABLE:
147         PAINT_BUTTON( wndPtr, style, ODA_DRAWENTIRE );
148         break;
149
150     case WM_CREATE:
151         if (!hbitmapCheckBoxes)
152         {
153             BITMAP bmp;
154             hbitmapCheckBoxes = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CHECKBOXES));
155             GetObjectW( hbitmapCheckBoxes, sizeof(bmp), &bmp );
156             checkBoxWidth  = bmp.bmWidth / 4;
157             checkBoxHeight = bmp.bmHeight / 3;
158         }
159         if (style < 0L || style >= MAX_BTN_TYPE)
160             return -1; /* abort */
161         infoPtr->state = BUTTON_UNCHECKED;
162         infoPtr->hFont = 0;
163         infoPtr->hImage = 0;
164         return 0;
165
166     case WM_ERASEBKGND:
167         return 1;
168
169     case WM_PAINT:
170         if (btnPaintFunc[style])
171         {
172             PAINTSTRUCT ps;
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 );
178         }
179         break;
180
181     case WM_KEYDOWN:
182         if (wParam == VK_SPACE)
183         {
184             SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
185             infoPtr->state |= BUTTON_BTNPRESSED;
186         }
187         break;
188         
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);
196             break;
197         }
198         /* fall through */
199     case WM_LBUTTONDOWN:
200         SetCapture( hWnd );
201         SetFocus( hWnd );
202         SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 );
203         infoPtr->state |= BUTTON_BTNPRESSED;
204         break;
205
206     case WM_KEYUP:
207         if (wParam != VK_SPACE)
208             break;
209         /* fall through */
210     case WM_LBUTTONUP:
211         if (!(infoPtr->state & BUTTON_BTNPRESSED)) break;
212         infoPtr->state &= BUTTON_NSTATES;
213         if (!(infoPtr->state & BUTTON_HIGHLIGHTED)) {
214             ReleaseCapture();
215             break;
216         }
217         SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 );
218         ReleaseCapture();
219         GetClientRect( hWnd, &rect );
220         if (uMsg == WM_KEYUP || PtInRect( &rect, pt ))
221         {
222             switch(style)
223             {
224             case BS_AUTOCHECKBOX:
225                 SendMessageW( hWnd, BM_SETCHECK,
226                                 !(infoPtr->state & BUTTON_CHECKED), 0 );
227                 break;
228             case BS_AUTORADIOBUTTON:
229                 SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 );
230                 break;
231             case BS_AUTO3STATE:
232                 SendMessageW( hWnd, BM_SETCHECK,
233                                 (infoPtr->state & BUTTON_3STATE) ? 0 :
234                                 ((infoPtr->state & 3) + 1), 0 );
235                 break;
236             }
237             SendMessageW( GetParent(hWnd), WM_COMMAND,
238                             MAKEWPARAM( wndPtr->wIDmenu, BN_CLICKED ), hWnd);
239         }
240         break;
241
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 );
247         }
248         break;
249
250     case WM_MOUSEMOVE:
251         if (GetCapture() == hWnd)
252         {
253             GetClientRect( hWnd, &rect );
254             SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 );
255         }
256         break;
257
258     case WM_SETTEXT:
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 */
264
265     case WM_SETFONT:
266         infoPtr->hFont = (HFONT16)wParam;
267         if (lParam && (wndPtr->dwStyle & WS_VISIBLE)) 
268             PAINT_BUTTON( wndPtr, style, ODA_DRAWENTIRE );
269         break;
270
271     case WM_GETFONT:
272         return infoPtr->hFont;
273
274     case WM_SETFOCUS:
275         if ((style == BS_RADIOBUTTON || style == BS_AUTORADIOBUTTON) && (GetCapture() != hWnd) &&
276             !(SendMessageW(hWnd, BM_GETCHECK, 0, 0) & BST_CHECKED))
277         {
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);
284         }
285         infoPtr->state |= BUTTON_HASFOCUS;
286         PAINT_BUTTON( wndPtr, style, ODA_FOCUS );
287         break;
288
289     case WM_KILLFOCUS:
290         infoPtr->state &= ~BUTTON_HASFOCUS;
291         PAINT_BUTTON( wndPtr, style, ODA_FOCUS );
292         InvalidateRect( hWnd, NULL, TRUE );
293         break;
294
295     case WM_SYSCOLORCHANGE:
296         InvalidateRect( hWnd, NULL, FALSE );
297         break;
298
299     case BM_SETSTYLE16:
300     case BM_SETSTYLE:
301         if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
302         wndPtr->dwStyle = (wndPtr->dwStyle & 0xfffffff0) 
303                            | (wParam & 0x0000000f);
304         style = wndPtr->dwStyle & 0x0000000f;
305
306         /* Only redraw if lParam flag is set.*/
307         if (lParam)
308            PAINT_BUTTON( wndPtr, style, ODA_DRAWENTIRE );
309
310         break;
311
312     case BM_CLICK:
313         SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 );
314         SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 );
315         break;
316
317     case BM_SETIMAGE:
318         /* Check that image format confirm button style */
319         if ((wndPtr->dwStyle & (BS_BITMAP|BS_ICON)) == BS_BITMAP)
320         {
321             if (wParam != (WPARAM) IMAGE_BITMAP)
322                 return (HICON)0;
323         }
324         else if ((wndPtr->dwStyle & (BS_BITMAP|BS_ICON)) == BS_ICON)
325         {
326             if (wParam != (WPARAM) IMAGE_ICON)
327                 return (HICON)0;
328         } else
329             return (HICON)0;
330
331         oldHbitmap = infoPtr->hImage;
332         infoPtr->hImage = (HANDLE) lParam;
333         InvalidateRect( hWnd, NULL, FALSE );
334         return oldHbitmap;
335
336     case BM_GETIMAGE:
337         if ((wndPtr->dwStyle & (BS_BITMAP|BS_ICON)) == BS_BITMAP)
338         {
339             if (wParam != (WPARAM) IMAGE_BITMAP)
340                 return (HICON)0;
341         }
342         else if ((wndPtr->dwStyle & (BS_BITMAP|BS_ICON)) == BS_ICON)
343         {
344             if (wParam != (WPARAM) IMAGE_ICON)
345                 return (HICON)0;
346         } else
347             return (HICON)0;
348         return infoPtr->hImage;
349
350     case BM_GETCHECK16:
351     case BM_GETCHECK:
352         return infoPtr->state & 3;
353
354     case BM_SETCHECK16:
355     case BM_SETCHECK:
356         if (wParam > maxCheckState[style]) wParam = maxCheckState[style];
357         if ((infoPtr->state & 3) != wParam)
358         {
359             if ((style == BS_RADIOBUTTON) || (style == BS_AUTORADIOBUTTON))
360             {
361                 if (wParam)
362                     wndPtr->dwStyle |= WS_TABSTOP;
363                 else
364                     wndPtr->dwStyle &= ~WS_TABSTOP;
365             }
366             infoPtr->state = (infoPtr->state & ~3) | wParam;
367             PAINT_BUTTON( wndPtr, style, ODA_SELECT );
368         }
369         if ((style == BS_AUTORADIOBUTTON) && (wParam == BUTTON_CHECKED))
370             BUTTON_CheckAutoRadioButton( wndPtr );
371         break;
372
373     case BM_GETSTATE16:
374     case BM_GETSTATE:
375         return infoPtr->state;
376
377     case BM_SETSTATE16:
378     case BM_SETSTATE:
379         if (wParam)
380         {
381             if (infoPtr->state & BUTTON_HIGHLIGHTED) break;
382             infoPtr->state |= BUTTON_HIGHLIGHTED;
383         }
384         else
385         {
386             if (!(infoPtr->state & BUTTON_HIGHLIGHTED)) break;
387             infoPtr->state &= ~BUTTON_HIGHLIGHTED;
388         }
389         PAINT_BUTTON( wndPtr, style, ODA_SELECT );
390         break;
391
392     case WM_NCHITTEST:
393         if(style == BS_GROUPBOX) return HTTRANSPARENT;
394         /* fall through */
395     default:
396         return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) :
397                          DefWindowProcA(hWnd, uMsg, wParam, lParam);
398     }
399     return 0;
400 }
401
402 /***********************************************************************
403  *           ButtonWndProcW
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).
407  */
408 static LRESULT WINAPI ButtonWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
409 {
410     LRESULT res = 0;
411     WND *wndPtr = WIN_FindWndPtr(hWnd);
412
413     if (wndPtr)
414     {
415         res = ButtonWndProc_locked(wndPtr,uMsg,wParam,lParam,TRUE);
416         WIN_ReleaseWndPtr(wndPtr);
417     }
418     return res;
419 }
420
421
422 /***********************************************************************
423  *           ButtonWndProcA
424  */
425 static LRESULT WINAPI ButtonWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
426 {
427     LRESULT res = 0;
428     WND *wndPtr = WIN_FindWndPtr(hWnd);
429
430     if (wndPtr)
431     {
432         res = ButtonWndProc_locked(wndPtr,uMsg,wParam,lParam,FALSE);
433         WIN_ReleaseWndPtr(wndPtr);
434     }
435     return res;
436 }
437
438
439 /**********************************************************************
440  *       Push Button Functions
441  */
442 static void PB_Paint( WND *wndPtr, HDC hDC, WORD action )
443 {
444     BUTTONINFO *infoPtr      = (BUTTONINFO *)wndPtr->wExtra;
445     BOOL        bHighLighted = (infoPtr->state & BUTTON_HIGHLIGHTED);
446
447     /* 
448      * Delegate this to the more generic pushbutton painting
449      * method.
450      */
451     BUTTON_DrawPushButton(wndPtr,
452                           hDC,
453                           action,
454                           bHighLighted);
455 }
456
457 /**********************************************************************
458  * Convert button styles to flags used by DrawText.
459  * TODO: handle WS_EX_RIGHT extended style.
460  */
461 static UINT BUTTON_BStoDT(DWORD style)
462 {
463    UINT dtStyle = DT_NOCLIP;  /* We use SelectClipRgn to limit output */
464
465    /* "Convert" pushlike buttons to pushbuttons */
466    if (style & BS_PUSHLIKE)
467       style &= ~0x0F;
468
469    if (!(style & BS_MULTILINE))
470       dtStyle |= DT_SINGLELINE;
471    else
472       dtStyle |= DT_WORDBREAK;
473
474    switch (style & BS_CENTER)
475    {
476       case BS_LEFT:   /* DT_LEFT is 0 */    break;
477       case BS_RIGHT:  dtStyle |= DT_RIGHT;  break;
478       case BS_CENTER: dtStyle |= DT_CENTER; break;
479       default:
480          /* Pushbutton's text is centered by default */
481          if ((style & 0x0F) <= BS_DEFPUSHBUTTON)
482             dtStyle |= DT_CENTER;
483          /* all other flavours have left aligned text */
484    }
485
486    /* DrawText ignores vertical alignment for multiline text,
487     * but we use these flags to align label manualy.
488     */
489    if ((style & 0x0F) != BS_GROUPBOX)
490    {
491       switch (style & BS_VCENTER)
492       {
493          case BS_TOP:     /* DT_TOP is 0 */      break;
494          case BS_BOTTOM:  dtStyle |= DT_BOTTOM;  break;
495          case BS_VCENTER: /* fall through */
496          default:         dtStyle |= DT_VCENTER; break;
497       }
498    }
499    else
500       /* GroupBox's text is always single line and is top aligned. */
501       dtStyle |= DT_SINGLELINE;
502
503    return dtStyle;
504 }
505
506 /**********************************************************************
507  *       BUTTON_CalcLabelRect
508  *
509  *   Calculates label's rectangle depending on button style.
510  *
511  * Returns flags to be passed to DrawText.
512  * Calculated rectangle doesn't take into account button state
513  * (pushed, etc.). If there is nothing to draw (no text/image) output
514  * rectangle is empty, and return value is (UINT)-1.
515  */
516 static UINT BUTTON_CalcLabelRect(WND *wndPtr, HDC hdc, RECT *rc)
517 {
518    BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
519    ICONINFO    iconInfo;
520    BITMAP      bm;
521    UINT        dtStyle = BUTTON_BStoDT(wndPtr->dwStyle);
522    RECT        r = *rc;
523    INT         n;
524
525    /* Calculate label rectangle according to label type */
526    switch (wndPtr->dwStyle & (BS_ICON|BS_BITMAP))
527    {
528       case BS_TEXT:
529          if (wndPtr->text && wndPtr->text[0])
530             DrawTextW(hdc, wndPtr->text, -1, &r, dtStyle | DT_CALCRECT);
531          else
532             goto empty_rect;
533          break;
534
535       case BS_ICON:
536          if (!GetIconInfo((HICON)infoPtr->hImage, &iconInfo))
537             goto empty_rect;
538
539          GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm);
540
541          r.right  = r.left + bm.bmWidth;
542          r.bottom = r.top  + bm.bmHeight;
543
544          DeleteObject(iconInfo.hbmColor);
545          DeleteObject(iconInfo.hbmMask);
546          break;
547
548       case BS_BITMAP:
549          if (!GetObjectW (infoPtr->hImage, sizeof(BITMAP), &bm))
550             goto empty_rect;
551
552          r.right  = r.left + bm.bmWidth;
553          r.bottom = r.top  + bm.bmHeight;
554          break;
555
556       default:
557       empty_rect:   
558          r.right = r.left;
559          r.bottom = r.top;
560          return (UINT)(LONG)-1;
561    }
562
563    /* Position label inside bounding rectangle according to
564     * alignment flags. (calculated rect is always left-top aligned).
565     * If label is aligned to any side - shift label in opposite
566     * direction to leave extra space for focus rectangle.
567     */
568    switch (dtStyle & (DT_CENTER|DT_RIGHT))
569    {
570       case DT_LEFT:    r.left++;  r.right++;  break;
571       case DT_CENTER:  n = r.right - r.left;
572                        r.left   = rc->left + ((rc->right - rc->left) - n) / 2;
573                        r.right  = r.left + n; break;
574       case DT_RIGHT:   n = r.right - r.left;
575                        r.right  = rc->right - 1;
576                        r.left   = r.right - n;
577                        break;
578    }
579
580    switch (dtStyle & (DT_VCENTER|DT_BOTTOM))
581    {
582       case DT_TOP:     r.top++;  r.bottom++;  break;
583       case DT_VCENTER: n = r.bottom - r.top;
584                        r.top    = rc->top + ((rc->bottom - rc->top) - n) / 2;
585                        r.bottom = r.top + n;  break;
586       case DT_BOTTOM:  n = r.bottom - r.top;
587                        r.bottom = rc->bottom - 1;
588                        r.top    = r.bottom - n;
589                        break;
590    }
591
592    *rc = r;
593    return dtStyle;
594 }
595
596
597 /**********************************************************************
598  *       BUTTON_DrawTextCallback
599  *
600  *   Callback function used by DrawStateW function.
601  */
602 static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int cx, int cy)
603 {
604    RECT rc;
605    rc.left = 0;
606    rc.top = 0;
607    rc.right = cx;
608    rc.bottom = cy;
609
610    DrawTextW(hdc, (LPCWSTR)lp, -1, &rc, (UINT)wp);
611    return TRUE;
612 }
613
614
615 /**********************************************************************
616  *       BUTTON_DrawLabel
617  *
618  *   Common function for drawing button label.
619  */
620 static void BUTTON_DrawLabel(WND *wndPtr, HDC hdc, UINT dtFlags, RECT *rc)
621 {
622    BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
623    DRAWSTATEPROC lpOutputProc = NULL;
624    LPARAM lp;
625    WPARAM wp = 0;
626    HBRUSH hbr = 0;
627    UINT flags = IsWindowEnabled(wndPtr->hwndSelf) ? DSS_NORMAL : DSS_DISABLED;
628
629    /* Fixme: To draw disabled label in Win31 look-and-feel, we probably
630     * must use DSS_MONO flag and COLOR_GRAYTEXT brush (or maybe DSS_UNION).
631     * I don't have Win31 on hand to verify that, so I leave it as is.
632     */
633
634    if ((wndPtr->dwStyle & BS_PUSHLIKE) && (infoPtr->state & BUTTON_3STATE))
635    {
636       hbr = GetSysColorBrush(COLOR_GRAYTEXT);
637       flags |= DSS_MONO;
638    }
639
640    switch (wndPtr->dwStyle & (BS_ICON|BS_BITMAP))
641    {
642       case BS_TEXT:
643          /* DST_COMPLEX -- is 0 */
644          lpOutputProc = BUTTON_DrawTextCallback;
645          lp = (LPARAM)wndPtr->text;
646          wp = (WPARAM)dtFlags;
647          break;
648
649       case BS_ICON:
650          flags |= DST_ICON;
651          lp = (LPARAM)infoPtr->hImage;
652          break;
653
654       case BS_BITMAP:
655          flags |= DST_BITMAP;
656          lp = (LPARAM)infoPtr->hImage;
657          break;
658
659       default:
660          return;
661    }
662
663    DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top,
664               rc->right - rc->left, rc->bottom - rc->top, flags);
665 }
666
667 /**********************************************************************
668  * This method will actually do the drawing of the pushbutton 
669  * depending on it's state and the pushedState parameter.
670  */
671 static void BUTTON_DrawPushButton(
672   WND* wndPtr,
673   HDC  hDC, 
674   WORD action, 
675   BOOL pushedState )
676 {
677     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
678     RECT     rc, focus_rect, r;
679     UINT     dtFlags;
680     HRGN     hRgn;
681     HPEN     hOldPen;
682     HBRUSH   hOldBrush;
683     INT      oldBkMode;
684     COLORREF oldTxtColor;
685
686     GetClientRect( wndPtr->hwndSelf, &rc );
687
688     /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
689     if (infoPtr->hFont) SelectObject( hDC, infoPtr->hFont );
690     BUTTON_SEND_CTLCOLOR( wndPtr, hDC );
691     hOldPen = (HPEN)SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
692     hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
693     oldBkMode = SetBkMode(hDC, TRANSPARENT);
694
695     if ( TWEAK_WineLook == WIN31_LOOK)
696     {
697         COLORREF clr_wnd = GetSysColor(COLOR_WINDOW);
698         Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
699
700         SetPixel( hDC, rc.left, rc.top, clr_wnd);
701         SetPixel( hDC, rc.left, rc.bottom-1, clr_wnd);
702         SetPixel( hDC, rc.right-1, rc.top, clr_wnd);
703         SetPixel( hDC, rc.right-1, rc.bottom-1, clr_wnd);
704         InflateRect( &rc, -1, -1 );
705     }
706     
707     if ((wndPtr->dwStyle & 0x000f) == BS_DEFPUSHBUTTON)
708     {
709         Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
710         InflateRect( &rc, -1, -1 );
711     }
712
713     if (TWEAK_WineLook == WIN31_LOOK)
714     {
715         if (pushedState)
716         {
717             /* draw button shadow: */
718             SelectObject(hDC, GetSysColorBrush(COLOR_BTNSHADOW));
719             PatBlt(hDC, rc.left, rc.top, 1, rc.bottom-rc.top, PATCOPY );
720             PatBlt(hDC, rc.left, rc.top, rc.right-rc.left, 1, PATCOPY );
721         } else {
722            rc.right++, rc.bottom++;
723            DrawEdge( hDC, &rc, EDGE_RAISED, BF_RECT );
724            rc.right--, rc.bottom--;
725         }
726     }
727     else
728     {
729         UINT uState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
730
731         if (wndPtr->dwStyle & BS_FLAT)
732             uState |= DFCS_MONO;
733         else if (pushedState)
734         {
735             if ( (wndPtr->dwStyle & 0x000f) == BS_DEFPUSHBUTTON )
736                 uState |= DFCS_FLAT;
737             else
738                 uState |= DFCS_PUSHED;
739         }
740
741         if (infoPtr->state & (BUTTON_CHECKED | BUTTON_3STATE))
742             uState |= DFCS_CHECKED;
743
744         DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
745
746         focus_rect = rc;
747     }
748
749     /* draw button label */
750     r = rc;
751     dtFlags = BUTTON_CalcLabelRect(wndPtr, hDC, &r);
752
753     if (dtFlags == (UINT)-1L)
754        goto cleanup;
755
756     if (pushedState)
757        OffsetRect(&r, 1, 1);
758
759     if(TWEAK_WineLook == WIN31_LOOK)
760     {
761        focus_rect = r;
762        InflateRect(&focus_rect, 2, 0);
763     }
764
765     hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
766     SelectClipRgn(hDC, hRgn);
767
768     oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
769
770     BUTTON_DrawLabel(wndPtr, hDC, dtFlags, &r);
771
772     SetTextColor( hDC, oldTxtColor );
773     SelectClipRgn(hDC, 0);
774     DeleteObject(hRgn);
775
776     if (infoPtr->state & BUTTON_HASFOCUS)
777     {
778         InflateRect( &focus_rect, -1, -1 );
779         IntersectRect(&focus_rect, &focus_rect, &rc);
780         DrawFocusRect( hDC, &focus_rect );
781     }
782
783  cleanup:
784     SelectObject( hDC, hOldPen );
785     SelectObject( hDC, hOldBrush );
786     SetBkMode(hDC, oldBkMode);
787 }
788
789 /**********************************************************************
790  *       Check Box & Radio Button Functions
791  */
792
793 static void CB_Paint( WND *wndPtr, HDC hDC, WORD action )
794 {
795     RECT rbox, rtext, client;
796     HBRUSH hBrush;
797     int delta;
798     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
799     UINT dtFlags;
800     HRGN hRgn;
801
802     if (wndPtr->dwStyle & BS_PUSHLIKE)
803     {
804         BOOL bHighLighted = (infoPtr->state & BUTTON_HIGHLIGHTED);
805
806         BUTTON_DrawPushButton(wndPtr,
807                               hDC,
808                               action,
809                               bHighLighted);
810         return;
811     }
812
813     GetClientRect(wndPtr->hwndSelf, &client);
814     rbox = rtext = client;
815
816     if (infoPtr->hFont) SelectObject( hDC, infoPtr->hFont );
817
818     /* GetControlBrush16 sends WM_CTLCOLORBTN, plus it returns default brush
819      * if parent didn't return valid one. So we kill two hares at once
820      */
821     hBrush = GetControlBrush16( wndPtr->hwndSelf, hDC, CTLCOLOR_BTN );
822
823     if (wndPtr->dwStyle & BS_LEFTTEXT) 
824     {
825         /* magic +4 is what CTL3D expects */
826
827         rtext.right -= checkBoxWidth + 4;
828         rbox.left = rbox.right - checkBoxWidth;
829     }
830     else 
831     {
832         rtext.left += checkBoxWidth + 4;
833         rbox.right = checkBoxWidth;
834     }
835
836     /* Draw the check-box bitmap */
837     if (action == ODA_DRAWENTIRE || action == ODA_SELECT)
838     { 
839         /* Since WM_ERASEBKGND does nothing, first prepare background */
840         if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush );
841         else FillRect( hDC, &client, hBrush );
842
843         if( TWEAK_WineLook == WIN31_LOOK )
844         {
845         HDC hMemDC = CreateCompatibleDC( hDC );
846         int x = 0, y = 0;
847         delta = (rbox.bottom - rbox.top - checkBoxHeight) / 2;
848
849         /* Check in case the client area is smaller than the checkbox bitmap */
850         if (delta < 0) delta = 0;
851
852         if (infoPtr->state & BUTTON_HIGHLIGHTED) x += 2 * checkBoxWidth;
853         if (infoPtr->state & (BUTTON_CHECKED | BUTTON_3STATE)) x += checkBoxWidth;
854         if (((wndPtr->dwStyle & 0x0f) == BS_RADIOBUTTON) ||
855             ((wndPtr->dwStyle & 0x0f) == BS_AUTORADIOBUTTON)) y += checkBoxHeight;
856         else if (infoPtr->state & BUTTON_3STATE) y += 2 * checkBoxHeight;
857
858         /* The bitmap for the radio button is not aligned with the
859          * left of the window, it is 1 pixel off. */
860         if (((wndPtr->dwStyle & 0x0f) == BS_RADIOBUTTON) ||
861             ((wndPtr->dwStyle & 0x0f) == BS_AUTORADIOBUTTON))
862           rbox.left += 1;
863
864         SelectObject( hMemDC, hbitmapCheckBoxes );
865         BitBlt( hDC, rbox.left, rbox.top + delta, checkBoxWidth,
866                   checkBoxHeight, hMemDC, x, y, SRCCOPY );
867         DeleteDC( hMemDC );
868         }
869         else
870         {
871             UINT state;
872
873             if (((wndPtr->dwStyle & 0x0f) == BS_RADIOBUTTON) ||
874                 ((wndPtr->dwStyle & 0x0f) == BS_AUTORADIOBUTTON)) state = DFCS_BUTTONRADIO;
875             else if (infoPtr->state & BUTTON_3STATE) state = DFCS_BUTTON3STATE;
876             else state = DFCS_BUTTONCHECK;
877
878             if (infoPtr->state & (BUTTON_CHECKED | BUTTON_3STATE)) state |= DFCS_CHECKED;
879             
880             if (infoPtr->state & BUTTON_HIGHLIGHTED) state |= DFCS_PUSHED;
881
882             if (wndPtr->dwStyle & WS_DISABLED) state |= DFCS_INACTIVE;
883
884             /* rbox must have the correct height */ 
885             delta = rbox.bottom - rbox.top - checkBoxHeight;
886             if (delta > 0) 
887             {  
888                 int ofs = (abs(delta) / 2);
889                 rbox.bottom -= ofs + 1;
890                 rbox.top = rbox.bottom - checkBoxHeight;
891             }
892             else if (delta < 0)
893             {
894                 int ofs = (abs(delta) / 2);
895                 rbox.top -= ofs + 1;
896                 rbox.bottom = rbox.top + checkBoxHeight;
897             }
898
899             DrawFrameControl( hDC, &rbox, DFC_BUTTON, state );
900         }
901     }
902
903     /* Draw label */
904     client = rtext;
905     dtFlags = BUTTON_CalcLabelRect(wndPtr, hDC, &rtext);
906
907     if (dtFlags == (UINT)-1L) /* Noting to draw */
908         return;
909     hRgn = CreateRectRgn(client.left, client.top, client.right, client.bottom);
910     SelectClipRgn(hDC, hRgn);
911     DeleteObject(hRgn);
912
913     if (action == ODA_DRAWENTIRE)
914         BUTTON_DrawLabel(wndPtr, hDC, dtFlags, &rtext);
915
916     /* ... and focus */
917     if ((action == ODA_FOCUS) ||
918         ((action == ODA_DRAWENTIRE) && (infoPtr->state & BUTTON_HASFOCUS)))
919     {
920         rtext.left--;
921         rtext.right++;
922         IntersectRect(&rtext, &rtext, &client);
923         DrawFocusRect( hDC, &rtext );
924     }
925     SelectClipRgn(hDC, 0);
926 }
927
928
929 /**********************************************************************
930  *       BUTTON_CheckAutoRadioButton
931  *
932  * wndPtr is checked, uncheck every other auto radio button in group
933  */
934 static void BUTTON_CheckAutoRadioButton( WND *wndPtr )
935 {
936     HWND parent, sibling, start;
937     if (!(wndPtr->dwStyle & WS_CHILD)) return;
938     parent = wndPtr->parent->hwndSelf;
939     /* assure that starting control is not disabled or invisible */
940     start = sibling = GetNextDlgGroupItem( parent, wndPtr->hwndSelf, TRUE );
941     do
942     {
943         WND *tmpWnd;
944         if (!sibling) break;
945         tmpWnd = WIN_FindWndPtr(sibling);
946         if ((wndPtr->hwndSelf != sibling) &&
947             ((tmpWnd->dwStyle & 0x0f) == BS_AUTORADIOBUTTON))
948             SendMessageW( sibling, BM_SETCHECK, BUTTON_UNCHECKED, 0 );
949         sibling = GetNextDlgGroupItem( parent, sibling, FALSE );
950         WIN_ReleaseWndPtr(tmpWnd);
951     } while (sibling != start);
952 }
953
954
955 /**********************************************************************
956  *       Group Box Functions
957  */
958
959 static void GB_Paint( WND *wndPtr, HDC hDC, WORD action )
960 {
961     RECT rc, rcFrame;
962     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
963     HBRUSH hbr;
964     UINT dtFlags;
965
966     if (action != ODA_DRAWENTIRE) return;
967
968     if (infoPtr->hFont)
969         SelectObject (hDC, infoPtr->hFont);
970     /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */
971     hbr = GetControlBrush16( wndPtr->hwndSelf, hDC, CTLCOLOR_STATIC );
972
973
974     GetClientRect( wndPtr->hwndSelf, &rc);
975     if (TWEAK_WineLook == WIN31_LOOK) {
976         HPEN hPrevPen = SelectObject( hDC,
977                                           GetSysColorPen(COLOR_WINDOWFRAME));
978         HBRUSH hPrevBrush = SelectObject( hDC,
979                                               GetStockObject(NULL_BRUSH) );
980
981         Rectangle( hDC, rc.left, rc.top + 2, rc.right - 1, rc.bottom - 1 );
982         SelectObject( hDC, hPrevBrush );
983         SelectObject( hDC, hPrevPen );
984     } else {
985         TEXTMETRICW tm;
986         rcFrame = rc;
987
988         GetTextMetricsW (hDC, &tm);
989         rcFrame.top += (tm.tmHeight / 2) - 1;
990         DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT |
991                            ((wndPtr->dwStyle & BS_FLAT) ? BF_FLAT : 0));
992     }
993
994     InflateRect(&rc, -7, 1);
995     dtFlags = BUTTON_CalcLabelRect(wndPtr, hDC, &rc);
996
997     if (dtFlags == (UINT)-1L)
998        return;
999
1000     /* Because buttons have CS_PARENTDC class style, there is a chance
1001      * that label will be drawn out of client rect.
1002      * But Windows doesn't clip label's rect, so do I.
1003      */
1004
1005     /* There is 1-pixel marging at the left, right, and bottom */
1006     rc.left--; rc.right++; rc.bottom++;
1007     FillRect(hDC, &rc, hbr);
1008     rc.left++; rc.right--; rc.bottom--;
1009
1010     BUTTON_DrawLabel(wndPtr, hDC, dtFlags, &rc);   
1011 }
1012
1013
1014 /**********************************************************************
1015  *       User Button Functions
1016  */
1017
1018 static void UB_Paint( WND *wndPtr, HDC hDC, WORD action )
1019 {
1020     RECT rc;
1021     HBRUSH hBrush;
1022     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
1023
1024     if (action == ODA_SELECT) return;
1025
1026     GetClientRect( wndPtr->hwndSelf, &rc);
1027
1028     if (infoPtr->hFont) SelectObject( hDC, infoPtr->hFont );
1029     hBrush = GetControlBrush16( wndPtr->hwndSelf, hDC, CTLCOLOR_BTN );
1030
1031     FillRect( hDC, &rc, hBrush );
1032     if ((action == ODA_FOCUS) ||
1033         ((action == ODA_DRAWENTIRE) && (infoPtr->state & BUTTON_HASFOCUS)))
1034         DrawFocusRect( hDC, &rc );
1035 }
1036
1037
1038 /**********************************************************************
1039  *       Ownerdrawn Button Functions
1040  */
1041
1042 static void OB_Paint( WND *wndPtr, HDC hDC, WORD action )
1043 {
1044     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
1045     DRAWITEMSTRUCT dis;
1046     HRGN clipRegion;
1047     RECT clipRect;
1048
1049     dis.CtlType    = ODT_BUTTON;
1050     dis.CtlID      = wndPtr->wIDmenu;
1051     dis.itemID     = 0;
1052     dis.itemAction = action;
1053     dis.itemState  = ((infoPtr->state & BUTTON_HASFOCUS) ? ODS_FOCUS : 0) |
1054                      ((infoPtr->state & BUTTON_HIGHLIGHTED) ? ODS_SELECTED : 0) |
1055                      (IsWindowEnabled(wndPtr->hwndSelf) ? 0: ODS_DISABLED);
1056     dis.hwndItem   = wndPtr->hwndSelf;
1057     dis.hDC        = hDC;
1058     dis.itemData   = 0;
1059     GetClientRect( wndPtr->hwndSelf, &dis.rcItem );
1060
1061     clipRegion = CreateRectRgnIndirect(&dis.rcItem);   
1062     if (GetClipRgn(hDC, clipRegion) != 1)
1063     {
1064         DeleteObject(clipRegion);
1065         clipRegion=(HRGN)NULL;
1066     }
1067     clipRect = dis.rcItem;
1068     DPtoLP(hDC, (LPPOINT) &clipRect, 2);    
1069     IntersectClipRect(hDC, clipRect.left,  clipRect.top, clipRect.right, clipRect.bottom);
1070
1071     SetBkColor( hDC, GetSysColor( COLOR_BTNFACE ) );
1072
1073     SendMessageW( GetParent(wndPtr->hwndSelf), WM_DRAWITEM,
1074                     wndPtr->wIDmenu, (LPARAM)&dis );
1075
1076     SelectClipRgn(hDC, clipRegion);             
1077 }
1078