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