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