Release 951105
[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 "win.h"
9 #include "user.h"
10 #include "syscolor.h"
11 #include "graphics.h"
12 #include "button.h"
13 #include "stackframe.h"
14
15 extern void DEFWND_SetText( HWND hwnd, LPSTR text );  /* windows/defwnd.c */
16
17 static void PB_Paint( HWND hWnd, HDC hDC, WORD action );
18 static void CB_Paint( HWND hWnd, HDC hDC, WORD action );
19 static void GB_Paint( HWND hWnd, HDC hDC, WORD action );
20 static void UB_Paint( HWND hWnd, HDC hDC, WORD action );
21 static void OB_Paint( HWND hWnd, HDC hDC, WORD action );
22 static void BUTTON_CheckAutoRadioButton(HWND hWnd);
23
24
25 #define MAX_BTN_TYPE  12
26
27 static WORD maxCheckState[MAX_BTN_TYPE] =
28 {
29     BUTTON_UNCHECKED,   /* BS_PUSHBUTTON */
30     BUTTON_UNCHECKED,   /* BS_DEFPUSHBUTTON */
31     BUTTON_CHECKED,     /* BS_CHECKBOX */
32     BUTTON_CHECKED,     /* BS_AUTOCHECKBOX */
33     BUTTON_CHECKED,     /* BS_RADIOBUTTON */
34     BUTTON_3STATE,      /* BS_3STATE */
35     BUTTON_3STATE,      /* BS_AUTO3STATE */
36     BUTTON_UNCHECKED,   /* BS_GROUPBOX */
37     BUTTON_UNCHECKED,   /* BS_USERBUTTON */
38     BUTTON_CHECKED,     /* BS_AUTORADIOBUTTON */
39     BUTTON_UNCHECKED,   /* Not defined */
40     BUTTON_UNCHECKED    /* BS_OWNERDRAW */
41 };
42
43 typedef void (*pfPaint)(HWND,HDC,WORD);
44
45 static pfPaint btnPaintFunc[MAX_BTN_TYPE] =
46 {
47     PB_Paint,    /* BS_PUSHBUTTON */
48     PB_Paint,    /* BS_DEFPUSHBUTTON */
49     CB_Paint,    /* BS_CHECKBOX */
50     CB_Paint,    /* BS_AUTOCHECKBOX */
51     CB_Paint,    /* BS_RADIOBUTTON */
52     CB_Paint,    /* BS_3STATE */
53     CB_Paint,    /* BS_AUTO3STATE */
54     GB_Paint,    /* BS_GROUPBOX */
55     UB_Paint,    /* BS_USERBUTTON */
56     CB_Paint,    /* BS_AUTORADIOBUTTON */
57     NULL,        /* Not defined */
58     OB_Paint     /* BS_OWNERDRAW */
59 };
60
61 #define PAINT_BUTTON(hwnd,style,action) \
62      if (btnPaintFunc[style]) { \
63          HDC hdc = GetDC( hwnd ); \
64          (btnPaintFunc[style])(hwnd,hdc,action); \
65          ReleaseDC( hwnd, hdc ); }
66
67 static HBITMAP hbitmapCheckBoxes = 0;
68 static WORD checkBoxWidth = 0, checkBoxHeight = 0;
69
70
71 LRESULT ButtonWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
72 {
73         RECT rect;
74         POINT pt;
75         LONG lResult = 0;
76         WND *wndPtr = WIN_FindWndPtr(hWnd);
77         LONG style = wndPtr->dwStyle & 0x0000000F;
78         BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
79
80         switch (uMsg) {
81         case WM_GETDLGCODE:
82                 switch(style)
83                 {
84                 case BS_PUSHBUTTON:
85                     return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON;
86                 case BS_DEFPUSHBUTTON:
87                     return DLGC_BUTTON | DLGC_DEFPUSHBUTTON;
88                 case BS_RADIOBUTTON:
89                 case BS_AUTORADIOBUTTON:
90                     return DLGC_BUTTON | DLGC_RADIOBUTTON;
91                 default:
92                     return DLGC_BUTTON;
93                 }
94
95         case WM_ENABLE:
96                 PAINT_BUTTON( hWnd, style, ODA_DRAWENTIRE );
97                 break;
98
99         case WM_CREATE:
100                 if (!hbitmapCheckBoxes)
101                 {
102                     BITMAP bmp;
103                     hbitmapCheckBoxes = LoadBitmap( 0, MAKEINTRESOURCE(OBM_CHECKBOXES) );
104                     GetObject( hbitmapCheckBoxes, sizeof(bmp), (LPSTR)&bmp );
105                     checkBoxWidth  = bmp.bmWidth / 4;
106                     checkBoxHeight = bmp.bmHeight / 3;
107                 }
108                 
109                 if (style < 0L || style >= MAX_BTN_TYPE)
110                     lResult = -1L;
111                 else
112                 {
113                     infoPtr->state = BUTTON_UNCHECKED;
114                     infoPtr->hFont = 0;
115                     lResult = 0L;
116                 }
117                 break;
118
119         case WM_ERASEBKGND:
120                 break;
121
122         case WM_PAINT:
123                 if (btnPaintFunc[style])
124                 {
125                     PAINTSTRUCT ps;
126                     HDC hdc = BeginPaint( hWnd, &ps );
127                     (btnPaintFunc[style])( hWnd, hdc, ODA_DRAWENTIRE );
128                     ReleaseDC( hWnd, hdc );
129                 }
130                 break;
131
132         case WM_LBUTTONDOWN:
133                 SendMessage( hWnd, BM_SETSTATE, TRUE, 0 );
134                 SetFocus( hWnd );
135                 SetCapture( hWnd );
136                 break;
137
138         case WM_LBUTTONUP:
139                 ReleaseCapture();
140                 if (!(infoPtr->state & BUTTON_HIGHLIGHTED)) break;
141                 SendMessage( hWnd, BM_SETSTATE, FALSE, 0 );
142                 GetClientRect( hWnd, &rect );
143                 pt.x = LOWORD(lParam);
144                 pt.y = HIWORD(lParam);
145                 if (PtInRect( &rect, pt ))
146                 {
147                     switch(style)
148                     {
149                     case BS_AUTOCHECKBOX:
150                         SendMessage( hWnd, BM_SETCHECK,
151                                     !(infoPtr->state & BUTTON_CHECKED), 0 );
152                         break;
153                     case BS_AUTORADIOBUTTON:
154                         SendMessage( hWnd, BM_SETCHECK, TRUE, 0 );
155                         break;
156                     case BS_AUTO3STATE:
157                         SendMessage( hWnd, BM_SETCHECK,
158                                      (infoPtr->state & BUTTON_3STATE) ? 0 :
159                                      ((infoPtr->state & 3) + 1), 0 );
160                         break;
161                     }
162 #ifdef WINELIB32
163                     SendMessage( GetParent(hWnd), WM_COMMAND,
164                                  MAKEWPARAM(wndPtr->wIDmenu,BN_CLICKED),
165                                  (LPARAM) hWnd );
166 #else
167                     SendMessage( GetParent(hWnd), WM_COMMAND,
168                                  wndPtr->wIDmenu, MAKELPARAM(hWnd,BN_CLICKED));
169 #endif
170                 }
171                 break;
172
173         case WM_MOUSEMOVE:
174                 if (GetCapture() == hWnd)
175                 {
176                     GetClientRect( hWnd, &rect );
177                     pt.x = LOWORD(lParam);
178                     pt.y = HIWORD(lParam);
179                     if (PtInRect( &rect, pt) )
180                        SendMessage( hWnd, BM_SETSTATE, TRUE, 0 );
181                     else SendMessage( hWnd, BM_SETSTATE, FALSE, 0 );
182                 }
183                 break;
184
185         case WM_NCHITTEST:
186                 if(style == BS_GROUPBOX) return HTTRANSPARENT;
187                 lResult = DefWindowProc(hWnd, uMsg, wParam, lParam);
188                 break;
189
190         case WM_SETTEXT:
191                 DEFWND_SetText( hWnd, (LPSTR)PTR_SEG_TO_LIN(lParam) );
192                 PAINT_BUTTON( hWnd, style, ODA_DRAWENTIRE );
193                 return 0;
194
195         case WM_SETFONT:
196                 infoPtr->hFont = (HFONT) wParam;
197                 if (lParam)
198                     PAINT_BUTTON( hWnd, style, ODA_DRAWENTIRE );
199                 break;
200
201         case WM_GETFONT:
202                 return (LONG) infoPtr->hFont;
203
204         case WM_SETFOCUS:
205                 infoPtr->state |= BUTTON_HASFOCUS;
206                 PAINT_BUTTON( hWnd, style, ODA_FOCUS );
207                 break;
208
209         case WM_KILLFOCUS:
210                 infoPtr->state &= ~BUTTON_HASFOCUS;
211                 PAINT_BUTTON( hWnd, style, ODA_FOCUS );
212                 break;
213
214         case WM_SYSCOLORCHANGE:
215                 InvalidateRect(hWnd, NULL, FALSE);
216                 break;
217
218         case BM_SETSTYLE:
219                 if ((wParam & 0x0f) >= MAX_BTN_TYPE) break;
220                 wndPtr->dwStyle = (wndPtr->dwStyle & 0xfffffff0) 
221                                    | (wParam & 0x0000000f);
222                 style = wndPtr->dwStyle & 0x0000000f;
223                 PAINT_BUTTON( hWnd, style, ODA_DRAWENTIRE );
224                 break;
225
226         case BM_GETCHECK:
227                 lResult = infoPtr->state & 3;
228                 break;
229
230         case BM_SETCHECK:
231                 if (wParam > maxCheckState[style])
232                     wParam = maxCheckState[style];
233                 if ((infoPtr->state & 3) != wParam)
234                 {
235                     infoPtr->state = (infoPtr->state & ~3) | wParam;
236                     PAINT_BUTTON( hWnd, style, ODA_SELECT );
237                 }
238                 if(style == BS_AUTORADIOBUTTON && wParam==BUTTON_CHECKED)
239                         BUTTON_CheckAutoRadioButton(hWnd);
240                 break;
241
242         case BM_GETSTATE:
243                 lResult = infoPtr->state;
244                 break;
245
246         case BM_SETSTATE:
247                 if (!wParam != !(infoPtr->state & BUTTON_HIGHLIGHTED))
248                 {
249                     if (wParam) infoPtr->state |= BUTTON_HIGHLIGHTED;
250                     else infoPtr->state &= ~BUTTON_HIGHLIGHTED;
251                     PAINT_BUTTON( hWnd, style, ODA_SELECT );
252                 }
253                 break;
254
255         default:
256                 lResult = DefWindowProc(hWnd, uMsg, wParam, lParam);
257                 break;
258         }
259
260         return lResult;
261 }
262
263
264 /**********************************************************************
265  *       Push Button Functions
266  */
267
268 static void PB_Paint( HWND hButton, HDC hDC, WORD action )
269 {
270     RECT rc;
271     HPEN hOldPen;
272     HBRUSH hOldBrush;
273     char *text;
274     DWORD dwTextSize;
275     TEXTMETRIC tm;
276     WND *wndPtr = WIN_FindWndPtr( hButton );
277     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
278
279     GetClientRect(hButton, &rc);
280
281       /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */
282     if (infoPtr->hFont) SelectObject( hDC, infoPtr->hFont );
283 #ifdef WINELIB32
284     SendMessage( GetParent(hButton), WM_CTLCOLORBTN,
285                  (WPARAM)hDC, (LPARAM)hButton );
286 #else
287     SendMessage( GetParent(hButton), WM_CTLCOLOR, (WORD)hDC,
288                  MAKELPARAM(hButton, CTLCOLOR_BTN) );
289 #endif
290     hOldPen = (HPEN)SelectObject(hDC, sysColorObjects.hpenWindowFrame);
291     hOldBrush = (HBRUSH)SelectObject(hDC, sysColorObjects.hbrushBtnFace);
292     SetBkMode(hDC, TRANSPARENT);
293     Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
294     if (action == ODA_DRAWENTIRE)
295     {
296         SetPixel( hDC, rc.left, rc.top, GetSysColor(COLOR_WINDOW) );
297         SetPixel( hDC, rc.left, rc.bottom-1, GetSysColor(COLOR_WINDOW) );
298         SetPixel( hDC, rc.right-1, rc.top, GetSysColor(COLOR_WINDOW) );
299         SetPixel( hDC, rc.right-1, rc.bottom-1, GetSysColor(COLOR_WINDOW) );
300     }
301     InflateRect( &rc, -1, -1 );
302
303     if ((wndPtr->dwStyle & 0x000f) == BS_DEFPUSHBUTTON)
304     {
305         Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
306         InflateRect( &rc, -1, -1 );
307     }
308
309     if (infoPtr->state & BUTTON_HIGHLIGHTED)
310     {
311         /* draw button shadow: */
312         SelectObject(hDC, sysColorObjects.hbrushBtnShadow );
313         PatBlt(hDC, rc.left, rc.top, 1, rc.bottom-rc.top, PATCOPY );
314         PatBlt(hDC, rc.left, rc.top, rc.right-rc.left, 1, PATCOPY );
315         rc.left += 2;  /* To position the text down and right */
316         rc.top  += 2;
317     }
318     else GRAPH_DrawReliefRect( hDC, &rc, 2, 2, FALSE );
319     
320     /* draw button label, if any: */
321     text = (char*) USER_HEAP_LIN_ADDR( wndPtr->hText );
322     if (text && text[0])
323     {
324         SetTextColor( hDC, (wndPtr->dwStyle & WS_DISABLED) ?
325                      GetSysColor(COLOR_GRAYTEXT) : GetSysColor(COLOR_BTNTEXT));
326         DrawText(hDC, text, -1, &rc,
327                  DT_SINGLELINE | DT_CENTER | DT_VCENTER);
328         /* do we have the focus? */
329         if (infoPtr->state & BUTTON_HASFOCUS)
330         {
331             short xdelta, ydelta;
332             dwTextSize = GetTextExtent( hDC, text, strlen(text) );
333             GetTextMetrics( hDC, &tm );
334             xdelta = ((rc.right - rc.left) - LOWORD(dwTextSize) - 1) / 2;
335             ydelta = ((rc.bottom - rc.top) - tm.tmHeight - 1) / 2;
336             if (xdelta < 0) xdelta = 0;
337             if (ydelta < 0) ydelta = 0;
338             InflateRect( &rc, -xdelta, -ydelta );
339             DrawFocusRect( hDC, &rc );
340         }
341     }
342
343     SelectObject(hDC, (HANDLE)hOldPen);
344     SelectObject(hDC, (HANDLE)hOldBrush);
345 }
346
347
348 /**********************************************************************
349  *       Check Box & Radio Button Functions
350  */
351
352 static void CB_Paint( HWND hWnd, HDC hDC, WORD action )
353 {
354     RECT rc;
355     HBRUSH hBrush;
356     int textlen, delta, x, y;
357     char *text;
358     TEXTMETRIC tm;
359     SIZE size;
360     WND *wndPtr = WIN_FindWndPtr(hWnd);
361     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
362
363     GetClientRect(hWnd, &rc);
364
365     if (infoPtr->hFont) SelectObject( hDC, infoPtr->hFont );
366 #ifdef WINELIB32 /* JBP: Different in Win32 */
367     hBrush = (HBRUSH) SendMessage(GetParent(hWnd), WM_CTLCOLORBTN, (WPARAM)hDC,
368                                   (LPARAM)hWnd);
369 #else
370     hBrush = SendMessage(GetParent(hWnd), WM_CTLCOLOR, (WORD)hDC,
371                          MAKELPARAM(hWnd, CTLCOLOR_BTN));
372 #endif
373     if (action == ODA_DRAWENTIRE) FillRect(hDC, &rc, hBrush);
374
375     GetTextMetrics(hDC, &tm);
376     delta = (rc.bottom - rc.top - tm.tmHeight) >> 1;
377
378       /* Draw the check-box bitmap */
379     x = y = 0;
380     if (infoPtr->state & BUTTON_HIGHLIGHTED) x += 2 * checkBoxWidth;
381     if (infoPtr->state & (BUTTON_CHECKED | BUTTON_3STATE)) x += checkBoxWidth;
382     if (((wndPtr->dwStyle & 0x0f) == BS_RADIOBUTTON) ||
383         ((wndPtr->dwStyle & 0x0f) == BS_AUTORADIOBUTTON)) y += checkBoxHeight;
384     else if (infoPtr->state & BUTTON_3STATE) y += 2 * checkBoxHeight;
385     GRAPH_DrawBitmap( hDC, hbitmapCheckBoxes, rc.left, rc.top + delta,
386                       x, y, checkBoxWidth, checkBoxHeight );
387     rc.left += checkBoxWidth + tm.tmAveCharWidth / 2;
388
389     if (!(text = (char*) USER_HEAP_LIN_ADDR( wndPtr->hText ))) return;
390     textlen = strlen( text );
391
392     if (action == ODA_DRAWENTIRE)
393     {
394         if (wndPtr->dwStyle & WS_DISABLED)
395             SetTextColor( hDC, GetSysColor(COLOR_GRAYTEXT) );
396         DrawText(hDC, text, textlen, &rc, DT_SINGLELINE | DT_VCENTER);
397     }
398     
399     if ((action == ODA_FOCUS) ||
400         ((action == ODA_DRAWENTIRE) && (infoPtr->state & BUTTON_HASFOCUS)))
401     {
402         GetTextExtentPoint(hDC, text, textlen, &size);
403         if (delta > 1)
404         {
405             rc.top += delta - 1;
406             rc.bottom -= delta + 1;
407         }
408         rc.left--;
409         rc.right = MIN( rc.left + size.cx + 2, rc.right );
410         DrawFocusRect(hDC, &rc);
411     }
412 }
413
414
415 /**********************************************************************
416  *       BUTTON_CheckAutoRadioButton
417  *
418  * hWnd is checked, uncheck everything else in group
419  */
420 static void BUTTON_CheckAutoRadioButton(HWND hWnd)
421 {
422     HWND parent = GetParent(hWnd);
423     HWND sibling;
424     for(sibling = GetNextDlgGroupItem(parent,hWnd,FALSE);
425         sibling != hWnd;
426         sibling = GetNextDlgGroupItem(parent,sibling,FALSE))
427             SendMessage(sibling,BM_SETCHECK,BUTTON_UNCHECKED,0);
428 }
429
430
431 /**********************************************************************
432  *       Group Box Functions
433  */
434
435 static void GB_Paint( HWND hWnd, HDC hDC, WORD action )
436 {
437     RECT rc;
438     char *text;
439     SIZE size;
440     WND *wndPtr = WIN_FindWndPtr( hWnd );
441     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
442
443     if (action != ODA_DRAWENTIRE) return;
444
445     if (infoPtr->hFont) SelectObject( hDC, infoPtr->hFont );
446 #ifdef WINELIB32
447     SendMessage( GetParent(hWnd), WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hWnd );
448 #else
449     SendMessage( GetParent(hWnd), WM_CTLCOLOR, (WORD)hDC,
450                  MAKELPARAM(hWnd, CTLCOLOR_BTN));
451 #endif
452     SelectObject( hDC, sysColorObjects.hpenWindowFrame );
453
454     GetClientRect(hWnd, &rc);
455
456     MoveTo( hDC, rc.left, rc.top+2 );
457     LineTo( hDC, rc.right-1, rc.top+2 );
458     LineTo( hDC, rc.right-1, rc.bottom-1 );
459     LineTo( hDC, rc.left, rc.bottom-1 );
460     LineTo( hDC, rc.left, rc.top+2 );
461
462     if (!(text = (char*) USER_HEAP_LIN_ADDR( wndPtr->hText ))) return;
463     GetTextExtentPoint(hDC, text, strlen(text), &size);
464     rc.left  += 10;
465     rc.right  = rc.left + size.cx + 1;
466     rc.bottom = size.cy;
467     if (wndPtr->dwStyle & WS_DISABLED)
468         SetTextColor( hDC, GetSysColor(COLOR_GRAYTEXT) );
469     DrawText(hDC, text, -1, &rc, DT_SINGLELINE );
470 }
471
472
473 /**********************************************************************
474  *       User Button Functions
475  */
476
477 static void UB_Paint( HWND hWnd, HDC hDC, WORD action )
478 {
479     RECT rc;
480     HBRUSH hBrush;
481     WND *wndPtr = WIN_FindWndPtr( hWnd );
482     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
483
484     if (action == ODA_SELECT) return;
485
486     GetClientRect(hWnd, &rc);
487
488     if (infoPtr->hFont) SelectObject( hDC, infoPtr->hFont );
489 #ifdef WINELIB32
490     hBrush = (HBRUSH) SendMessage(GetParent(hWnd), WM_CTLCOLORBTN, (WPARAM)hDC,
491                                   (LPARAM)hWnd);
492 #else
493     hBrush = SendMessage(GetParent(hWnd), WM_CTLCOLOR, (WORD)hDC,
494                          MAKELPARAM(hWnd, CTLCOLOR_BTN));
495 #endif
496     FillRect(hDC, &rc, hBrush);
497
498     if ((action == ODA_FOCUS) ||
499         ((action == ODA_DRAWENTIRE) && (infoPtr->state & BUTTON_HASFOCUS)))
500         DrawFocusRect(hDC, &rc);
501 }
502
503
504 /**********************************************************************
505  *       Ownerdrawn Button Functions
506  */
507
508 static void OB_Paint( HWND hWnd, HDC hDC, WORD action )
509 {
510     DRAWITEMSTRUCT dis;
511     WND *wndPtr = WIN_FindWndPtr( hWnd );
512     BUTTONINFO *infoPtr = (BUTTONINFO *)wndPtr->wExtra;
513
514     dis.CtlType    = ODT_BUTTON;
515     dis.CtlID      = wndPtr->wIDmenu;
516     dis.itemID     = 0;
517     dis.itemAction = action;
518     dis.itemState  = (infoPtr->state & BUTTON_HASFOCUS) ? ODS_FOCUS : 0 |
519                      (infoPtr->state & BUTTON_HIGHLIGHTED) ? ODS_SELECTED : 0 |
520                      (wndPtr->dwStyle & WS_DISABLED) ? ODS_DISABLED : 0;
521     dis.hwndItem   = hWnd;
522     dis.hDC        = hDC;
523     GetClientRect( hWnd, &dis.rcItem );
524     dis.itemData   = 0;
525     SendMessage(GetParent(hWnd), WM_DRAWITEM, 1, (LPARAM) MAKE_SEGPTR(&dis) );
526 }