update for HEAD-2003091401
[reactos.git] / lib / user32 / controls / static.c
1 /* $Id$
2  *
3  * COPYRIGHT:        See COPYING in the top level directory
4  * PROJECT:          ReactOS User32
5  * PURPOSE:          Static control
6  * FILE:             lib/user32/controls/static.c
7  * PROGRAMER:        Ge van Geldorp (ge@gse.nl)
8  * REVISION HISTORY: 2003/05/28 GvG Created
9  * NOTES:            Adapted from Wine
10  */
11
12 #include "windows.h"
13 #include "user32/regcontrol.h"
14
15 static void STATIC_PaintOwnerDrawfn( HWND hwnd, HDC hdc, DWORD style );
16 static void STATIC_PaintTextfn( HWND hwnd, HDC hdc, DWORD style );
17 static void STATIC_PaintRectfn( HWND hwnd, HDC hdc, DWORD style );
18 static void STATIC_PaintIconfn( HWND hwnd, HDC hdc, DWORD style );
19 static void STATIC_PaintBitmapfn( HWND hwnd, HDC hdc, DWORD style );
20 static void STATIC_PaintEtchedfn( HWND hwnd, HDC hdc, DWORD style );
21 //static LRESULT CALLBACK StaticWndProcA( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
22 static LRESULT CALLBACK StaticWndProcW( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
23
24 static COLORREF color_black, color_gray, color_white;
25
26 /* offsets for GetWindowLong for static private information */
27 #define HFONT_GWL_OFFSET    0
28 #define HICON_GWL_OFFSET    (sizeof(HFONT))
29 #define STATIC_EXTRA_BYTES  (HICON_GWL_OFFSET + sizeof(HICON))
30
31 typedef void (*pfPaint)( HWND hwnd, HDC hdc, DWORD style );
32
33 static pfPaint staticPaintFunc[SS_TYPEMASK+1] =
34 {
35     STATIC_PaintTextfn,      /* SS_LEFT */
36     STATIC_PaintTextfn,      /* SS_CENTER */
37     STATIC_PaintTextfn,      /* SS_RIGHT */
38     STATIC_PaintIconfn,      /* SS_ICON */
39     STATIC_PaintRectfn,      /* SS_BLACKRECT */
40     STATIC_PaintRectfn,      /* SS_GRAYRECT */
41     STATIC_PaintRectfn,      /* SS_WHITERECT */
42     STATIC_PaintRectfn,      /* SS_BLACKFRAME */
43     STATIC_PaintRectfn,      /* SS_GRAYFRAME */
44     STATIC_PaintRectfn,      /* SS_WHITEFRAME */
45     NULL,                    /* SS_USERITEM */
46     STATIC_PaintTextfn,      /* SS_SIMPLE */
47     STATIC_PaintTextfn,      /* SS_LEFTNOWORDWRAP */
48     STATIC_PaintOwnerDrawfn, /* SS_OWNERDRAW */
49     STATIC_PaintBitmapfn,    /* SS_BITMAP */
50     NULL,                    /* SS_ENHMETAFILE */
51     STATIC_PaintEtchedfn,    /* SS_ETCHEDHORIZ */
52     STATIC_PaintEtchedfn,    /* SS_ETCHEDVERT */
53     STATIC_PaintEtchedfn,    /* SS_ETCHEDFRAME */
54 };
55
56
57 /*********************************************************************
58  * static class descriptor
59  */
60 const struct builtin_class_descr STATIC_builtin_class =
61 {
62     L"Static",            /* name */
63     CS_GLOBALCLASS | CS_DBLCLKS, /* style  */
64     (WNDPROC) StaticWndProcW,                  /* procW */
65     STATIC_EXTRA_BYTES,                        /* extra */
66     (LPCWSTR) IDC_ARROW,                        /* cursor */ /* FIXME Wine uses IDC_ARROWA */
67     0                                          /* brush */
68 };
69
70
71 /***********************************************************************
72  *           STATIC_SetIcon
73  *
74  * Set the icon for an SS_ICON control.
75  */
76 static HICON STATIC_SetIcon( HWND hwnd, HICON hicon, DWORD style )
77 {
78 #if 0 /* FIXME */
79     HICON prevIcon;
80     CURSORICONINFO *info = hicon?(CURSORICONINFO *) GlobalLock16(HICON_16(hicon)):NULL;
81
82     if ((style & SS_TYPEMASK) != SS_ICON) return 0;
83     if (hicon && !info) {
84         ERR("huh? hicon!=0, but info=0???\n");
85         return 0;
86     }
87     prevIcon = (HICON)SetWindowLongA( hwnd, HICON_GWL_OFFSET, (LONG)hicon );
88     if (hicon)
89     {
90         SetWindowPos( hwnd, 0, 0, 0, info->nWidth, info->nHeight,
91                         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER );
92         GlobalUnlock16(HICON_16(hicon));
93     }
94     return prevIcon;
95 #else
96     OutputDebugStringA("STATIC_SetIcon not implemented\n");
97     return NULL;
98 #endif
99 }
100
101 /***********************************************************************
102  *           STATIC_SetBitmap
103  *
104  * Set the bitmap for an SS_BITMAP control.
105  */
106 static HBITMAP STATIC_SetBitmap( HWND hwnd, HBITMAP hBitmap, DWORD style )
107 {
108     HBITMAP hOldBitmap;
109
110     if ((style & SS_TYPEMASK) != SS_BITMAP) return 0;
111     if (hBitmap && GetObjectType(hBitmap) != OBJ_BITMAP) {
112         OutputDebugStringA("huh? hBitmap!=0, but not bitmap\n");
113         return 0;
114     }
115     hOldBitmap = (HBITMAP)SetWindowLongA( hwnd, HICON_GWL_OFFSET, (LONG)hBitmap );
116     if (hBitmap)
117     {
118         BITMAP bm;
119         GetObjectW(hBitmap, sizeof(bm), &bm);
120         SetWindowPos( hwnd, 0, 0, 0, bm.bmWidth, bm.bmHeight,
121                       SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER );
122     }
123     return hOldBitmap;
124 }
125
126 /***********************************************************************
127  *           STATIC_LoadIconW
128  *
129  * Load the icon for an SS_ICON control.
130  */
131 static HICON STATIC_LoadIconW( HWND hwnd, LPCWSTR name )
132 {
133     HINSTANCE hInstance = (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE );
134     HICON hicon = LoadIconW( hInstance, name );
135     if (!hicon) hicon = LoadIconW( 0, name );
136     return hicon;
137 }
138
139 /***********************************************************************
140  *           STATIC_LoadBitmapW
141  *
142  * Load the bitmap for an SS_BITMAP control.
143  */
144 static HBITMAP STATIC_LoadBitmapW( HWND hwnd, LPCWSTR name )
145 {
146     HINSTANCE hInstance = (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE );
147     HBITMAP hbitmap = LoadBitmapW( hInstance, name );
148     if (!hbitmap)  /* Try OEM icon (FIXME: is this right?) */
149         hbitmap = LoadBitmapW( 0, name );
150     return hbitmap;
151 }
152
153 /***********************************************************************
154  *           STATIC_TryPaintFcn
155  *
156  * Try to immediately paint the control.
157  */
158 static VOID STATIC_TryPaintFcn(HWND hwnd, LONG full_style)
159 {
160     LONG style = full_style & SS_TYPEMASK;
161     RECT rc;
162
163     GetClientRect( hwnd, &rc );
164     if (!IsRectEmpty(&rc) && IsWindowVisible(hwnd) && staticPaintFunc[style])
165     {
166         HDC hdc;
167         hdc = GetDC( hwnd );
168         (staticPaintFunc[style])( hwnd, hdc, full_style );
169         ReleaseDC( hwnd, hdc );
170     }
171 }
172
173 /***********************************************************************
174  *           StaticWndProcW
175  */
176 static LRESULT CALLBACK StaticWndProcW( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
177 {
178     LRESULT lResult = 0;
179     LONG full_style = GetWindowLongA( hwnd, GWL_STYLE );
180     LONG style = full_style & SS_TYPEMASK;
181
182     if (!IsWindow( hwnd )) return 0;
183     switch (uMsg)
184     {
185     case WM_CREATE:
186         if (style < 0L || style > SS_TYPEMASK)
187         {
188             OutputDebugStringA("Unknown style\n");
189             return -1;
190         }
191         /* initialise colours */
192         color_black = GetSysColor(COLOR_3DDKSHADOW);
193         color_gray = GetSysColor(COLOR_3DSHADOW);
194         color_white  = GetSysColor(COLOR_3DHILIGHT);
195         break;
196
197     case WM_NCDESTROY:
198         if (style == SS_ICON) {
199 /*
200  * FIXME
201  *           DestroyIcon32( STATIC_SetIcon( wndPtr, 0 ) );
202  *
203  * We don't want to do this yet because DestroyIcon32 is broken. If the icon
204  * had already been loaded by the application the last thing we want to do is
205  * GlobalFree16 the handle.
206  */
207             break;
208         }
209         else return DefWindowProcW(hwnd, uMsg, wParam, lParam);
210
211     case WM_PAINT:
212         {
213             PAINTSTRUCT ps;
214             BeginPaint(hwnd, &ps);
215             if (staticPaintFunc[style])
216                 (staticPaintFunc[style])( hwnd, ps.hdc, full_style );
217             EndPaint(hwnd, &ps);
218         }
219         break;
220
221     case WM_ENABLE:
222         InvalidateRect(hwnd, NULL, TRUE);
223         break;
224
225     case WM_SYSCOLORCHANGE:
226         color_black = GetSysColor(COLOR_3DDKSHADOW);
227         color_gray = GetSysColor(COLOR_3DSHADOW);
228         color_white  = GetSysColor(COLOR_3DHILIGHT);
229         InvalidateRect(hwnd, NULL, TRUE);
230         break;
231
232     case WM_NCCREATE:
233         if (full_style & SS_SUNKEN)
234             SetWindowLongA( hwnd, GWL_EXSTYLE,
235                             GetWindowLongA( hwnd, GWL_EXSTYLE ) | WS_EX_STATICEDGE );
236
237         lParam = (LPARAM)(((LPCREATESTRUCTW)lParam)->lpszName);
238         /* fall through */
239     case WM_SETTEXT:
240         switch (style) {
241         case SS_ICON:
242         {
243             HICON hIcon;
244             hIcon = STATIC_LoadIconW(hwnd, (LPCWSTR)lParam);
245             /* FIXME : should we also return the previous hIcon here ??? */
246             STATIC_SetIcon(hwnd, hIcon, style);
247             break;
248         }
249         case SS_BITMAP:
250         {
251             HBITMAP hBitmap;
252             hBitmap = STATIC_LoadBitmapW(hwnd, (LPCWSTR)lParam);
253             STATIC_SetBitmap(hwnd, hBitmap, style);
254             break;
255         }
256         case SS_LEFT:
257         case SS_CENTER:
258         case SS_RIGHT:
259         case SS_SIMPLE:
260         case SS_LEFTNOWORDWRAP:
261         {
262             if (HIWORD(lParam))
263             {
264                 lResult = DefWindowProcW( hwnd, WM_SETTEXT, wParam, lParam );
265             }
266             if (uMsg == WM_SETTEXT)
267                 STATIC_TryPaintFcn( hwnd, full_style );
268             break;
269         }
270         default:
271             if (HIWORD(lParam))
272             {
273                 lResult = DefWindowProcW( hwnd, WM_SETTEXT, wParam, lParam );
274             }
275             if(uMsg == WM_SETTEXT)
276                 InvalidateRect(hwnd, NULL, TRUE);
277         }
278         return 1; /* success. FIXME: check text length */
279
280     case WM_SETFONT:
281         if ((style == SS_ICON) || (style == SS_BITMAP)) return 0;
282         SetWindowLongA( hwnd, HFONT_GWL_OFFSET, wParam );
283         if (LOWORD(lParam))
284             InvalidateRect( hwnd, NULL, TRUE );
285         break;
286
287     case WM_GETFONT:
288         return GetWindowLongA( hwnd, HFONT_GWL_OFFSET );
289
290     case WM_NCHITTEST:
291         if (full_style & SS_NOTIFY)
292            return HTCLIENT;
293         else
294            return HTTRANSPARENT;
295
296     case WM_GETDLGCODE:
297         return DLGC_STATIC;
298
299     case WM_LBUTTONDOWN:
300         if (! (full_style & SS_NOTIFY)) return 0;
301         SendMessageW(GetParent(hwnd), WM_COMMAND,
302                      MAKEWPARAM((WORD) GetWindowLongW(hwnd, GWL_ID), STN_CLICKED), (LPARAM) hwnd);
303         return 0;
304
305     case WM_LBUTTONDBLCLK:
306         if (! (full_style & SS_NOTIFY)) return 0;
307         SendMessageW(GetParent(hwnd), WM_COMMAND,
308                      MAKEWPARAM((WORD) GetWindowLongW(hwnd, GWL_ID), STN_DBLCLK), (LPARAM) hwnd);
309         return 0;
310
311     case STM_GETIMAGE:
312     case STM_GETICON:
313         return GetWindowLongA( hwnd, HICON_GWL_OFFSET );
314
315     case STM_SETIMAGE:
316         switch(wParam) {
317         case IMAGE_BITMAP:
318             lResult = (LRESULT)STATIC_SetBitmap( hwnd, (HBITMAP)lParam, style );
319             break;
320         case IMAGE_ICON:
321             lResult = (LRESULT)STATIC_SetIcon( hwnd, (HICON)lParam, style );
322             break;
323         default:
324             OutputDebugStringA("STM_SETIMAGE: Unhandled type\n");
325             break;
326         }
327         InvalidateRect( hwnd, NULL, TRUE );
328         break;
329
330     case STM_SETICON:
331         lResult = (LRESULT)STATIC_SetIcon( hwnd, (HICON)wParam, style );
332         InvalidateRect( hwnd, NULL, TRUE );
333         break;
334
335     default:
336         return DefWindowProcW(hwnd, uMsg, wParam, lParam);
337     }
338     return lResult;
339 }
340
341 static void STATIC_PaintOwnerDrawfn( HWND hwnd, HDC hdc, DWORD style )
342 {
343   DRAWITEMSTRUCT dis;
344   LONG id = GetWindowLongA( hwnd, GWL_ID );
345
346   dis.CtlType    = ODT_STATIC;
347   dis.CtlID      = id;
348   dis.itemID     = 0;
349   dis.itemAction = ODA_DRAWENTIRE;
350   dis.itemState  = 0;
351   dis.hwndItem   = hwnd;
352   dis.hDC        = hdc;
353   dis.itemData   = 0;
354   GetClientRect( hwnd, &dis.rcItem );
355
356   SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd );
357   SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
358 }
359
360 static void STATIC_PaintTextfn( HWND hwnd, HDC hdc, DWORD style )
361 {
362     RECT rc;
363     HBRUSH hBrush;
364     HFONT hFont;
365     UINT wFormat;
366     INT len;
367     WCHAR *text;
368     HPEN ShadowPen, HighlightPen;
369
370     GetClientRect( hwnd, &rc);
371
372     switch (style & SS_TYPEMASK)
373     {
374     case SS_LEFT:
375         wFormat = DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_NOCLIP;
376         break;
377
378     case SS_CENTER:
379         wFormat = DT_CENTER | DT_EXPANDTABS | DT_WORDBREAK | DT_NOCLIP;
380         break;
381
382     case SS_RIGHT:
383         wFormat = DT_RIGHT | DT_EXPANDTABS | DT_WORDBREAK | DT_NOCLIP;
384         break;
385
386     case SS_SIMPLE:
387         wFormat = DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_NOCLIP;
388         break;
389
390     case SS_LEFTNOWORDWRAP:
391         wFormat = DT_LEFT | DT_EXPANDTABS | DT_VCENTER;
392         break;
393
394     default:
395         return;
396     }
397
398     if (style & SS_NOPREFIX)
399         wFormat |= DT_NOPREFIX;
400     switch(style & SS_ELLIPSISMASK)
401     {
402     case SS_WORDELLIPSIS:
403         wFormat |= DT_WORD_ELLIPSIS | DT_SINGLELINE;
404         break;
405     case SS_ENDELLIPSIS:
406         wFormat |= DT_END_ELLIPSIS;
407         wFormat &= ~DT_WORDBREAK;
408         break;
409     case SS_PATHELLIPSIS:
410         wFormat |= DT_PATH_ELLIPSIS;
411         wFormat &= ~DT_WORDBREAK;
412         break;
413     }
414
415     if ((hFont = (HFONT)GetWindowLongA( hwnd, HFONT_GWL_OFFSET ))) SelectObject( hdc, hFont );
416
417     if ((style & SS_NOPREFIX) || ((style & SS_TYPEMASK) != SS_SIMPLE))
418     {
419         hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC,
420                                        (WPARAM)hdc, (LPARAM)hwnd );
421         if (!hBrush) /* did the app forget to call defwindowproc ? */
422             hBrush = (HBRUSH)DefWindowProcW(GetParent(hwnd), WM_CTLCOLORSTATIC,
423                                             (WPARAM)hdc, (LPARAM)hwnd);
424         FillRect( hdc, &rc, hBrush );
425     }
426     if (!IsWindowEnabled(hwnd)) SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
427     SetBkMode(hdc, TRANSPARENT);
428
429     if (style & SS_SUNKEN)
430     {
431         ShadowPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW));
432         SelectObject(hdc, ShadowPen);
433         MoveToEx(hdc, 0, rc.bottom - 2, NULL);
434         LineTo(hdc, 0, 0);
435         LineTo(hdc, rc.right - 1, 0);
436         HighlightPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT));
437         SelectObject(hdc, HighlightPen);
438         LineTo(hdc, rc.right - 1, rc.bottom - 1);
439         LineTo(hdc, -1, rc.bottom - 1);
440         SelectObject(hdc, GetStockObject(WHITE_PEN));
441         DeleteObject(HighlightPen);
442         DeleteObject(ShadowPen);
443         rc.left++;
444         rc.right--;
445         rc.top++;
446         rc.bottom--;
447     }
448
449     if (!(len = SendMessageW( hwnd, WM_GETTEXTLENGTH, 0, 0 ))) return;
450     if (!(text = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) return;
451     SendMessageW( hwnd, WM_GETTEXT, len + 1, (LPARAM)text );
452     DrawTextW( hdc, text, -1, &rc, wFormat );
453     HeapFree( GetProcessHeap(), 0, text );
454 }
455
456 static void STATIC_PaintRectfn( HWND hwnd, HDC hdc, DWORD style )
457 {
458     RECT rc;
459     HBRUSH hBrush;
460
461     GetClientRect( hwnd, &rc);
462
463     switch (style & SS_TYPEMASK)
464     {
465     case SS_BLACKRECT:
466         hBrush = CreateSolidBrush(color_black);
467         FillRect( hdc, &rc, hBrush );
468         break;
469     case SS_GRAYRECT:
470         hBrush = CreateSolidBrush(color_gray);
471         FillRect( hdc, &rc, hBrush );
472         break;
473     case SS_WHITERECT:
474         hBrush = CreateSolidBrush(color_white);
475         FillRect( hdc, &rc, hBrush );
476         break;
477     case SS_BLACKFRAME:
478         hBrush = CreateSolidBrush(color_black);
479         FrameRect( hdc, &rc, hBrush );
480         break;
481     case SS_GRAYFRAME:
482         hBrush = CreateSolidBrush(color_gray);
483         FrameRect( hdc, &rc, hBrush );
484         break;
485     case SS_WHITEFRAME:
486         hBrush = CreateSolidBrush(color_white);
487         FrameRect( hdc, &rc, hBrush );
488         break;
489     default:
490         return;
491     }
492     DeleteObject( hBrush );
493 }
494
495
496 static void STATIC_PaintIconfn( HWND hwnd, HDC hdc, DWORD style )
497 {
498     RECT rc;
499     HBRUSH hbrush;
500     HICON hIcon;
501
502     GetClientRect( hwnd, &rc );
503     hbrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC,
504                                    (WPARAM)hdc, (LPARAM)hwnd );
505     FillRect( hdc, &rc, hbrush );
506     if ((hIcon = (HICON)GetWindowLongA( hwnd, HICON_GWL_OFFSET )))
507         DrawIcon( hdc, rc.left, rc.top, hIcon );
508 }
509
510 static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, DWORD style )
511 {
512     RECT rc;
513     HBRUSH hbrush;
514     HDC hMemDC;
515     HBITMAP hBitmap, oldbitmap;
516
517     GetClientRect( hwnd, &rc );
518     hbrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC,
519                                    (WPARAM)hdc, (LPARAM)hwnd );
520     FillRect( hdc, &rc, hbrush );
521
522     if ((hBitmap = (HBITMAP)GetWindowLongA( hwnd, HICON_GWL_OFFSET )))
523     {
524         BITMAP bm;
525         SIZE sz;
526
527         if(GetObjectType(hBitmap) != OBJ_BITMAP) return;
528         if (!(hMemDC = CreateCompatibleDC( hdc ))) return;
529         GetObjectW(hBitmap, sizeof(bm), &bm);
530         GetBitmapDimensionEx(hBitmap, &sz);
531         oldbitmap = SelectObject(hMemDC, hBitmap);
532         BitBlt(hdc, sz.cx, sz.cy, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0,
533                SRCCOPY);
534         SelectObject(hMemDC, oldbitmap);
535         DeleteDC(hMemDC);
536     }
537 }
538
539
540 static void STATIC_PaintEtchedfn( HWND hwnd, HDC hdc, DWORD style )
541 {
542     RECT rc;
543
544     GetClientRect( hwnd, &rc );
545     switch (style & SS_TYPEMASK)
546     {
547         case SS_ETCHEDHORZ:
548 #if 0 /* This is what seems logical */
549             DrawEdge(hdc,&rc,EDGE_ETCHED,BF_TOP|BF_BOTTOM);
550 #else /* But this is what NT, Win2k and WinXP actually do */
551             DrawEdge(hdc,&rc,EDGE_ETCHED,BF_TOP);
552 #endif
553             break;
554         case SS_ETCHEDVERT:
555 #if 0 /* This is what seems logical */
556             DrawEdge(hdc,&rc,EDGE_ETCHED,BF_LEFT|BF_RIGHT);
557 #else /* But this is what NT, Win2k and WinXP actually do */
558             DrawEdge(hdc,&rc,EDGE_ETCHED,BF_LEFT);
559 #endif
560             break;
561         case SS_ETCHEDFRAME:
562             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_RECT);
563             break;
564     }
565 }