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);