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