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