4 * Copyright 1997, 2002 Dimitrie O. Paun
5 * Copyright 1998, 1999 Eric Kohl
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * This code was audited for completeness against the documented features
24 * of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun.
26 * Unless otherwise noted, we believe this code to be complete, as per
27 * the specification mentioned above.
28 * If you discover missing features, or bugs, please note them below.
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(progress
);
47 HWND Self
; /* The window handle for this control */
48 INT CurVal
; /* Current progress value */
49 INT MinVal
; /* Minimum progress value */
50 INT MaxVal
; /* Maximum progress value */
51 INT Step
; /* Step to use on PMB_STEPIT */
52 INT MarqueePos
; /* Marquee animation position */
53 BOOL Marquee
; /* Whether the marquee animation is enabled */
54 COLORREF ColorBar
; /* Bar color */
55 COLORREF ColorBk
; /* Background color */
56 HFONT Font
; /* Handle to font (not unused) */
59 /* Control configuration constants */
62 #define MARQUEE_LEDS 5
63 #define ID_MARQUEE_TIMER 1
65 /***********************************************************************
68 * Invalide the range between old and new pos.
70 static void PROGRESS_Invalidate( PROGRESS_INFO
*infoPtr
, INT old
, INT
new )
72 LONG style
= GetWindowLongW (infoPtr
->Self
, GWL_STYLE
);
74 int oldPos
, newPos
, ledWidth
;
76 GetClientRect (infoPtr
->Self
, &rect
);
77 InflateRect(&rect
, -1, -1);
79 if (style
& PBS_VERTICAL
)
81 oldPos
= rect
.bottom
- MulDiv (old
- infoPtr
->MinVal
, rect
.bottom
- rect
.top
,
82 infoPtr
->MaxVal
- infoPtr
->MinVal
);
83 newPos
= rect
.bottom
- MulDiv (new - infoPtr
->MinVal
, rect
.bottom
- rect
.top
,
84 infoPtr
->MaxVal
- infoPtr
->MinVal
);
85 ledWidth
= MulDiv (rect
.right
- rect
.left
, 2, 3);
86 rect
.top
= min( oldPos
, newPos
);
87 rect
.bottom
= max( oldPos
, newPos
);
88 if (!(style
& PBS_SMOOTH
)) rect
.top
-= ledWidth
;
89 InvalidateRect( infoPtr
->Self
, &rect
, oldPos
< newPos
);
93 oldPos
= rect
.left
+ MulDiv (old
- infoPtr
->MinVal
, rect
.right
- rect
.left
,
94 infoPtr
->MaxVal
- infoPtr
->MinVal
);
95 newPos
= rect
.left
+ MulDiv (new - infoPtr
->MinVal
, rect
.right
- rect
.left
,
96 infoPtr
->MaxVal
- infoPtr
->MinVal
);
97 ledWidth
= MulDiv (rect
.bottom
- rect
.top
, 2, 3);
98 rect
.left
= min( oldPos
, newPos
);
99 rect
.right
= max( oldPos
, newPos
);
100 if (!(style
& PBS_SMOOTH
)) rect
.right
+= ledWidth
;
101 InvalidateRect( infoPtr
->Self
, &rect
, oldPos
> newPos
);
106 /***********************************************************************
108 * Draws the progress bar.
110 static LRESULT
PROGRESS_Draw (PROGRESS_INFO
*infoPtr
, HDC hdc
)
112 HBRUSH hbrBar
, hbrBk
;
113 int rightBar
, rightMost
, ledWidth
;
117 TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr
, hdc
);
119 /* get the required bar brush */
120 if (infoPtr
->ColorBar
== CLR_DEFAULT
)
121 hbrBar
= GetSysColorBrush(COLOR_HIGHLIGHT
);
123 hbrBar
= CreateSolidBrush (infoPtr
->ColorBar
);
125 if (infoPtr
->ColorBk
== CLR_DEFAULT
)
126 hbrBk
= GetSysColorBrush(COLOR_3DFACE
);
128 hbrBk
= CreateSolidBrush(infoPtr
->ColorBk
);
130 /* get client rectangle */
131 GetClientRect (infoPtr
->Self
, &rect
);
132 FrameRect( hdc
, &rect
, hbrBk
);
133 InflateRect(&rect
, -1, -1);
135 /* get the window style */
136 dwStyle
= GetWindowLongW (infoPtr
->Self
, GWL_STYLE
);
138 /* compute extent of progress bar */
139 if (dwStyle
& PBS_VERTICAL
) {
140 rightBar
= rect
.bottom
-
141 MulDiv (infoPtr
->CurVal
- infoPtr
->MinVal
,
142 rect
.bottom
- rect
.top
,
143 infoPtr
->MaxVal
- infoPtr
->MinVal
);
144 ledWidth
= MulDiv (rect
.right
- rect
.left
, 2, 3);
145 rightMost
= rect
.top
;
147 rightBar
= rect
.left
+
148 MulDiv (infoPtr
->CurVal
- infoPtr
->MinVal
,
149 rect
.right
- rect
.left
,
150 infoPtr
->MaxVal
- infoPtr
->MinVal
);
151 ledWidth
= MulDiv (rect
.bottom
- rect
.top
, 2, 3);
152 rightMost
= rect
.right
;
155 /* now draw the bar */
156 if (dwStyle
& PBS_SMOOTH
)
158 if (dwStyle
& PBS_VERTICAL
)
160 if (dwStyle
& PBS_MARQUEE
)
162 INT old_top
, old_bottom
, ledMStart
, leds
;
164 old_bottom
= rect
.bottom
;
166 leds
= rect
.bottom
- rect
.top
;
167 ledMStart
= (infoPtr
->MarqueePos
+ MARQUEE_LEDS
) - leds
;
171 rect
.top
= max(rect
.bottom
- ledMStart
, old_top
);
172 FillRect(hdc
, &rect
, hbrBar
);
173 rect
.bottom
= rect
.top
;
175 if(infoPtr
->MarqueePos
> 0)
177 rect
.top
= max(old_bottom
- infoPtr
->MarqueePos
, old_top
);
178 FillRect(hdc
, &rect
, hbrBk
);
179 rect
.bottom
= rect
.top
;
181 if(rect
.top
>= old_top
)
183 rect
.top
= max(rect
.bottom
- MARQUEE_LEDS
, old_top
);
184 FillRect(hdc
, &rect
, hbrBar
);
185 rect
.bottom
= rect
.top
;
187 if(rect
.top
>= old_top
)
190 FillRect(hdc
, &rect
, hbrBk
);
195 INT old_top
= rect
.top
;
197 FillRect(hdc
, &rect
, hbrBar
);
198 rect
.bottom
= rect
.top
;
200 FillRect(hdc
, &rect
, hbrBk
);
205 if (dwStyle
& PBS_MARQUEE
)
207 INT old_left
, old_right
, ledMStart
, leds
;
208 old_left
= rect
.left
;
209 old_right
= rect
.right
;
211 leds
= rect
.right
- rect
.left
;
212 ledMStart
= (infoPtr
->MarqueePos
+ MARQUEE_LEDS
) - leds
;
213 rect
.right
= rect
.left
;
217 rect
.right
= min(rect
.left
+ ledMStart
, old_right
);
218 FillRect(hdc
, &rect
, hbrBar
);
219 rect
.left
= rect
.right
;
221 if(infoPtr
->MarqueePos
> 0)
223 rect
.right
= min(old_left
+ infoPtr
->MarqueePos
, old_right
);
224 FillRect(hdc
, &rect
, hbrBk
);
225 rect
.left
= rect
.right
;
227 if(rect
.right
< old_right
)
229 rect
.right
= min(rect
.left
+ MARQUEE_LEDS
, old_right
);
230 FillRect(hdc
, &rect
, hbrBar
);
231 rect
.left
= rect
.right
;
233 if(rect
.right
< old_right
)
235 rect
.right
= old_right
;
236 FillRect(hdc
, &rect
, hbrBk
);
241 INT old_right
= rect
.right
;
242 rect
.right
= rightBar
;
243 FillRect(hdc
, &rect
, hbrBar
);
244 rect
.left
= rect
.right
;
245 rect
.right
= old_right
;
246 FillRect(hdc
, &rect
, hbrBk
);
250 if (dwStyle
& PBS_VERTICAL
) {
251 if (dwStyle
& PBS_MARQUEE
)
253 INT i
, old_top
, old_bottom
, ledMStart
, leds
;
255 old_bottom
= rect
.bottom
;
257 leds
= ((rect
.bottom
- rect
.top
) + (ledWidth
+ LED_GAP
) - 1) / (ledWidth
+ LED_GAP
);
258 ledMStart
= (infoPtr
->MarqueePos
+ MARQUEE_LEDS
) - leds
;
262 rect
.top
= max(rect
.bottom
- ledWidth
, old_top
);
263 FillRect(hdc
, &rect
, hbrBar
);
264 rect
.bottom
= rect
.top
;
266 if (rect
.top
<= old_top
) break;
267 FillRect(hdc
, &rect
, hbrBk
);
268 rect
.bottom
= rect
.top
;
271 if(infoPtr
->MarqueePos
> 0)
273 rect
.top
= max(old_bottom
- (infoPtr
->MarqueePos
* (ledWidth
+ LED_GAP
)), old_top
);
274 FillRect(hdc
, &rect
, hbrBk
);
275 rect
.bottom
= rect
.top
;
277 for(i
= 0; i
< MARQUEE_LEDS
&& rect
.top
>= old_top
; i
++)
279 rect
.top
= max(rect
.bottom
- ledWidth
, old_top
);
280 FillRect(hdc
, &rect
, hbrBar
);
281 rect
.bottom
= rect
.top
;
283 if (rect
.top
<= old_top
) break;
284 FillRect(hdc
, &rect
, hbrBk
);
285 rect
.bottom
= rect
.top
;
287 if(rect
.top
>= old_top
)
290 FillRect(hdc
, &rect
, hbrBk
);
295 while(rect
.bottom
> rightBar
) {
296 rect
.top
= rect
.bottom
- ledWidth
;
297 if (rect
.top
< rightMost
)
298 rect
.top
= rightMost
;
299 FillRect(hdc
, &rect
, hbrBar
);
300 rect
.bottom
= rect
.top
;
302 if (rect
.top
<= rightBar
) break;
303 FillRect(hdc
, &rect
, hbrBk
);
304 rect
.bottom
= rect
.top
;
307 rect
.top
= rightMost
;
308 FillRect(hdc
, &rect
, hbrBk
);
310 if (dwStyle
& PBS_MARQUEE
)
312 INT i
, old_right
, old_left
, ledMStart
, leds
;
313 old_left
= rect
.left
;
314 old_right
= rect
.right
;
316 leds
= ((rect
.right
- rect
.left
) + ledWidth
- 1) / (ledWidth
+ LED_GAP
);
317 ledMStart
= (infoPtr
->MarqueePos
+ MARQUEE_LEDS
) - leds
;
318 rect
.right
= rect
.left
;
322 rect
.right
= min(rect
.left
+ ledWidth
, old_right
);
323 FillRect(hdc
, &rect
, hbrBar
);
324 rect
.left
= rect
.right
;
325 rect
.right
+= LED_GAP
;
326 if (rect
.right
> old_right
) break;
327 FillRect(hdc
, &rect
, hbrBk
);
328 rect
.left
= rect
.right
;
331 if(infoPtr
->MarqueePos
> 0)
333 rect
.right
= min(old_left
+ (infoPtr
->MarqueePos
* (ledWidth
+ LED_GAP
)), old_right
);
334 FillRect(hdc
, &rect
, hbrBk
);
335 rect
.left
= rect
.right
;
337 for(i
= 0; i
< MARQUEE_LEDS
&& rect
.right
< old_right
; i
++)
339 rect
.right
= min(rect
.left
+ ledWidth
, old_right
);
340 FillRect(hdc
, &rect
, hbrBar
);
341 rect
.left
= rect
.right
;
342 rect
.right
+= LED_GAP
;
343 if (rect
.right
> old_right
) break;
344 FillRect(hdc
, &rect
, hbrBk
);
345 rect
.left
= rect
.right
;
347 if(rect
.right
< old_right
)
349 rect
.right
= old_right
;
350 FillRect(hdc
, &rect
, hbrBk
);
355 while(rect
.left
< rightBar
) {
356 rect
.right
= rect
.left
+ ledWidth
;
357 if (rect
.right
> rightMost
)
358 rect
.right
= rightMost
;
359 FillRect(hdc
, &rect
, hbrBar
);
360 rect
.left
= rect
.right
;
361 rect
.right
+= LED_GAP
;
362 if (rect
.right
>= rightBar
) break;
363 FillRect(hdc
, &rect
, hbrBk
);
364 rect
.left
= rect
.right
;
366 rect
.right
= rightMost
;
367 FillRect(hdc
, &rect
, hbrBk
);
372 /* delete bar brush */
373 if (infoPtr
->ColorBar
!= CLR_DEFAULT
) DeleteObject (hbrBar
);
374 if (infoPtr
->ColorBk
!= CLR_DEFAULT
) DeleteObject (hbrBk
);
380 /***********************************************************************
382 * Draw the progress bar. The background need not be erased.
383 * If dc!=0, it draws on it
385 static LRESULT
PROGRESS_Paint (PROGRESS_INFO
*infoPtr
, HDC hdc
)
388 if (hdc
) return PROGRESS_Draw (infoPtr
, hdc
);
389 hdc
= BeginPaint (infoPtr
->Self
, &ps
);
390 PROGRESS_Draw (infoPtr
, hdc
);
391 EndPaint (infoPtr
->Self
, &ps
);
396 /***********************************************************************
398 * Handle the marquee timer messages
400 static LRESULT
PROGRESS_Timer (PROGRESS_INFO
*infoPtr
, INT idTimer
)
402 if(idTimer
== ID_MARQUEE_TIMER
)
404 LONG style
= GetWindowLongW (infoPtr
->Self
, GWL_STYLE
);
408 GetClientRect (infoPtr
->Self
, &rect
);
409 InflateRect(&rect
, -1, -1);
411 if(!(style
& PBS_SMOOTH
))
415 if(style
& PBS_VERTICAL
)
417 width
= rect
.bottom
- rect
.top
;
418 height
= rect
.right
- rect
.left
;
422 height
= rect
.bottom
- rect
.top
;
423 width
= rect
.right
- rect
.left
;
425 ledWidth
= MulDiv (height
, 2, 3);
426 leds
= (width
+ ledWidth
- 1) / (ledWidth
+ LED_GAP
);
431 if(style
& PBS_VERTICAL
)
433 leds
= rect
.bottom
- rect
.top
;
437 leds
= rect
.right
- rect
.left
;
441 /* increment the marquee progress */
442 if(++infoPtr
->MarqueePos
>= leds
)
444 infoPtr
->MarqueePos
= 0;
447 InvalidateRect(infoPtr
->Self
, &rect
, TRUE
);
453 /***********************************************************************
455 * Makes sure the current position (CurVal) is within bounds.
457 static void PROGRESS_CoercePos(PROGRESS_INFO
*infoPtr
)
459 if(infoPtr
->CurVal
< infoPtr
->MinVal
)
460 infoPtr
->CurVal
= infoPtr
->MinVal
;
461 if(infoPtr
->CurVal
> infoPtr
->MaxVal
)
462 infoPtr
->CurVal
= infoPtr
->MaxVal
;
466 /***********************************************************************
468 * Set new Font for progress bar
470 static HFONT
PROGRESS_SetFont (PROGRESS_INFO
*infoPtr
, HFONT hFont
, BOOL bRedraw
)
472 HFONT hOldFont
= infoPtr
->Font
;
473 infoPtr
->Font
= hFont
;
474 /* Since infoPtr->Font is not used, there is no need for repaint */
478 static DWORD
PROGRESS_SetRange (PROGRESS_INFO
*infoPtr
, int low
, int high
)
480 DWORD res
= MAKELONG(LOWORD(infoPtr
->MinVal
), LOWORD(infoPtr
->MaxVal
));
482 /* if nothing changes, simply return */
483 if(infoPtr
->MinVal
== low
&& infoPtr
->MaxVal
== high
) return res
;
485 infoPtr
->MinVal
= low
;
486 infoPtr
->MaxVal
= high
;
487 PROGRESS_CoercePos(infoPtr
);
488 InvalidateRect(infoPtr
->Self
, NULL
, TRUE
);
492 /***********************************************************************
495 static LRESULT WINAPI
ProgressWindowProc(HWND hwnd
, UINT message
,
496 WPARAM wParam
, LPARAM lParam
)
498 PROGRESS_INFO
*infoPtr
;
500 TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd
, message
, wParam
, lParam
);
502 infoPtr
= (PROGRESS_INFO
*)GetWindowLongPtrW(hwnd
, 0);
504 if (!infoPtr
&& message
!= WM_CREATE
)
505 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
510 DWORD dwExStyle
= GetWindowLongW (hwnd
, GWL_EXSTYLE
);
511 dwExStyle
&= ~(WS_EX_CLIENTEDGE
| WS_EX_WINDOWEDGE
);
512 dwExStyle
|= WS_EX_STATICEDGE
;
513 SetWindowLongW (hwnd
, GWL_EXSTYLE
, dwExStyle
);
514 /* Force recalculation of a non-client area */
515 SetWindowPos(hwnd
, 0, 0, 0, 0, 0,
516 SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
518 /* allocate memory for info struct */
519 infoPtr
= (PROGRESS_INFO
*)Alloc (sizeof(PROGRESS_INFO
));
520 if (!infoPtr
) return -1;
521 SetWindowLongPtrW (hwnd
, 0, (DWORD_PTR
)infoPtr
);
523 /* initialize the info struct */
524 infoPtr
->Self
= hwnd
;
526 infoPtr
->MaxVal
= 100;
529 infoPtr
->MarqueePos
= 0;
530 infoPtr
->Marquee
= FALSE
;
531 infoPtr
->ColorBar
= CLR_DEFAULT
;
532 infoPtr
->ColorBk
= CLR_DEFAULT
;
534 TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd
);
539 TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd
);
541 SetWindowLongPtrW(hwnd
, 0, 0);
545 return (LRESULT
)infoPtr
->Font
;
548 return (LRESULT
)PROGRESS_SetFont(infoPtr
, (HFONT
)wParam
, (BOOL
)lParam
);
551 return PROGRESS_Paint (infoPtr
, (HDC
)wParam
);
554 return PROGRESS_Timer (infoPtr
, (INT
)wParam
);
559 oldVal
= infoPtr
->CurVal
;
561 infoPtr
->CurVal
+= (INT
)wParam
;
562 PROGRESS_CoercePos (infoPtr
);
563 TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal
, infoPtr
->CurVal
);
564 PROGRESS_Invalidate( infoPtr
, oldVal
, infoPtr
->CurVal
);
572 oldVal
= infoPtr
->CurVal
;
573 if(oldVal
!= wParam
) {
574 infoPtr
->CurVal
= (INT
)wParam
;
575 PROGRESS_CoercePos(infoPtr
);
576 TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal
, infoPtr
->CurVal
);
577 PROGRESS_Invalidate( infoPtr
, oldVal
, infoPtr
->CurVal
);
583 return PROGRESS_SetRange (infoPtr
, (int)LOWORD(lParam
), (int)HIWORD(lParam
));
588 oldStep
= infoPtr
->Step
;
589 infoPtr
->Step
= (INT
)wParam
;
596 oldVal
= infoPtr
->CurVal
;
597 infoPtr
->CurVal
+= infoPtr
->Step
;
598 if(infoPtr
->CurVal
> infoPtr
->MaxVal
)
599 infoPtr
->CurVal
= infoPtr
->MinVal
;
600 if(oldVal
!= infoPtr
->CurVal
)
602 TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal
, infoPtr
->CurVal
);
603 PROGRESS_Invalidate( infoPtr
, oldVal
, infoPtr
->CurVal
);
609 return PROGRESS_SetRange (infoPtr
, (int)wParam
, (int)lParam
);
613 ((PPBRANGE
)lParam
)->iLow
= infoPtr
->MinVal
;
614 ((PPBRANGE
)lParam
)->iHigh
= infoPtr
->MaxVal
;
616 return wParam
? infoPtr
->MinVal
: infoPtr
->MaxVal
;
619 return infoPtr
->CurVal
;
621 case PBM_SETBARCOLOR
:
622 infoPtr
->ColorBar
= (COLORREF
)lParam
;
623 InvalidateRect(hwnd
, NULL
, TRUE
);
627 infoPtr
->ColorBk
= (COLORREF
)lParam
;
628 InvalidateRect(hwnd
, NULL
, TRUE
);
634 infoPtr
->Marquee
= TRUE
;
635 SetTimer(infoPtr
->Self
, ID_MARQUEE_TIMER
, (UINT
)lParam
, NULL
);
639 infoPtr
->Marquee
= FALSE
;
640 KillTimer(infoPtr
->Self
, ID_MARQUEE_TIMER
);
642 return infoPtr
->Marquee
;
645 if ((message
>= WM_USER
) && (message
< WM_APP
))
646 ERR("unknown msg %04x wp=%04x lp=%08lx\n", message
, wParam
, lParam
);
647 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
652 /***********************************************************************
653 * PROGRESS_Register [Internal]
655 * Registers the progress bar window class.
657 void PROGRESS_Register (void)
661 ZeroMemory (&wndClass
, sizeof(wndClass
));
662 wndClass
.style
= CS_GLOBALCLASS
| CS_VREDRAW
| CS_HREDRAW
;
663 wndClass
.lpfnWndProc
= (WNDPROC
)ProgressWindowProc
;
664 wndClass
.cbClsExtra
= 0;
665 wndClass
.cbWndExtra
= sizeof (PROGRESS_INFO
*);
666 wndClass
.hCursor
= LoadCursorW (0, (LPWSTR
)IDC_ARROW
);
667 wndClass
.lpszClassName
= PROGRESS_CLASSW
;
669 RegisterClassW (&wndClass
);
673 /***********************************************************************
674 * PROGRESS_Unregister [Internal]
676 * Unregisters the progress bar window class.
678 void PROGRESS_Unregister (void)
680 UnregisterClassW (PROGRESS_CLASSW
, NULL
);