2 * Interface code to StatusWindow widget/control
4 * Copyright 1996 Bruce Milner
5 * Copyright 1998, 1999 Eric Kohl
6 * Copyright 2002 Dimitrie O. Paun
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 * This code was audited for completeness against the documented features
25 * of Comctl32.dll version 6.0 on Sep. 24, 2002, by Dimitrie O. Paun.
27 * Unless otherwise noted, we believe this code to be complete, as per
28 * the specification mentioned above.
29 * If you discover missing features, or bugs, please note them below.
32 * -- CCS_BOTTOM (default)
37 * -- CCS_NOPARENTALIGN
40 * -- CCS_VERT (defaults to RIGHT)
48 #include "wine/unicode.h"
56 #include "wine/debug.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(statusbar
);
75 UINT minHeight
; /* at least MIN_PANE_HEIGHT, can be increased by SB_SETMINHEIGHT */
80 COLORREF clrBk
; /* background color */
81 BOOL bUnicode
; /* notify format. TRUE if notifies in Unicode */
82 STATUSWINDOWPART part0
; /* simple window */
83 STATUSWINDOWPART
* parts
;
90 * Run tests using Waite Group Windows95 API Bible Vol. 1&2
91 * The second cdrom contains executables drawstat.exe, gettext.exe,
92 * simple.exe, getparts.exe, setparts.exe, statwnd.exe
99 static const WCHAR themeClass
[] = { 'S','t','a','t','u','s',0 };
103 STATUSBAR_SetPartBounds (STATUS_INFO
*infoPtr
);
105 STATUSBAR_NotifyFormat (STATUS_INFO
*infoPtr
, HWND from
, INT cmd
);
107 static inline LPCSTR
debugstr_t(LPCWSTR text
, BOOL isW
)
109 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
113 STATUSBAR_ComputeHeight(STATUS_INFO
*infoPtr
)
120 COMCTL32_GetFontMetrics(infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
, &tm
);
121 margin
= (tm
.tmInternalLeading
? tm
.tmInternalLeading
: 2);
122 height
= max(tm
.tmHeight
+ margin
+ 2*GetSystemMetrics(SM_CYBORDER
), infoPtr
->minHeight
) + infoPtr
->verticalBorder
;
124 if ((theme
= GetWindowTheme(infoPtr
->Self
)))
126 /* Determine bar height from theme such that the content area is
127 * textHeight pixels large */
128 HDC hdc
= GetDC(infoPtr
->Self
);
130 memset (&r
, 0, sizeof (r
));
131 r
.bottom
= max(infoPtr
->minHeight
, tm
.tmHeight
);
132 if (SUCCEEDED(GetThemeBackgroundExtent(theme
, hdc
, SP_PANE
, 0, &r
, &r
)))
134 height
= r
.bottom
- r
.top
;
136 ReleaseDC(infoPtr
->Self
, hdc
);
139 TRACE(" textHeight=%d+%d, final height=%d\n", tm
.tmHeight
, tm
.tmInternalLeading
, height
);
144 STATUSBAR_DrawSizeGrip (HTHEME theme
, HDC hdc
, LPRECT lpRect
)
146 HPEN hPenFace
, hPenShadow
, hPenHighlight
, hOldPen
;
150 TRACE("draw size grip %s\n", wine_dbgstr_rect(lpRect
));
156 gripperRect
= *lpRect
;
157 if (SUCCEEDED (GetThemePartSize (theme
, hdc
, SP_GRIPPER
, 0, lpRect
,
158 TS_DRAW
, &gripperSize
)))
160 gripperRect
.left
= gripperRect
.right
- gripperSize
.cx
;
161 gripperRect
.top
= gripperRect
.bottom
- gripperSize
.cy
;
162 if (SUCCEEDED (DrawThemeBackground(theme
, hdc
, SP_GRIPPER
, 0, &gripperRect
, NULL
)))
167 pt
.x
= lpRect
->right
- 1;
168 pt
.y
= lpRect
->bottom
- 1;
170 hPenFace
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
);
171 hOldPen
= SelectObject( hdc
, hPenFace
);
172 MoveToEx (hdc
, pt
.x
- 12, pt
.y
, NULL
);
173 LineTo (hdc
, pt
.x
, pt
.y
);
174 LineTo (hdc
, pt
.x
, pt
.y
- 13);
179 hPenShadow
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dShadow
);
180 SelectObject( hdc
, hPenShadow
);
181 for (i
= 1; i
< 11; i
+= 4) {
182 MoveToEx (hdc
, pt
.x
- i
, pt
.y
, NULL
);
183 LineTo (hdc
, pt
.x
+ 1, pt
.y
- i
- 1);
185 MoveToEx (hdc
, pt
.x
- i
- 1, pt
.y
, NULL
);
186 LineTo (hdc
, pt
.x
+ 1, pt
.y
- i
- 2);
189 hPenHighlight
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dHilight
);
190 SelectObject( hdc
, hPenHighlight
);
191 for (i
= 3; i
< 13; i
+= 4) {
192 MoveToEx (hdc
, pt
.x
- i
, pt
.y
, NULL
);
193 LineTo (hdc
, pt
.x
+ 1, pt
.y
- i
- 1);
196 SelectObject (hdc
, hOldPen
);
197 DeleteObject( hPenFace
);
198 DeleteObject( hPenShadow
);
199 DeleteObject( hPenHighlight
);
204 STATUSBAR_DrawPart (const STATUS_INFO
*infoPtr
, HDC hdc
, const STATUSWINDOWPART
*part
, int itemID
)
206 RECT r
= part
->bound
;
207 UINT border
= BDR_SUNKENOUTER
;
208 HTHEME theme
= GetWindowTheme (infoPtr
->Self
);
209 int themePart
= SP_PANE
;
211 TRACE("part bound %s\n", wine_dbgstr_rect(&r
));
212 if (part
->style
& SBT_POPOUT
)
213 border
= BDR_RAISEDOUTER
;
214 else if (part
->style
& SBT_NOBORDERS
)
219 if ((GetWindowLongW (infoPtr
->Self
, GWL_STYLE
) & SBARS_SIZEGRIP
)
220 && (infoPtr
->simple
|| (itemID
== (infoPtr
->numParts
-1))))
221 themePart
= SP_GRIPPERPANE
;
222 DrawThemeBackground(theme
, hdc
, themePart
, 0, &r
, NULL
);
225 DrawEdge(hdc
, &r
, border
, BF_RECT
|BF_ADJUST
);
227 if (part
->style
& SBT_OWNERDRAW
) {
230 dis
.CtlID
= GetWindowLongPtrW (infoPtr
->Self
, GWLP_ID
);
232 dis
.hwndItem
= infoPtr
->Self
;
235 dis
.itemData
= (ULONG_PTR
)part
->text
;
236 SendMessageW (infoPtr
->Notify
, WM_DRAWITEM
, (WPARAM
)dis
.CtlID
, (LPARAM
)&dis
);
239 INT cy
= r
.bottom
- r
.top
;
242 DrawIconEx (hdc
, r
.left
, r
.top
, part
->hIcon
, cy
, cy
, 0, 0, DI_NORMAL
);
245 DrawStatusTextW (hdc
, &r
, part
->text
, SBT_NOBORDERS
);
251 STATUSBAR_RefreshPart (const STATUS_INFO
*infoPtr
, HDC hdc
, const STATUSWINDOWPART
*part
, int itemID
)
256 TRACE("item %d\n", itemID
);
258 if (part
->bound
.right
< part
->bound
.left
) return;
260 if (!RectVisible(hdc
, &part
->bound
))
263 if ((theme
= GetWindowTheme (infoPtr
->Self
)))
266 GetClientRect (infoPtr
->Self
, &cr
);
267 DrawThemeBackground(theme
, hdc
, 0, 0, &cr
, &part
->bound
);
271 if (infoPtr
->clrBk
!= CLR_DEFAULT
)
272 hbrBk
= CreateSolidBrush (infoPtr
->clrBk
);
274 hbrBk
= GetSysColorBrush (COLOR_3DFACE
);
275 FillRect(hdc
, &part
->bound
, hbrBk
);
276 if (infoPtr
->clrBk
!= CLR_DEFAULT
)
277 DeleteObject (hbrBk
);
280 STATUSBAR_DrawPart (infoPtr
, hdc
, part
, itemID
);
285 STATUSBAR_Refresh (STATUS_INFO
*infoPtr
, HDC hdc
)
294 if (!IsWindowVisible(infoPtr
->Self
))
297 STATUSBAR_SetPartBounds(infoPtr
);
299 GetClientRect (infoPtr
->Self
, &rect
);
301 if ((theme
= GetWindowTheme (infoPtr
->Self
)))
303 DrawThemeBackground(theme
, hdc
, 0, 0, &rect
, NULL
);
307 if (infoPtr
->clrBk
!= CLR_DEFAULT
)
308 hbrBk
= CreateSolidBrush (infoPtr
->clrBk
);
310 hbrBk
= GetSysColorBrush (COLOR_3DFACE
);
311 FillRect(hdc
, &rect
, hbrBk
);
312 if (infoPtr
->clrBk
!= CLR_DEFAULT
)
313 DeleteObject (hbrBk
);
316 hOldFont
= SelectObject (hdc
, infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
);
318 if (infoPtr
->simple
) {
319 STATUSBAR_RefreshPart (infoPtr
, hdc
, &infoPtr
->part0
, 0);
321 for (i
= 0; i
< infoPtr
->numParts
; i
++) {
322 STATUSBAR_RefreshPart (infoPtr
, hdc
, &infoPtr
->parts
[i
], i
);
326 SelectObject (hdc
, hOldFont
);
328 if (GetWindowLongW (infoPtr
->Self
, GWL_STYLE
) & SBARS_SIZEGRIP
)
329 STATUSBAR_DrawSizeGrip (theme
, hdc
, &rect
);
336 STATUSBAR_InternalHitTest(const STATUS_INFO
*infoPtr
, const POINT
*pt
)
342 for (i
= 0; i
< infoPtr
->numParts
; i
++)
343 if (pt
->x
>= infoPtr
->parts
[i
].bound
.left
&& pt
->x
<= infoPtr
->parts
[i
].bound
.right
)
350 STATUSBAR_SetPartBounds (STATUS_INFO
*infoPtr
)
352 STATUSWINDOWPART
*part
;
356 /* get our window size */
357 GetClientRect (infoPtr
->Self
, &rect
);
358 TRACE("client wnd size is %s\n", wine_dbgstr_rect(&rect
));
360 rect
.left
+= infoPtr
->horizontalBorder
;
361 rect
.top
+= infoPtr
->verticalBorder
;
363 /* set bounds for simple rectangle */
364 infoPtr
->part0
.bound
= rect
;
366 /* set bounds for non-simple rectangles */
367 for (i
= 0; i
< infoPtr
->numParts
; i
++) {
368 part
= &infoPtr
->parts
[i
];
369 r
= &infoPtr
->parts
[i
].bound
;
371 r
->bottom
= rect
.bottom
;
375 r
->left
= infoPtr
->parts
[i
-1].bound
.right
+ infoPtr
->horizontalGap
;
377 r
->right
= rect
.right
;
381 if (infoPtr
->hwndToolTip
) {
384 ti
.cbSize
= sizeof(TTTOOLINFOW
);
385 ti
.hwnd
= infoPtr
->Self
;
388 SendMessageW (infoPtr
->hwndToolTip
, TTM_NEWTOOLRECTW
,
396 STATUSBAR_Relay2Tip (const STATUS_INFO
*infoPtr
, UINT uMsg
,
397 WPARAM wParam
, LPARAM lParam
)
401 msg
.hwnd
= infoPtr
->Self
;
405 msg
.time
= GetMessageTime ();
406 msg
.pt
.x
= (short)LOWORD(GetMessagePos ());
407 msg
.pt
.y
= (short)HIWORD(GetMessagePos ());
409 return SendMessageW (infoPtr
->hwndToolTip
, TTM_RELAYEVENT
, 0, (LPARAM
)&msg
);
414 STATUSBAR_GetBorders (const STATUS_INFO
*infoPtr
, INT out
[])
417 out
[0] = infoPtr
->horizontalBorder
;
418 out
[1] = infoPtr
->verticalBorder
;
419 out
[2] = infoPtr
->horizontalGap
;
426 STATUSBAR_SetBorders (STATUS_INFO
*infoPtr
, const INT in
[])
429 infoPtr
->horizontalBorder
= in
[0];
430 infoPtr
->verticalBorder
= in
[1];
431 infoPtr
->horizontalGap
= in
[2];
432 InvalidateRect(infoPtr
->Self
, NULL
, FALSE
);
439 STATUSBAR_GetIcon (const STATUS_INFO
*infoPtr
, INT nPart
)
441 TRACE("%d\n", nPart
);
442 /* MSDN says: "simple parts are indexed with -1" */
443 if ((nPart
< -1) || (nPart
>= infoPtr
->numParts
))
447 return (infoPtr
->part0
.hIcon
);
449 return (infoPtr
->parts
[nPart
].hIcon
);
454 STATUSBAR_GetParts (const STATUS_INFO
*infoPtr
, INT num_parts
, INT parts
[])
458 TRACE("(%d)\n", num_parts
);
460 for (i
= 0; i
< num_parts
; i
++) {
461 parts
[i
] = infoPtr
->parts
[i
].x
;
464 return infoPtr
->numParts
;
469 STATUSBAR_GetRect (const STATUS_INFO
*infoPtr
, INT nPart
, LPRECT rect
)
471 TRACE("part %d\n", nPart
);
472 if(nPart
>= infoPtr
->numParts
|| nPart
< 0)
475 *rect
= infoPtr
->part0
.bound
;
477 *rect
= infoPtr
->parts
[nPart
].bound
;
483 STATUSBAR_GetTextA (STATUS_INFO
*infoPtr
, INT nPart
, LPSTR buf
)
485 STATUSWINDOWPART
*part
;
488 TRACE("part %d\n", nPart
);
490 /* MSDN says: "simple parts use index of 0", so this check is ok. */
491 if (nPart
< 0 || nPart
>= infoPtr
->numParts
) return 0;
494 part
= &infoPtr
->part0
;
496 part
= &infoPtr
->parts
[nPart
];
498 if (part
->style
& SBT_OWNERDRAW
)
499 result
= (LRESULT
)part
->text
;
501 DWORD len
= part
->text
? WideCharToMultiByte( CP_ACP
, 0, part
->text
, -1,
502 NULL
, 0, NULL
, NULL
) - 1 : 0;
503 result
= MAKELONG( len
, part
->style
);
504 if (part
->text
&& buf
)
505 WideCharToMultiByte( CP_ACP
, 0, part
->text
, -1, buf
, len
+1, NULL
, NULL
);
512 STATUSBAR_GetTextW (STATUS_INFO
*infoPtr
, INT nPart
, LPWSTR buf
)
514 STATUSWINDOWPART
*part
;
517 TRACE("part %d\n", nPart
);
518 if (nPart
< 0 || nPart
>= infoPtr
->numParts
) return 0;
521 part
= &infoPtr
->part0
;
523 part
= &infoPtr
->parts
[nPart
];
525 if (part
->style
& SBT_OWNERDRAW
)
526 result
= (LRESULT
)part
->text
;
528 result
= part
->text
? strlenW (part
->text
) : 0;
529 result
|= (part
->style
<< 16);
530 if (part
->text
&& buf
)
531 strcpyW (buf
, part
->text
);
538 STATUSBAR_GetTextLength (STATUS_INFO
*infoPtr
, INT nPart
)
540 STATUSWINDOWPART
*part
;
543 TRACE("part %d\n", nPart
);
545 /* MSDN says: "simple parts use index of 0", so this check is ok. */
546 if (nPart
< 0 || nPart
>= infoPtr
->numParts
) return 0;
549 part
= &infoPtr
->part0
;
551 part
= &infoPtr
->parts
[nPart
];
553 if ((~part
->style
& SBT_OWNERDRAW
) && part
->text
)
554 result
= strlenW(part
->text
);
558 result
|= (part
->style
<< 16);
563 STATUSBAR_GetTipTextA (const STATUS_INFO
*infoPtr
, INT id
, LPSTR tip
, INT size
)
567 CHAR buf
[INFOTIPSIZE
];
570 if (infoPtr
->hwndToolTip
) {
572 ti
.cbSize
= sizeof(TTTOOLINFOA
);
573 ti
.hwnd
= infoPtr
->Self
;
576 SendMessageA (infoPtr
->hwndToolTip
, TTM_GETTEXTA
, 0, (LPARAM
)&ti
);
578 lstrcpynA (tip
, buf
, size
);
585 STATUSBAR_GetTipTextW (const STATUS_INFO
*infoPtr
, INT id
, LPWSTR tip
, INT size
)
589 WCHAR buf
[INFOTIPSIZE
];
592 if (infoPtr
->hwndToolTip
) {
594 ti
.cbSize
= sizeof(TTTOOLINFOW
);
595 ti
.hwnd
= infoPtr
->Self
;
598 SendMessageW(infoPtr
->hwndToolTip
, TTM_GETTEXTW
, 0, (LPARAM
)&ti
);
600 lstrcpynW(tip
, buf
, size
);
608 STATUSBAR_SetBkColor (STATUS_INFO
*infoPtr
, COLORREF color
)
612 oldBkColor
= infoPtr
->clrBk
;
613 infoPtr
->clrBk
= color
;
614 InvalidateRect(infoPtr
->Self
, NULL
, FALSE
);
616 TRACE("CREF: %08x -> %08x\n", oldBkColor
, infoPtr
->clrBk
);
622 STATUSBAR_SetIcon (STATUS_INFO
*infoPtr
, INT nPart
, HICON hIcon
)
624 if ((nPart
< -1) || (nPart
>= infoPtr
->numParts
))
627 TRACE("setting part %d\n", nPart
);
629 /* FIXME: MSDN says "if nPart is -1, the status bar is assumed simple" */
631 if (infoPtr
->part0
.hIcon
== hIcon
) /* same as - no redraw */
633 infoPtr
->part0
.hIcon
= hIcon
;
635 InvalidateRect(infoPtr
->Self
, &infoPtr
->part0
.bound
, FALSE
);
637 if (infoPtr
->parts
[nPart
].hIcon
== hIcon
) /* same as - no redraw */
640 infoPtr
->parts
[nPart
].hIcon
= hIcon
;
641 if (!(infoPtr
->simple
))
642 InvalidateRect(infoPtr
->Self
, &infoPtr
->parts
[nPart
].bound
, FALSE
);
649 STATUSBAR_SetMinHeight (STATUS_INFO
*infoPtr
, INT height
)
651 DWORD ysize
= GetSystemMetrics(SM_CYSIZE
);
652 if (ysize
& 1) ysize
--;
653 infoPtr
->minHeight
= max(height
, ysize
);
654 infoPtr
->height
= STATUSBAR_ComputeHeight(infoPtr
);
655 /* like native, don't resize the control */
661 STATUSBAR_SetParts (STATUS_INFO
*infoPtr
, INT count
, LPINT parts
)
663 STATUSWINDOWPART
*tmp
;
666 TRACE("(%d,%p)\n", count
, parts
);
668 oldNumParts
= infoPtr
->numParts
;
669 infoPtr
->numParts
= count
;
670 if (oldNumParts
> infoPtr
->numParts
) {
671 for (i
= infoPtr
->numParts
; i
< oldNumParts
; i
++) {
672 if (!(infoPtr
->parts
[i
].style
& SBT_OWNERDRAW
))
673 Free (infoPtr
->parts
[i
].text
);
675 } else if (oldNumParts
< infoPtr
->numParts
) {
676 tmp
= Alloc (sizeof(STATUSWINDOWPART
) * infoPtr
->numParts
);
677 if (!tmp
) return FALSE
;
678 for (i
= 0; i
< oldNumParts
; i
++) {
679 tmp
[i
] = infoPtr
->parts
[i
];
681 Free (infoPtr
->parts
);
682 infoPtr
->parts
= tmp
;
684 if (oldNumParts
== infoPtr
->numParts
) {
685 for (i
=0; i
< oldNumParts
; i
++)
686 if (infoPtr
->parts
[i
].x
!= parts
[i
])
688 if (i
==oldNumParts
) /* Unchanged? no need to redraw! */
692 for (i
= 0; i
< infoPtr
->numParts
; i
++)
693 infoPtr
->parts
[i
].x
= parts
[i
];
695 if (infoPtr
->hwndToolTip
) {
699 ZeroMemory (&ti
, sizeof(TTTOOLINFOW
));
700 ti
.cbSize
= sizeof(TTTOOLINFOW
);
701 ti
.hwnd
= infoPtr
->Self
;
703 nTipCount
= SendMessageW (infoPtr
->hwndToolTip
, TTM_GETTOOLCOUNT
, 0, 0);
704 if (nTipCount
< infoPtr
->numParts
) {
706 for (i
= nTipCount
; i
< infoPtr
->numParts
; i
++) {
707 TRACE("add tool %d\n", i
);
709 SendMessageW (infoPtr
->hwndToolTip
, TTM_ADDTOOLW
,
713 else if (nTipCount
> infoPtr
->numParts
) {
715 for (i
= nTipCount
- 1; i
>= infoPtr
->numParts
; i
--) {
716 TRACE("delete tool %d\n", i
);
718 SendMessageW (infoPtr
->hwndToolTip
, TTM_DELTOOLW
,
723 STATUSBAR_SetPartBounds (infoPtr
);
724 InvalidateRect(infoPtr
->Self
, NULL
, FALSE
);
730 STATUSBAR_SetTextT (STATUS_INFO
*infoPtr
, INT nPart
, WORD style
,
731 LPCWSTR text
, BOOL isW
)
733 STATUSWINDOWPART
*part
=NULL
;
734 BOOL changed
= FALSE
;
737 if (style
& SBT_OWNERDRAW
) {
738 TRACE("part %d, text %p\n",nPart
,text
);
740 else TRACE("part %d, text %s\n", nPart
, debugstr_t(text
, isW
));
742 /* MSDN says: "If the parameter is set to SB_SIMPLEID (255), the status
743 * window is assumed to be a simple window */
745 if (nPart
== 0x00ff) {
746 part
= &infoPtr
->part0
;
748 if (infoPtr
->parts
&& nPart
>= 0 && nPart
< infoPtr
->numParts
) {
749 part
= &infoPtr
->parts
[nPart
];
752 if (!part
) return FALSE
;
754 if (part
->style
!= style
)
757 oldStyle
= part
->style
;
759 if (style
& SBT_OWNERDRAW
) {
760 if (!(oldStyle
& SBT_OWNERDRAW
))
762 part
->text
= (LPWSTR
)text
;
768 LPCSTR atxt
= (LPCSTR
)text
;
769 DWORD len
= MultiByteToWideChar( CP_ACP
, 0, atxt
, -1, NULL
, 0 );
770 ntext
= Alloc( (len
+ 1)*sizeof(WCHAR
) );
771 if (!ntext
) return FALSE
;
772 MultiByteToWideChar( CP_ACP
, 0, atxt
, -1, ntext
, len
);
774 ntext
= Alloc( (strlenW(text
) + 1)*sizeof(WCHAR
) );
775 if (!ntext
) return FALSE
;
776 strcpyW (ntext
, text
);
779 /* replace nonprintable characters with spaces */
789 /* check if text is unchanged -> no need to redraw */
791 if (!changed
&& part
->text
&& !lstrcmpW(ntext
, part
->text
)) {
796 if (!changed
&& !part
->text
)
800 if (!(oldStyle
& SBT_OWNERDRAW
))
804 InvalidateRect(infoPtr
->Self
, &part
->bound
, FALSE
);
805 UpdateWindow(infoPtr
->Self
);
812 STATUSBAR_SetTipTextA (const STATUS_INFO
*infoPtr
, INT id
, LPSTR text
)
814 TRACE("part %d: \"%s\"\n", id
, text
);
815 if (infoPtr
->hwndToolTip
) {
817 ti
.cbSize
= sizeof(TTTOOLINFOA
);
818 ti
.hwnd
= infoPtr
->Self
;
822 SendMessageA (infoPtr
->hwndToolTip
, TTM_UPDATETIPTEXTA
, 0, (LPARAM
)&ti
);
830 STATUSBAR_SetTipTextW (const STATUS_INFO
*infoPtr
, INT id
, LPWSTR text
)
832 TRACE("part %d: \"%s\"\n", id
, debugstr_w(text
));
833 if (infoPtr
->hwndToolTip
) {
835 ti
.cbSize
= sizeof(TTTOOLINFOW
);
836 ti
.hwnd
= infoPtr
->Self
;
840 SendMessageW (infoPtr
->hwndToolTip
, TTM_UPDATETIPTEXTW
, 0, (LPARAM
)&ti
);
847 static inline LRESULT
848 STATUSBAR_SetUnicodeFormat (STATUS_INFO
*infoPtr
, BOOL bUnicode
)
850 BOOL bOld
= infoPtr
->bUnicode
;
852 TRACE("(0x%x)\n", bUnicode
);
853 infoPtr
->bUnicode
= bUnicode
;
860 STATUSBAR_Simple (STATUS_INFO
*infoPtr
, BOOL simple
)
864 TRACE("(simple=%d)\n", simple
);
865 if (infoPtr
->simple
== simple
) /* no need to change */
868 infoPtr
->simple
= simple
;
870 /* send notification */
871 nmhdr
.hwndFrom
= infoPtr
->Self
;
872 nmhdr
.idFrom
= GetWindowLongPtrW (infoPtr
->Self
, GWLP_ID
);
873 nmhdr
.code
= SBN_SIMPLEMODECHANGE
;
874 SendMessageW (infoPtr
->Notify
, WM_NOTIFY
, 0, (LPARAM
)&nmhdr
);
875 InvalidateRect(infoPtr
->Self
, NULL
, FALSE
);
881 STATUSBAR_WMDestroy (STATUS_INFO
*infoPtr
)
886 for (i
= 0; i
< infoPtr
->numParts
; i
++) {
887 if (!(infoPtr
->parts
[i
].style
& SBT_OWNERDRAW
))
888 Free (infoPtr
->parts
[i
].text
);
890 if (!(infoPtr
->part0
.style
& SBT_OWNERDRAW
))
891 Free (infoPtr
->part0
.text
);
892 Free (infoPtr
->parts
);
894 /* delete default font */
895 if (infoPtr
->hDefaultFont
)
896 DeleteObject (infoPtr
->hDefaultFont
);
898 /* delete tool tip control */
899 if (infoPtr
->hwndToolTip
)
900 DestroyWindow (infoPtr
->hwndToolTip
);
902 CloseThemeData (GetWindowTheme (infoPtr
->Self
));
904 SetWindowLongPtrW(infoPtr
->Self
, 0, 0);
911 STATUSBAR_WMCreate (HWND hwnd
, const CREATESTRUCTA
*lpCreate
)
913 STATUS_INFO
*infoPtr
;
914 NONCLIENTMETRICSW nclm
;
920 infoPtr
= Alloc (sizeof(STATUS_INFO
));
921 if (!infoPtr
) goto create_fail
;
922 SetWindowLongPtrW (hwnd
, 0, (DWORD_PTR
)infoPtr
);
924 infoPtr
->Self
= hwnd
;
925 infoPtr
->Notify
= lpCreate
->hwndParent
;
926 infoPtr
->numParts
= 1;
928 infoPtr
->simple
= FALSE
;
929 infoPtr
->clrBk
= CLR_DEFAULT
;
931 infoPtr
->horizontalBorder
= HORZ_BORDER
;
932 infoPtr
->verticalBorder
= VERT_BORDER
;
933 infoPtr
->horizontalGap
= HORZ_GAP
;
934 infoPtr
->minHeight
= GetSystemMetrics(SM_CYSIZE
);
935 if (infoPtr
->minHeight
& 1) infoPtr
->minHeight
--;
937 STATUSBAR_NotifyFormat(infoPtr
, infoPtr
->Notify
, NF_REQUERY
);
939 ZeroMemory (&nclm
, sizeof(nclm
));
940 nclm
.cbSize
= sizeof(nclm
);
941 SystemParametersInfoW (SPI_GETNONCLIENTMETRICS
, nclm
.cbSize
, &nclm
, 0);
942 infoPtr
->hDefaultFont
= CreateFontIndirectW (&nclm
.lfStatusFont
);
944 GetClientRect (hwnd
, &rect
);
946 /* initialize simple case */
947 infoPtr
->part0
.bound
= rect
;
948 infoPtr
->part0
.text
= 0;
949 infoPtr
->part0
.x
= 0;
950 infoPtr
->part0
.style
= 0;
951 infoPtr
->part0
.hIcon
= 0;
953 /* initialize first part */
954 infoPtr
->parts
= Alloc (sizeof(STATUSWINDOWPART
));
955 if (!infoPtr
->parts
) goto create_fail
;
956 infoPtr
->parts
[0].bound
= rect
;
957 infoPtr
->parts
[0].text
= 0;
958 infoPtr
->parts
[0].x
= -1;
959 infoPtr
->parts
[0].style
= 0;
960 infoPtr
->parts
[0].hIcon
= 0;
962 OpenThemeData (hwnd
, themeClass
);
964 if (lpCreate
->lpszName
&& (len
= strlenW ((LPCWSTR
)lpCreate
->lpszName
)))
966 infoPtr
->parts
[0].text
= Alloc ((len
+ 1)*sizeof(WCHAR
));
967 if (!infoPtr
->parts
[0].text
) goto create_fail
;
968 strcpyW (infoPtr
->parts
[0].text
, (LPCWSTR
)lpCreate
->lpszName
);
971 dwStyle
= GetWindowLongW (hwnd
, GWL_STYLE
);
972 /* native seems to clear WS_BORDER, too */
973 dwStyle
&= ~WS_BORDER
;
974 SetWindowLongW (hwnd
, GWL_STYLE
, dwStyle
);
976 infoPtr
->height
= STATUSBAR_ComputeHeight(infoPtr
);
978 if (dwStyle
& SBT_TOOLTIPS
) {
979 infoPtr
->hwndToolTip
=
980 CreateWindowExW (0, TOOLTIPS_CLASSW
, NULL
, WS_POPUP
| TTS_ALWAYSTIP
,
981 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
982 CW_USEDEFAULT
, hwnd
, 0,
983 (HINSTANCE
)GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
), NULL
);
985 if (infoPtr
->hwndToolTip
) {
986 NMTOOLTIPSCREATED nmttc
;
988 nmttc
.hdr
.hwndFrom
= hwnd
;
989 nmttc
.hdr
.idFrom
= GetWindowLongPtrW (hwnd
, GWLP_ID
);
990 nmttc
.hdr
.code
= NM_TOOLTIPSCREATED
;
991 nmttc
.hwndToolTips
= infoPtr
->hwndToolTip
;
993 SendMessageW (lpCreate
->hwndParent
, WM_NOTIFY
, nmttc
.hdr
.idFrom
, (LPARAM
)&nmttc
);
1000 TRACE(" failed!\n");
1001 if (infoPtr
) STATUSBAR_WMDestroy(infoPtr
);
1006 /* in contrast to SB_GETTEXT*, WM_GETTEXT handles the text
1007 * of the first part only (usual behaviour) */
1009 STATUSBAR_WMGetText (const STATUS_INFO
*infoPtr
, INT size
, LPWSTR buf
)
1014 if (!(infoPtr
->parts
[0].text
))
1017 len
= strlenW (infoPtr
->parts
[0].text
);
1020 strcpyW (buf
, infoPtr
->parts
[0].text
);
1029 STATUSBAR_WMNCHitTest (const STATUS_INFO
*infoPtr
, INT x
, INT y
)
1031 if (GetWindowLongW (infoPtr
->Self
, GWL_STYLE
) & SBARS_SIZEGRIP
) {
1035 GetClientRect (infoPtr
->Self
, &rect
);
1039 ScreenToClient (infoPtr
->Self
, &pt
);
1041 rect
.left
= rect
.right
- 13;
1044 if (PtInRect (&rect
, pt
))
1045 return HTBOTTOMRIGHT
;
1053 STATUSBAR_WMPaint (STATUS_INFO
*infoPtr
, HDC hdc
)
1058 if (hdc
) return STATUSBAR_Refresh (infoPtr
, hdc
);
1059 hdc
= BeginPaint (infoPtr
->Self
, &ps
);
1060 STATUSBAR_Refresh (infoPtr
, hdc
);
1061 EndPaint (infoPtr
->Self
, &ps
);
1068 STATUSBAR_WMSetFont (STATUS_INFO
*infoPtr
, HFONT font
, BOOL redraw
)
1070 infoPtr
->hFont
= font
;
1071 TRACE("%p\n", infoPtr
->hFont
);
1073 infoPtr
->height
= STATUSBAR_ComputeHeight(infoPtr
);
1074 SendMessageW(infoPtr
->Self
, WM_SIZE
, 0, 0); /* update size */
1076 InvalidateRect(infoPtr
->Self
, NULL
, FALSE
);
1083 STATUSBAR_WMSetText (const STATUS_INFO
*infoPtr
, LPCSTR text
)
1085 STATUSWINDOWPART
*part
;
1089 if (infoPtr
->numParts
== 0)
1092 part
= &infoPtr
->parts
[0];
1093 /* duplicate string */
1097 if (text
&& (len
= strlenW((LPCWSTR
)text
))) {
1098 part
->text
= Alloc ((len
+1)*sizeof(WCHAR
));
1099 if (!part
->text
) return FALSE
;
1100 strcpyW (part
->text
, (LPCWSTR
)text
);
1103 InvalidateRect(infoPtr
->Self
, &part
->bound
, FALSE
);
1110 STATUSBAR_WMSize (STATUS_INFO
*infoPtr
, WORD flags
)
1115 /* Need to resize width to match parent */
1116 TRACE("flags %04x\n", flags
);
1118 if (flags
!= SIZE_RESTORED
&& flags
!= SIZE_MAXIMIZED
) {
1119 WARN("flags MUST be SIZE_RESTORED or SIZE_MAXIMIZED\n");
1123 if (GetWindowLongW(infoPtr
->Self
, GWL_STYLE
) & CCS_NORESIZE
) return FALSE
;
1125 /* width and height don't apply */
1126 if (!GetClientRect (infoPtr
->Notify
, &parent_rect
))
1129 width
= parent_rect
.right
- parent_rect
.left
;
1130 x
= parent_rect
.left
;
1131 y
= parent_rect
.bottom
- infoPtr
->height
;
1132 MoveWindow (infoPtr
->Self
, x
, y
, width
, infoPtr
->height
, TRUE
);
1133 STATUSBAR_SetPartBounds (infoPtr
);
1138 /* update theme after a WM_THEMECHANGED message */
1139 static LRESULT
theme_changed (const STATUS_INFO
* infoPtr
)
1141 HTHEME theme
= GetWindowTheme (infoPtr
->Self
);
1142 CloseThemeData (theme
);
1143 OpenThemeData (infoPtr
->Self
, themeClass
);
1149 STATUSBAR_NotifyFormat (STATUS_INFO
*infoPtr
, HWND from
, INT cmd
)
1151 if (cmd
== NF_REQUERY
) {
1152 INT i
= SendMessageW(from
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->Self
, NF_QUERY
);
1153 infoPtr
->bUnicode
= (i
== NFR_UNICODE
);
1155 return infoPtr
->bUnicode
? NFR_UNICODE
: NFR_ANSI
;
1160 STATUSBAR_SendMouseNotify(const STATUS_INFO
*infoPtr
, UINT code
, LPARAM lParam
)
1164 TRACE("code %04x, lParam=%lx\n", code
, lParam
);
1165 nm
.hdr
.hwndFrom
= infoPtr
->Self
;
1166 nm
.hdr
.idFrom
= GetWindowLongPtrW(infoPtr
->Self
, GWLP_ID
);
1168 nm
.pt
.x
= (short)LOWORD(lParam
);
1169 nm
.pt
.y
= (short)HIWORD(lParam
);
1170 nm
.dwItemSpec
= STATUSBAR_InternalHitTest(infoPtr
, &nm
.pt
);
1172 nm
.dwHitInfo
= 0x30000; /* seems constant */
1173 SendMessageW(infoPtr
->Notify
, WM_NOTIFY
, 0, (LPARAM
)&nm
);
1179 static LRESULT WINAPI
1180 StatusWindowProc (HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1182 STATUS_INFO
*infoPtr
= (STATUS_INFO
*)GetWindowLongPtrW (hwnd
, 0);
1183 INT nPart
= ((INT
) wParam
) & 0x00ff;
1186 TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd
, msg
, wParam
, lParam
);
1187 if (!infoPtr
&& msg
!= WM_CREATE
)
1188 return DefWindowProcW (hwnd
, msg
, wParam
, lParam
);
1192 return STATUSBAR_GetBorders (infoPtr
, (INT
*)lParam
);
1195 return (LRESULT
)STATUSBAR_GetIcon (infoPtr
, nPart
);
1198 return STATUSBAR_GetParts (infoPtr
, (INT
)wParam
, (INT
*)lParam
);
1201 return STATUSBAR_GetRect (infoPtr
, nPart
, (LPRECT
)lParam
);
1204 return STATUSBAR_GetTextA (infoPtr
, nPart
, (LPSTR
)lParam
);
1207 return STATUSBAR_GetTextW (infoPtr
, nPart
, (LPWSTR
)lParam
);
1209 case SB_GETTEXTLENGTHA
:
1210 case SB_GETTEXTLENGTHW
:
1211 return STATUSBAR_GetTextLength (infoPtr
, nPart
);
1213 case SB_GETTIPTEXTA
:
1214 return STATUSBAR_GetTipTextA (infoPtr
, LOWORD(wParam
), (LPSTR
)lParam
, HIWORD(wParam
));
1216 case SB_GETTIPTEXTW
:
1217 return STATUSBAR_GetTipTextW (infoPtr
, LOWORD(wParam
), (LPWSTR
)lParam
, HIWORD(wParam
));
1219 case SB_GETUNICODEFORMAT
:
1220 return infoPtr
->bUnicode
;
1223 return infoPtr
->simple
;
1226 return STATUSBAR_SetBorders (infoPtr
, (INT
*)lParam
);
1229 return STATUSBAR_SetBkColor (infoPtr
, (COLORREF
)lParam
);
1232 return STATUSBAR_SetIcon (infoPtr
, nPart
, (HICON
)lParam
);
1234 case SB_SETMINHEIGHT
:
1235 return STATUSBAR_SetMinHeight (infoPtr
, (INT
)wParam
);
1238 return STATUSBAR_SetParts (infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
1241 return STATUSBAR_SetTextT (infoPtr
, nPart
, wParam
& 0xff00, (LPCWSTR
)lParam
, FALSE
);
1244 return STATUSBAR_SetTextT (infoPtr
, nPart
, wParam
& 0xff00, (LPCWSTR
)lParam
, TRUE
);
1246 case SB_SETTIPTEXTA
:
1247 return STATUSBAR_SetTipTextA (infoPtr
, (INT
)wParam
, (LPSTR
)lParam
);
1249 case SB_SETTIPTEXTW
:
1250 return STATUSBAR_SetTipTextW (infoPtr
, (INT
)wParam
, (LPWSTR
)lParam
);
1252 case SB_SETUNICODEFORMAT
:
1253 return STATUSBAR_SetUnicodeFormat (infoPtr
, (BOOL
)wParam
);
1256 return STATUSBAR_Simple (infoPtr
, (BOOL
)wParam
);
1259 return STATUSBAR_WMCreate (hwnd
, (LPCREATESTRUCTA
)lParam
);
1262 return STATUSBAR_WMDestroy (infoPtr
);
1265 return (LRESULT
)(infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
);
1268 return STATUSBAR_WMGetText (infoPtr
, (INT
)wParam
, (LPWSTR
)lParam
);
1270 case WM_GETTEXTLENGTH
:
1271 return STATUSBAR_GetTextLength (infoPtr
, 0);
1273 case WM_LBUTTONDBLCLK
:
1274 return STATUSBAR_SendMouseNotify(infoPtr
, NM_DBLCLK
, lParam
);
1277 return STATUSBAR_SendMouseNotify(infoPtr
, NM_CLICK
, lParam
);
1280 return STATUSBAR_Relay2Tip (infoPtr
, msg
, wParam
, lParam
);
1283 res
= STATUSBAR_WMNCHitTest(infoPtr
, (short)LOWORD(lParam
),
1284 (short)HIWORD(lParam
));
1285 if (res
!= HTERROR
) return res
;
1286 return DefWindowProcW (hwnd
, msg
, wParam
, lParam
);
1288 case WM_NCLBUTTONUP
:
1289 case WM_NCLBUTTONDOWN
:
1290 PostMessageW (infoPtr
->Notify
, msg
, wParam
, lParam
);
1293 case WM_NOTIFYFORMAT
:
1294 return STATUSBAR_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
1296 case WM_PRINTCLIENT
:
1298 return STATUSBAR_WMPaint (infoPtr
, (HDC
)wParam
);
1300 case WM_RBUTTONDBLCLK
:
1301 return STATUSBAR_SendMouseNotify(infoPtr
, NM_RDBLCLK
, lParam
);
1304 return STATUSBAR_SendMouseNotify(infoPtr
, NM_RCLICK
, lParam
);
1307 return STATUSBAR_WMSetFont (infoPtr
, (HFONT
)wParam
, LOWORD(lParam
));
1310 return STATUSBAR_WMSetText (infoPtr
, (LPCSTR
)lParam
);
1313 if (STATUSBAR_WMSize (infoPtr
, (WORD
)wParam
)) return 0;
1314 return DefWindowProcW (hwnd
, msg
, wParam
, lParam
);
1316 case WM_SYSCOLORCHANGE
:
1317 COMCTL32_RefreshSysColors();
1320 case WM_THEMECHANGED
:
1321 return theme_changed (infoPtr
);
1324 if ((msg
>= WM_USER
) && (msg
< WM_APP
) && !COMCTL32_IsReflectedMessage(msg
))
1325 ERR("unknown msg %04x wp=%04lx lp=%08lx\n",
1326 msg
, wParam
, lParam
);
1327 return DefWindowProcW (hwnd
, msg
, wParam
, lParam
);
1332 /***********************************************************************
1333 * STATUS_Register [Internal]
1335 * Registers the status window class.
1339 STATUS_Register (void)
1343 ZeroMemory (&wndClass
, sizeof(WNDCLASSW
));
1344 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
| CS_VREDRAW
;
1345 wndClass
.lpfnWndProc
= StatusWindowProc
;
1346 wndClass
.cbClsExtra
= 0;
1347 wndClass
.cbWndExtra
= sizeof(STATUS_INFO
*);
1348 wndClass
.hCursor
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
1349 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_BTNFACE
+ 1);
1350 wndClass
.lpszClassName
= STATUSCLASSNAMEW
;
1352 RegisterClassW (&wndClass
);
1356 /***********************************************************************
1357 * STATUS_Unregister [Internal]
1359 * Unregisters the status window class.
1363 STATUS_Unregister (void)
1365 UnregisterClassW (STATUSCLASSNAMEW
, NULL
);