Protect against programs creating parts with negative width.
[wine] / dlls / comctl32 / status.c
1 /*
2  * Interface code to StatusWindow widget/control
3  *
4  * Copyright 1996 Bruce Milner
5  * Copyright 1998, 1999 Eric Kohl
6  */
7
8 #include "winbase.h"
9 #include "commctrl.h"
10 #include "status.h"
11 #include "debugtools.h"
12
13 DEFAULT_DEBUG_CHANNEL(statusbar)
14
15 /*
16  * Run tests using Waite Group Windows95 API Bible Vol. 1&2
17  * The second cdrom contains executables drawstat.exe,gettext.exe,
18  * simple.exe, getparts.exe, setparts.exe, statwnd.exe
19  */
20
21 /*
22  * Fixme/Todo
23  * 1) Don't hard code bar to bottom of window, allow CCS_TOP also.
24  * 2) Tooltip support (almost done).
25  */
26
27 #define _MAX(a,b) (((a)>(b))?(a):(b))
28 #define _MIN(a,b) (((a)>(b))?(b):(a))
29
30 #define HORZ_BORDER 0
31 #define VERT_BORDER 2
32 #define HORZ_GAP    2
33
34 #define STATUSBAR_GetInfoPtr(hwnd) ((STATUSWINDOWINFO *)GetWindowLongA (hwnd, 0))
35
36 /* prototype */
37 static void
38 STATUSBAR_SetPartBounds (HWND hwnd);
39
40 static void
41 STATUSBAR_DrawSizeGrip (HDC hdc, LPRECT lpRect)
42 {
43     HPEN hOldPen;
44     POINT pt;
45     INT i;
46
47     pt.x = lpRect->right - 1;
48     pt.y = lpRect->bottom - 1;
49
50     hOldPen = SelectObject (hdc, GetSysColorPen (COLOR_3DFACE));
51     MoveToEx (hdc, pt.x - 12, pt.y, NULL);
52     LineTo (hdc, pt.x, pt.y);
53     LineTo (hdc, pt.x, pt.y - 12);
54
55     pt.x--;
56     pt.y--;
57
58     SelectObject (hdc, GetSysColorPen (COLOR_3DSHADOW));
59     for (i = 1; i < 11; i += 4) {
60         MoveToEx (hdc, pt.x - i, pt.y, NULL);
61         LineTo (hdc, pt.x, pt.y - i);
62
63         MoveToEx (hdc, pt.x - i-1, pt.y, NULL);
64         LineTo (hdc, pt.x, pt.y - i-1);
65     }
66
67     SelectObject (hdc, GetSysColorPen (COLOR_3DHIGHLIGHT));
68     for (i = 3; i < 13; i += 4) {
69         MoveToEx (hdc, pt.x - i, pt.y, NULL);
70         LineTo (hdc, pt.x, pt.y - i);
71     }
72
73     SelectObject (hdc, hOldPen);
74 }
75
76
77 static void 
78 STATUSBAR_DrawPart (HDC hdc, STATUSWINDOWPART *part)
79 {
80     RECT r = part->bound;
81     UINT border = BDR_SUNKENOUTER;
82
83     if (part->style & SBT_POPOUT)
84       border = BDR_RAISEDOUTER;
85     else if (part->style & SBT_NOBORDERS)
86       border = 0;
87
88     DrawEdge(hdc, &r, border, BF_RECT|BF_ADJUST);
89
90     /* draw the icon */
91     if (part->hIcon) {
92         INT cy = r.bottom - r.top;
93
94         r.left += 2;
95         DrawIconEx (hdc, r.left, r.top, part->hIcon, cy, cy, 0, 0, DI_NORMAL);
96         r.left += cy;
97     }
98
99     /* now draw text */
100     if (part->text) {
101       int oldbkmode = SetBkMode(hdc, TRANSPARENT);
102       LPWSTR p = (LPWSTR)part->text;
103       UINT align = DT_LEFT;
104       if (*p == L'\t') {
105         p++;
106         align = DT_CENTER;
107
108         if (*p == L'\t') {
109           p++;
110           align = DT_RIGHT;
111         }
112       }
113       r.left += 3;
114       DrawTextW (hdc, p, lstrlenW (p), &r, align|DT_VCENTER|DT_SINGLELINE);
115       if (oldbkmode != TRANSPARENT)
116         SetBkMode(hdc, oldbkmode);
117     }
118 }
119
120
121 static VOID
122 STATUSBAR_RefreshPart (HWND hwnd, STATUSWINDOWPART *part, HDC hdc, int itemID)
123 {
124     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
125     HBRUSH hbrBk;
126     HFONT  hOldFont;
127
128     if (!IsWindowVisible (hwnd))
129         return;
130
131     if (part->bound.right < part->bound.left) return;
132
133     if (self->clrBk != CLR_DEFAULT)
134         hbrBk = CreateSolidBrush (self->clrBk);
135     else
136         hbrBk = GetSysColorBrush (COLOR_3DFACE);
137     FillRect(hdc, &part->bound, hbrBk);
138
139     hOldFont = SelectObject (hdc, self->hFont ? self->hFont : self->hDefaultFont);
140
141     if (part->style & SBT_OWNERDRAW) {
142         DRAWITEMSTRUCT dis;
143
144         dis.CtlID = GetWindowLongA (hwnd, GWL_ID);
145         dis.itemID = itemID;
146         dis.hwndItem = hwnd;
147         dis.hDC = hdc;
148         dis.rcItem = part->bound;
149         dis.itemData = (INT)part->text;
150         SendMessageA (GetParent (hwnd), WM_DRAWITEM,
151                 (WPARAM)dis.CtlID, (LPARAM)&dis);
152     }
153     else
154         STATUSBAR_DrawPart (hdc, part);
155
156     SelectObject (hdc, hOldFont);
157
158     if (self->clrBk != CLR_DEFAULT)
159         DeleteObject (hbrBk);
160
161     if (GetWindowLongA (hwnd, GWL_STYLE) & SBARS_SIZEGRIP) {
162         RECT rect;
163
164         GetClientRect (hwnd, &rect);
165         STATUSBAR_DrawSizeGrip (hdc, &rect);
166     }
167 }
168
169
170 static BOOL
171 STATUSBAR_Refresh (HWND hwnd, HDC hdc)
172 {
173     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
174     int      i;
175     RECT   rect;
176     HBRUSH hbrBk;
177     HFONT  hOldFont;
178
179     if (!IsWindowVisible(hwnd))
180         return (TRUE);
181
182     STATUSBAR_SetPartBounds(hwnd);
183
184     GetClientRect (hwnd, &rect);
185
186     if (infoPtr->clrBk != CLR_DEFAULT)
187         hbrBk = CreateSolidBrush (infoPtr->clrBk);
188     else
189         hbrBk = GetSysColorBrush (COLOR_3DFACE);
190     FillRect(hdc, &rect, hbrBk);
191
192     hOldFont = SelectObject (hdc, infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont);
193
194     if (infoPtr->simple) {
195         STATUSBAR_RefreshPart (hwnd, &infoPtr->part0,hdc,0);
196     }
197     else {
198         for (i = 0; i < infoPtr->numParts; i++) {
199             if (infoPtr->parts[i].style & SBT_OWNERDRAW) {
200                 DRAWITEMSTRUCT dis;
201
202                 dis.CtlID = GetWindowLongA (hwnd, GWL_ID);
203                 dis.itemID = i;
204                 dis.hwndItem = hwnd;
205                 dis.hDC = hdc;
206                 dis.rcItem = infoPtr->parts[i].bound;
207                 dis.itemData = (INT)infoPtr->parts[i].text;
208                 SendMessageA (GetParent (hwnd), WM_DRAWITEM,
209                         (WPARAM)dis.CtlID, (LPARAM)&dis);
210             }
211             else
212                 STATUSBAR_RefreshPart (hwnd, &infoPtr->parts[i], hdc,i);
213         }
214     }
215
216     SelectObject (hdc, hOldFont);
217
218     if (infoPtr->clrBk != CLR_DEFAULT)
219         DeleteObject (hbrBk);
220
221     if (GetWindowLongA(hwnd, GWL_STYLE) & SBARS_SIZEGRIP)
222         STATUSBAR_DrawSizeGrip (hdc, &rect);
223
224     return TRUE;
225 }
226
227
228 static void
229 STATUSBAR_SetPartBounds (HWND hwnd)
230 {
231     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
232     STATUSWINDOWPART *part;
233     RECT rect, *r;
234     int i;
235
236     /* get our window size */
237     GetClientRect (hwnd, &rect);
238
239     rect.top += VERT_BORDER;
240
241     /* set bounds for simple rectangle */
242     self->part0.bound = rect;
243
244     /* set bounds for non-simple rectangles */
245     for (i = 0; i < self->numParts; i++) {
246         part = &self->parts[i];
247         r = &self->parts[i].bound;
248         r->top = rect.top;
249         r->bottom = rect.bottom;
250         if (i == 0)
251             r->left = 0;
252         else
253             r->left = self->parts[i-1].bound.right + HORZ_GAP;
254         if (part->x == -1)
255             r->right = rect.right;
256         else
257             r->right = part->x;
258
259         if (self->hwndToolTip) {
260             TTTOOLINFOA ti;
261
262             ti.cbSize = sizeof(TTTOOLINFOA);
263             ti.hwnd = hwnd;
264             ti.uId = i;
265             ti.rect = *r;
266             SendMessageA (self->hwndToolTip, TTM_NEWTOOLRECTA,
267                             0, (LPARAM)&ti);
268         }
269     }
270 }
271
272
273 static VOID
274 STATUSBAR_RelayEvent (HWND hwndTip, HWND hwndMsg, UINT uMsg,
275                       WPARAM wParam, LPARAM lParam)
276 {
277     MSG msg;
278
279     msg.hwnd = hwndMsg;
280     msg.message = uMsg;
281     msg.wParam = wParam;
282     msg.lParam = lParam;
283     msg.time = GetMessageTime ();
284     msg.pt.x = LOWORD(GetMessagePos ());
285     msg.pt.y = HIWORD(GetMessagePos ());
286
287     SendMessageA (hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
288 }
289
290
291 inline static LRESULT
292 STATUSBAR_GetBorders (LPARAM lParam)
293 {
294     LPINT out = (LPINT) lParam;
295
296     out[0] = HORZ_BORDER; /* horizontal border width */
297     out[1] = VERT_BORDER; /* vertical border width */
298     out[2] = HORZ_GAP; /* width of border between rectangles */
299
300     return TRUE;
301 }
302
303
304 static LRESULT
305 STATUSBAR_GetIcon (HWND hwnd, WPARAM wParam)
306 {
307     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
308     INT nPart;
309
310     nPart = (INT)wParam & 0x00ff;
311     if ((nPart < -1) || (nPart >= self->numParts))
312         return 0;
313
314     if (nPart == -1)
315         return (self->part0.hIcon);
316     else
317         return (self->parts[nPart].hIcon);
318 }
319
320
321 static LRESULT
322 STATUSBAR_GetParts (HWND hwnd, WPARAM wParam, LPARAM lParam)
323 {
324     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
325     LPINT parts;
326     INT   num_parts;
327     INT   i;
328
329     num_parts = (INT) wParam;
330     parts = (LPINT) lParam;
331     if (parts) {
332         for (i = 0; i < num_parts; i++) {
333             parts[i] = infoPtr->parts[i].x;
334         }
335     }
336     return (infoPtr->numParts);
337 }
338
339
340 static LRESULT
341 STATUSBAR_GetRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
342 {
343     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
344     int part_num;
345     LPRECT  rect;
346
347     part_num = ((INT) wParam) & 0x00ff;
348     rect = (LPRECT) lParam;
349     if (infoPtr->simple)
350         *rect = infoPtr->part0.bound;
351     else
352         *rect = infoPtr->parts[part_num].bound;
353     return TRUE;
354 }
355
356
357 static LRESULT
358 STATUSBAR_GetTextA (HWND hwnd, WPARAM wParam, LPARAM lParam)
359 {
360     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
361     STATUSWINDOWPART *part;
362     INT   nPart;
363     LRESULT result;
364
365     nPart = ((INT) wParam) & 0x00ff;
366     if (self->simple)
367         part = &self->part0;
368     else
369         part = &self->parts[nPart];
370
371     if (part->style & SBT_OWNERDRAW)
372         result = (LRESULT)part->text;
373     else {
374         result = part->text ? lstrlenW (part->text) : 0;
375         result |= (part->style << 16);
376         if (lParam && LOWORD(result))
377             lstrcpyWtoA ((LPSTR)lParam, part->text);
378     }
379     return result;
380 }
381
382
383 static LRESULT
384 STATUSBAR_GetTextW (HWND hwnd, WPARAM wParam, LPARAM lParam)
385 {
386     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
387     STATUSWINDOWPART *part;
388     INT   nPart;
389     LRESULT result;
390
391     nPart = ((INT)wParam) & 0x00ff;
392     if (infoPtr->simple)
393         part = &infoPtr->part0;
394     else
395         part = &infoPtr->parts[nPart];
396
397     if (part->style & SBT_OWNERDRAW)
398         result = (LRESULT)part->text;
399     else {
400         result = part->text ? lstrlenW (part->text) : 0;
401         result |= (part->style << 16);
402         if (lParam)
403             lstrcpyW ((LPWSTR)lParam, part->text);
404     }
405     return result;
406 }
407
408
409 static LRESULT
410 STATUSBAR_GetTextLength (HWND hwnd, WPARAM wParam)
411 {
412     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
413     STATUSWINDOWPART *part;
414     INT part_num;
415     DWORD result;
416
417     part_num = ((INT) wParam) & 0x00ff;
418
419     if (infoPtr->simple)
420         part = &infoPtr->part0;
421     else
422         part = &infoPtr->parts[part_num];
423
424     if (part->text)
425         result = lstrlenW(part->text);
426     else
427         result = 0;
428
429     result |= (part->style << 16);
430     return result;
431 }
432
433
434 static LRESULT
435 STATUSBAR_GetTipTextA (HWND hwnd, WPARAM wParam, LPARAM lParam)
436 {
437     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
438
439     if (infoPtr->hwndToolTip) {
440         TTTOOLINFOA ti;
441         ti.cbSize = sizeof(TTTOOLINFOA);
442         ti.hwnd = hwnd;
443         ti.uId = LOWORD(wParam);
444         SendMessageA (infoPtr->hwndToolTip, TTM_GETTEXTA, 0, (LPARAM)&ti);
445
446         if (ti.lpszText)
447             lstrcpynA ((LPSTR)lParam, ti.lpszText, HIWORD(wParam));
448     }
449
450     return 0;
451 }
452
453
454 static LRESULT
455 STATUSBAR_GetTipTextW (HWND hwnd, WPARAM wParam, LPARAM lParam)
456 {
457     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
458
459     if (infoPtr->hwndToolTip) {
460         TTTOOLINFOW ti;
461         ti.cbSize = sizeof(TTTOOLINFOW);
462         ti.hwnd = hwnd;
463         ti.uId = LOWORD(wParam);
464         SendMessageW (infoPtr->hwndToolTip, TTM_GETTEXTW, 0, (LPARAM)&ti);
465
466         if (ti.lpszText)
467             lstrcpynW ((LPWSTR)lParam, ti.lpszText, HIWORD(wParam));
468     }
469
470     return 0;
471 }
472
473
474 inline static LRESULT
475 STATUSBAR_GetUnicodeFormat (HWND hwnd)
476 {
477     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
478     return infoPtr->bUnicode;
479 }
480
481
482 inline static LRESULT
483 STATUSBAR_IsSimple (HWND hwnd)
484 {
485     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
486     return infoPtr->simple;
487 }
488
489
490 static LRESULT
491 STATUSBAR_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
492 {
493     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
494     COLORREF oldBkColor;
495
496     oldBkColor = self->clrBk;
497     self->clrBk = (COLORREF)lParam;
498     RedrawWindow(hwnd,(RECT*)NULL,(HRGN)NULL,RDW_INVALIDATE|RDW_UPDATENOW);
499
500     return oldBkColor;
501 }
502
503
504 static LRESULT
505 STATUSBAR_SetIcon (HWND hwnd, WPARAM wParam, LPARAM lParam)
506 {
507     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
508     INT nPart = (INT)wParam & 0x00ff;
509     HDC hdc; 
510
511     if ((nPart < -1) || (nPart >= self->numParts))
512         return FALSE;
513
514     hdc = GetDC (hwnd);
515     if (nPart == -1) {
516         self->part0.hIcon = (HICON)lParam;
517         if (self->simple)
518             RedrawWindow(hwnd, &self->part0.bound,(HRGN)NULL,
519                          RDW_INVALIDATE|RDW_UPDATENOW);
520     }
521     else {
522         self->parts[nPart].hIcon = (HICON)lParam;
523         if (!(self->simple))
524             RedrawWindow(hwnd,&self->parts[nPart].bound,(HRGN)NULL,
525                          RDW_INVALIDATE|RDW_UPDATENOW);
526     }
527     ReleaseDC (hwnd, hdc);
528
529     return TRUE;
530 }
531
532
533 static LRESULT
534 STATUSBAR_SetMinHeight (HWND hwnd, WPARAM wParam, LPARAM lParam)
535 {
536     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
537
538     if (IsWindowVisible (hwnd)) {
539         HWND parent = GetParent (hwnd);
540         INT  width, x, y;
541         RECT parent_rect;
542
543         GetClientRect (parent, &parent_rect);
544         self->height = (INT)wParam + VERT_BORDER;
545         width = parent_rect.right - parent_rect.left;
546         x = parent_rect.left;
547         y = parent_rect.bottom - self->height;
548         MoveWindow (hwnd, parent_rect.left,
549                       parent_rect.bottom - self->height,
550                       width, self->height, TRUE);
551         STATUSBAR_SetPartBounds (hwnd);
552     }
553
554     return TRUE;
555 }
556
557
558 static LRESULT
559 STATUSBAR_SetParts (HWND hwnd, WPARAM wParam, LPARAM lParam)
560 {
561     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
562     STATUSWINDOWPART *tmp;
563     LPINT parts;
564     int i;
565     int oldNumParts;
566
567     if (self->simple)
568         self->simple = FALSE;
569
570     oldNumParts = self->numParts;
571     self->numParts = (INT) wParam;
572     parts = (LPINT) lParam;
573     if (oldNumParts > self->numParts) {
574         for (i = self->numParts ; i < oldNumParts; i++) {
575             if (self->parts[i].text && !(self->parts[i].style & SBT_OWNERDRAW))
576                 COMCTL32_Free (self->parts[i].text);
577         }
578     }
579     else if (oldNumParts < self->numParts) {
580         tmp = COMCTL32_Alloc (sizeof(STATUSWINDOWPART) * self->numParts);
581         for (i = 0; i < oldNumParts; i++) {
582             tmp[i] = self->parts[i];
583         }
584         if (self->parts)
585             COMCTL32_Free (self->parts);
586         self->parts = tmp;
587     }
588     
589     for (i = 0; i < self->numParts; i++) {
590         self->parts[i].x = parts[i];
591     }
592
593     if (self->hwndToolTip) {
594         INT nTipCount =
595             SendMessageA (self->hwndToolTip, TTM_GETTOOLCOUNT, 0, 0);
596
597         if (nTipCount < self->numParts) {
598             /* add tools */
599             TTTOOLINFOA ti;
600             INT i;
601
602             ZeroMemory (&ti, sizeof(TTTOOLINFOA));
603             ti.cbSize = sizeof(TTTOOLINFOA);
604             ti.hwnd = hwnd;
605             for (i = nTipCount; i < self->numParts; i++) {
606                 TRACE("add tool %d\n", i);
607                 ti.uId = i;
608                 SendMessageA (self->hwndToolTip, TTM_ADDTOOLA,
609                                 0, (LPARAM)&ti);
610             }
611         }
612         else if (nTipCount > self->numParts) {
613             /* delete tools */
614             INT i;
615
616             for (i = nTipCount - 1; i >= self->numParts; i--) {
617
618                 FIXME("delete tool %d\n", i);
619
620             }
621         }
622     }
623
624     STATUSBAR_SetPartBounds (hwnd);
625
626     RedrawWindow(hwnd,(RECT*)NULL,(HRGN)NULL,RDW_INVALIDATE|RDW_UPDATENOW);
627
628     return TRUE;
629 }
630
631
632 static LRESULT
633 STATUSBAR_SetTextA (HWND hwnd, WPARAM wParam, LPARAM lParam)
634 {
635     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
636     STATUSWINDOWPART *part=NULL;
637     int part_num;
638     int style;
639     LPSTR text;
640     int len;
641
642     text = (LPSTR) lParam;
643     part_num = ((INT) wParam) & 0x00ff;
644     style = ((INT) wParam) & 0xff00;
645
646     if (part_num==255)
647         part = &self->part0;
648     else if (!self->simple && self->parts!=NULL)
649         part = &self->parts[part_num];
650     if (!part) return FALSE;
651
652     if (!(part->style & SBT_OWNERDRAW) && part->text)
653         COMCTL32_Free (part->text);
654     part->text = 0;
655
656     if (style & SBT_OWNERDRAW) {
657         part->text = (LPWSTR)text;
658     }
659     else {
660         /* duplicate string */
661         if (text && (len = lstrlenA(text))) {
662             part->text = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
663             lstrcpyAtoW (part->text, text);
664         }
665     }
666     part->style = style;
667
668     RedrawWindow(hwnd,&part->bound,(HRGN)NULL,RDW_INVALIDATE|RDW_UPDATENOW);
669
670     return TRUE;
671 }
672
673
674 static LRESULT
675 STATUSBAR_SetTextW (HWND hwnd, WPARAM wParam, LPARAM lParam)
676 {
677     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
678     STATUSWINDOWPART *part;
679     INT  part_num, style, len;
680     LPWSTR text;
681
682     text = (LPWSTR) lParam;
683     part_num = ((INT) wParam) & 0x00ff;
684     style = ((INT) wParam) & 0xff00;
685
686     if ((self->simple) || (self->parts==NULL) || (part_num==255))
687         part = &self->part0;
688     else
689         part = &self->parts[part_num];
690     if (!part) return FALSE;
691
692     if (!(part->style & SBT_OWNERDRAW) && part->text)
693         COMCTL32_Free (part->text);
694     part->text = 0;
695
696     if (style & SBT_OWNERDRAW) {
697         part->text = text;
698     }
699     else {
700         /* duplicate string */
701         if (text && (len = lstrlenW(text))) {
702             part->text = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
703             lstrcpyW(part->text, text);
704         }
705     }
706     part->style = style;
707
708     RedrawWindow(hwnd,&part->bound,(HRGN)NULL,RDW_INVALIDATE|RDW_UPDATENOW);
709
710     return TRUE;
711 }
712
713
714 static LRESULT
715 STATUSBAR_SetTipTextA (HWND hwnd, WPARAM wParam, LPARAM lParam)
716 {
717     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
718
719     TRACE("part %d: \"%s\"\n", (INT)wParam, (LPSTR)lParam);
720     if (infoPtr->hwndToolTip) {
721         TTTOOLINFOA ti;
722         ti.cbSize = sizeof(TTTOOLINFOA);
723         ti.hwnd = hwnd;
724         ti.uId = (INT)wParam;
725         ti.hinst = 0;
726         ti.lpszText = (LPSTR)lParam;
727         SendMessageA (infoPtr->hwndToolTip, TTM_UPDATETIPTEXTA,
728                         0, (LPARAM)&ti);
729     }
730
731     return 0;
732 }
733
734
735 static LRESULT
736 STATUSBAR_SetTipTextW (HWND hwnd, WPARAM wParam, LPARAM lParam)
737 {
738     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
739
740     TRACE("part %d: \"%s\"\n", (INT)wParam, (LPSTR)lParam);
741     if (infoPtr->hwndToolTip) {
742         TTTOOLINFOW ti;
743         ti.cbSize = sizeof(TTTOOLINFOW);
744         ti.hwnd = hwnd;
745         ti.uId = (INT)wParam;
746         ti.hinst = 0;
747         ti.lpszText = (LPWSTR)lParam;
748         SendMessageW (infoPtr->hwndToolTip, TTM_UPDATETIPTEXTW,
749                         0, (LPARAM)&ti);
750     }
751
752     return 0;
753 }
754
755
756 inline static LRESULT
757 STATUSBAR_SetUnicodeFormat (HWND hwnd, WPARAM wParam)
758 {
759     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
760     BOOL bTemp = infoPtr->bUnicode;
761
762     TRACE("(0x%x)\n", (BOOL)wParam);
763     infoPtr->bUnicode = (BOOL)wParam;
764
765     return bTemp;
766 }
767
768
769 static LRESULT
770 STATUSBAR_Simple (HWND hwnd, WPARAM wParam, LPARAM lParam)
771 {
772     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
773     NMHDR  nmhdr;
774
775     infoPtr->simple = (BOOL)wParam;
776
777     /* send notification */
778     nmhdr.hwndFrom = hwnd;
779     nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
780     nmhdr.code = SBN_SIMPLEMODECHANGE;
781     SendMessageA (GetParent (hwnd), WM_NOTIFY, 0, (LPARAM)&nmhdr);
782
783     RedrawWindow(hwnd,(RECT*)NULL,(HRGN)NULL,RDW_INVALIDATE|RDW_UPDATENOW);
784
785     return TRUE;
786 }
787
788
789 static LRESULT
790 STATUSBAR_WMCreate (HWND hwnd, WPARAM wParam, LPARAM lParam)
791 {
792     LPCREATESTRUCTA lpCreate = (LPCREATESTRUCTA)lParam;
793     NONCLIENTMETRICSA nclm;
794     RECT        rect;
795     int         width, len;
796     HDC hdc;
797     STATUSWINDOWINFO *self;
798
799     self = (STATUSWINDOWINFO*)COMCTL32_Alloc (sizeof(STATUSWINDOWINFO));
800     SetWindowLongA (hwnd, 0, (DWORD)self);
801
802     self->numParts = 1;
803     self->parts = 0;
804     self->simple = FALSE;
805     self->clrBk = CLR_DEFAULT;
806     self->hFont = 0;
807     GetClientRect (hwnd, &rect);
808
809     nclm.cbSize = sizeof(NONCLIENTMETRICSA);
810     SystemParametersInfoA (SPI_GETNONCLIENTMETRICS, 0, &nclm, 0);
811     self->hDefaultFont = CreateFontIndirectA (&nclm.lfStatusFont);
812
813     /* initialize simple case */
814     self->part0.bound = rect;
815     self->part0.text = 0;
816     self->part0.x = 0;
817     self->part0.style = 0;
818     self->part0.hIcon = 0;
819
820     /* initialize first part */
821     self->parts = COMCTL32_Alloc (sizeof(STATUSWINDOWPART));
822     self->parts[0].bound = rect;
823     self->parts[0].text = 0;
824     self->parts[0].x = -1;
825     self->parts[0].style = 0;
826     self->parts[0].hIcon = 0;
827
828     if (IsWindowUnicode (hwnd)) {
829         self->bUnicode = TRUE;
830         if (lpCreate->lpszName &&
831             (len = lstrlenW ((LPCWSTR)lpCreate->lpszName))) {
832             self->parts[0].text = COMCTL32_Alloc ((len + 1)*sizeof(WCHAR));
833             lstrcpyW (self->parts[0].text, (LPCWSTR)lpCreate->lpszName);
834         }
835     }
836     else {
837         if (lpCreate->lpszName &&
838             (len = lstrlenA ((LPCSTR)lpCreate->lpszName))) {
839             self->parts[0].text = COMCTL32_Alloc ((len + 1)*sizeof(WCHAR));
840             lstrcpyAtoW (self->parts[0].text, (LPCSTR)lpCreate->lpszName);
841         }
842     }
843
844     if ((hdc = GetDC (0))) {
845         TEXTMETRICA tm;
846         HFONT hOldFont;
847
848         hOldFont = SelectObject (hdc,self->hDefaultFont);
849         GetTextMetricsA(hdc, &tm);
850         self->textHeight = tm.tmHeight;
851         SelectObject (hdc, hOldFont);
852         ReleaseDC(0, hdc);
853     }
854
855     if (GetWindowLongA (hwnd, GWL_STYLE) & SBT_TOOLTIPS) {
856         self->hwndToolTip =
857             CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
858                                CW_USEDEFAULT, CW_USEDEFAULT,
859                                CW_USEDEFAULT, CW_USEDEFAULT,
860                              hwnd, 0,
861                              GetWindowLongA (hwnd, GWL_HINSTANCE), NULL);
862
863         if (self->hwndToolTip) {
864             NMTOOLTIPSCREATED nmttc;
865
866             nmttc.hdr.hwndFrom = hwnd;
867             nmttc.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
868             nmttc.hdr.code = NM_TOOLTIPSCREATED;
869             nmttc.hwndToolTips = self->hwndToolTip;
870
871             SendMessageA (GetParent (hwnd), WM_NOTIFY,
872                             (WPARAM)nmttc.hdr.idFrom, (LPARAM)&nmttc);
873         }
874     }
875
876     GetClientRect (GetParent (hwnd), &rect);
877     width = rect.right - rect.left;
878     self->height = self->textHeight + 4 + VERT_BORDER;
879     MoveWindow (hwnd, lpCreate->x, lpCreate->y-1,
880                   width, self->height, FALSE);
881     STATUSBAR_SetPartBounds (hwnd);
882
883     return 0;
884 }
885
886
887 static LRESULT
888 STATUSBAR_WMDestroy (HWND hwnd)
889 {
890     STATUSWINDOWINFO *self = STATUSBAR_GetInfoPtr (hwnd);
891     int i;
892
893     for (i = 0; i < self->numParts; i++) {
894         if (self->parts[i].text && !(self->parts[i].style & SBT_OWNERDRAW))
895             COMCTL32_Free (self->parts[i].text);
896     }
897     if (self->part0.text && !(self->part0.style & SBT_OWNERDRAW))
898         COMCTL32_Free (self->part0.text);
899     COMCTL32_Free (self->parts);
900
901     /* delete default font */
902     if (self->hDefaultFont)
903         DeleteObject (self->hDefaultFont);
904
905     /* delete tool tip control */
906     if (self->hwndToolTip)
907         DestroyWindow (self->hwndToolTip);
908
909     COMCTL32_Free (self);
910     SetWindowLongA(hwnd, 0, 0);
911     return 0;
912 }
913
914
915 static inline LRESULT
916 STATUSBAR_WMGetFont (HWND hwnd)
917 {
918     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
919     return infoPtr->hFont;
920 }
921
922
923 static LRESULT
924 STATUSBAR_WMGetText (HWND hwnd, WPARAM wParam, LPARAM lParam)
925 {
926     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
927     INT len;
928
929     if (!(infoPtr->parts[0].text))
930         return 0;
931     len = lstrlenW (infoPtr->parts[0].text);
932     if (wParam > len) {
933         if (infoPtr->bUnicode)
934             lstrcpyW ((LPWSTR)lParam, infoPtr->parts[0].text);
935         else
936             lstrcpyWtoA ((LPSTR)lParam, infoPtr->parts[0].text);
937         return len;
938     }
939
940     return -1;
941 }
942
943
944 inline static LRESULT
945 STATUSBAR_WMMouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
946 {
947     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
948
949     if (infoPtr->hwndToolTip)
950         STATUSBAR_RelayEvent (infoPtr->hwndToolTip, hwnd,
951                               WM_MOUSEMOVE, wParam, lParam);
952     return 0;
953 }
954
955
956 static LRESULT
957 STATUSBAR_WMNCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
958 {
959     if (GetWindowLongA (hwnd, GWL_STYLE) & SBARS_SIZEGRIP) {
960         RECT  rect;
961         POINT pt;
962
963         GetClientRect (hwnd, &rect);
964
965         pt.x = (INT)LOWORD(lParam);
966         pt.y = (INT)HIWORD(lParam);
967         ScreenToClient (hwnd, &pt);
968
969         rect.left = rect.right - 13;
970         rect.top += 2;
971
972         if (PtInRect (&rect, pt))
973             return HTBOTTOMRIGHT;
974     }
975
976     return DefWindowProcA (hwnd, WM_NCHITTEST, wParam, lParam);
977 }
978
979
980 static inline LRESULT
981 STATUSBAR_WMNCLButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
982 {
983     PostMessageA (GetParent (hwnd), WM_NCLBUTTONDOWN, wParam, lParam);
984     return 0;
985 }
986
987
988 static inline LRESULT
989 STATUSBAR_WMNCLButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
990 {
991     PostMessageA (GetParent (hwnd), WM_NCLBUTTONUP, wParam, lParam);
992     return 0;
993 }
994
995
996 static LRESULT
997 STATUSBAR_WMPaint (HWND hwnd, WPARAM wParam)
998 {
999     HDC hdc;
1000     PAINTSTRUCT ps;
1001
1002     hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1003     STATUSBAR_Refresh (hwnd, hdc);
1004     if (!wParam)
1005         EndPaint (hwnd, &ps);
1006
1007     return 0;
1008 }
1009
1010
1011 static LRESULT
1012 STATUSBAR_WMSetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
1013 {
1014     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
1015
1016     infoPtr->hFont = (HFONT)wParam;
1017     if (LOWORD(lParam) == TRUE)
1018         RedrawWindow(hwnd,(RECT*)NULL,(HRGN)NULL,
1019                      RDW_INVALIDATE|RDW_UPDATENOW);
1020
1021     return 0;
1022 }
1023
1024
1025 static LRESULT
1026 STATUSBAR_WMSetText (HWND hwnd, WPARAM wParam, LPARAM lParam)
1027 {
1028     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
1029     STATUSWINDOWPART *part;
1030     int len;
1031
1032     if (infoPtr->numParts == 0)
1033         return FALSE;
1034
1035     part = &infoPtr->parts[0];
1036     /* duplicate string */
1037     if (part->text)
1038         COMCTL32_Free (part->text);
1039     part->text = 0;
1040     if (infoPtr->bUnicode) {
1041         if (lParam && (len = lstrlenW((LPCWSTR)lParam))) {
1042             part->text = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1043             lstrcpyW (part->text, (LPCWSTR)lParam);
1044         }
1045     }
1046     else {
1047         if (lParam && (len = lstrlenA((LPCSTR)lParam))) {
1048             part->text = COMCTL32_Alloc ((len+1)*sizeof(WCHAR));
1049             lstrcpyAtoW (part->text, (LPCSTR)lParam);
1050         }
1051     }
1052
1053     RedrawWindow(hwnd,&part->bound,(HRGN)NULL,RDW_INVALIDATE|RDW_UPDATENOW);
1054
1055     return TRUE;
1056 }
1057
1058
1059 static LRESULT
1060 STATUSBAR_WMSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
1061 {
1062     STATUSWINDOWINFO *infoPtr = STATUSBAR_GetInfoPtr (hwnd);
1063     INT  width, x, y, flags;
1064     RECT parent_rect;
1065     HWND parent;
1066
1067     /* Need to resize width to match parent */
1068     flags = (INT) wParam;
1069
1070     /* FIXME for flags =
1071      * SIZE_MAXIMIZED, SIZE_MAXSHOW, SIZE_MINIMIZED, SIZE_RESTORED
1072      */
1073
1074     if (flags == SIZE_RESTORED) {
1075         /* width and height don't apply */
1076         parent = GetParent (hwnd);
1077         GetClientRect (parent, &parent_rect);
1078         width = parent_rect.right - parent_rect.left;
1079         x = parent_rect.left;
1080         y = parent_rect.bottom - infoPtr->height;
1081         MoveWindow (hwnd, parent_rect.left, 
1082                       parent_rect.bottom - infoPtr->height,
1083                       width, infoPtr->height, TRUE);
1084         STATUSBAR_SetPartBounds (hwnd);
1085     }
1086     return 0;
1087 }
1088
1089
1090 static LRESULT
1091 STATUSBAR_SendNotify (HWND hwnd, UINT code)
1092 {
1093     NMHDR  nmhdr;
1094
1095     nmhdr.hwndFrom = hwnd;
1096     nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1097     nmhdr.code = code;
1098     SendMessageA (GetParent (hwnd), WM_NOTIFY, 0, (LPARAM)&nmhdr);
1099     return 0;
1100 }
1101
1102
1103
1104 static LRESULT WINAPI
1105 StatusWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1106 {
1107     TRACE("hwnd=%x msg=%x wparam=%x lparam=%lx\n", hwnd, msg, wParam, lParam);
1108     if (!(STATUSBAR_GetInfoPtr(hwnd)) && (msg != WM_CREATE))
1109         return DefWindowProcA (hwnd, msg, wParam, lParam);
1110
1111     switch (msg) {
1112         case SB_GETBORDERS:
1113             return STATUSBAR_GetBorders (lParam);
1114
1115         case SB_GETICON:
1116             return STATUSBAR_GetIcon (hwnd, wParam);
1117
1118         case SB_GETPARTS:
1119             return STATUSBAR_GetParts (hwnd, wParam, lParam);
1120
1121         case SB_GETRECT:
1122             return STATUSBAR_GetRect (hwnd, wParam, lParam);
1123
1124         case SB_GETTEXTA:
1125             return STATUSBAR_GetTextA (hwnd, wParam, lParam);
1126
1127         case SB_GETTEXTW:
1128             return STATUSBAR_GetTextW (hwnd, wParam, lParam);
1129
1130         case SB_GETTEXTLENGTHA:
1131         case SB_GETTEXTLENGTHW:
1132             return STATUSBAR_GetTextLength (hwnd, wParam);
1133
1134         case SB_GETTIPTEXTA:
1135             return STATUSBAR_GetTipTextA (hwnd, wParam, lParam);
1136
1137         case SB_GETTIPTEXTW:
1138             return STATUSBAR_GetTipTextW (hwnd, wParam, lParam);
1139
1140         case SB_GETUNICODEFORMAT:
1141             return STATUSBAR_GetUnicodeFormat (hwnd);
1142
1143         case SB_ISSIMPLE:
1144             return STATUSBAR_IsSimple (hwnd);
1145
1146         case SB_SETBKCOLOR:
1147             return STATUSBAR_SetBkColor (hwnd, wParam, lParam);
1148
1149         case SB_SETICON:
1150             return STATUSBAR_SetIcon (hwnd, wParam, lParam);
1151
1152         case SB_SETMINHEIGHT:
1153             return STATUSBAR_SetMinHeight (hwnd, wParam, lParam);
1154
1155         case SB_SETPARTS:       
1156             return STATUSBAR_SetParts (hwnd, wParam, lParam);
1157
1158         case SB_SETTEXTA:
1159             return STATUSBAR_SetTextA (hwnd, wParam, lParam);
1160
1161         case SB_SETTEXTW:
1162             return STATUSBAR_SetTextW (hwnd, wParam, lParam);
1163
1164         case SB_SETTIPTEXTA:
1165             return STATUSBAR_SetTipTextA (hwnd, wParam, lParam);
1166
1167         case SB_SETTIPTEXTW:
1168             return STATUSBAR_SetTipTextW (hwnd, wParam, lParam);
1169
1170         case SB_SETUNICODEFORMAT:
1171             return STATUSBAR_SetUnicodeFormat (hwnd, wParam);
1172
1173         case SB_SIMPLE:
1174             return STATUSBAR_Simple (hwnd, wParam, lParam);
1175
1176
1177         case WM_CREATE:
1178             return STATUSBAR_WMCreate (hwnd, wParam, lParam);
1179
1180         case WM_DESTROY:
1181             return STATUSBAR_WMDestroy (hwnd);
1182
1183         case WM_GETFONT:
1184             return STATUSBAR_WMGetFont (hwnd);
1185
1186         case WM_GETTEXT:
1187             return STATUSBAR_WMGetText (hwnd, wParam, lParam);
1188
1189         case WM_GETTEXTLENGTH:
1190             return STATUSBAR_GetTextLength (hwnd, 0);
1191
1192         case WM_LBUTTONDBLCLK:
1193             return STATUSBAR_SendNotify (hwnd, NM_DBLCLK);
1194
1195         case WM_LBUTTONUP:
1196             return STATUSBAR_SendNotify (hwnd, NM_CLICK);
1197
1198         case WM_MOUSEMOVE:
1199             return STATUSBAR_WMMouseMove (hwnd, wParam, lParam);
1200
1201         case WM_NCHITTEST:
1202             return STATUSBAR_WMNCHitTest (hwnd, wParam, lParam);
1203
1204         case WM_NCLBUTTONDOWN:
1205             return STATUSBAR_WMNCLButtonDown (hwnd, wParam, lParam);
1206
1207         case WM_NCLBUTTONUP:
1208             return STATUSBAR_WMNCLButtonUp (hwnd, wParam, lParam);
1209
1210         case WM_PAINT:
1211             return STATUSBAR_WMPaint (hwnd, wParam);
1212
1213         case WM_RBUTTONDBLCLK:
1214             return STATUSBAR_SendNotify (hwnd, NM_RDBLCLK);
1215
1216         case WM_RBUTTONUP:
1217             return STATUSBAR_SendNotify (hwnd, NM_RCLICK);
1218
1219         case WM_SETFONT:
1220             return STATUSBAR_WMSetFont (hwnd, wParam, lParam);
1221
1222         case WM_SETTEXT:
1223             return STATUSBAR_WMSetText (hwnd, wParam, lParam);
1224
1225         case WM_SIZE:
1226             return STATUSBAR_WMSize (hwnd, wParam, lParam);
1227
1228         default:
1229             if (msg >= WM_USER)
1230                 ERR("unknown msg %04x wp=%04x lp=%08lx\n",
1231                      msg, wParam, lParam);
1232             return DefWindowProcA (hwnd, msg, wParam, lParam);
1233     }
1234     return 0;
1235 }
1236
1237
1238 /***********************************************************************
1239  * STATUS_Register [Internal]
1240  *
1241  * Registers the status window class.
1242  */
1243
1244 VOID
1245 STATUS_Register (void)
1246 {
1247     WNDCLASSA wndClass;
1248
1249     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1250     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW;
1251     wndClass.lpfnWndProc   = (WNDPROC)StatusWindowProc;
1252     wndClass.cbClsExtra    = 0;
1253     wndClass.cbWndExtra    = sizeof(STATUSWINDOWINFO *);
1254     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
1255     wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1256     wndClass.lpszClassName = STATUSCLASSNAMEA;
1257  
1258     RegisterClassA (&wndClass);
1259 }
1260
1261
1262 /***********************************************************************
1263  * STATUS_Unregister [Internal]
1264  *
1265  * Unregisters the status window class.
1266  */
1267
1268 VOID
1269 STATUS_Unregister (void)
1270 {
1271     UnregisterClassA (STATUSCLASSNAMEA, (HINSTANCE)NULL);
1272 }
1273