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 belive this code to be complete, as per
27 * the specification mentioned above.
28 * If you discover missing features, or bugs, please note them below.
31 * --support PBS_MARQUE
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(progress);
44 HWND Self; /* The window handle for this control */
45 INT CurVal; /* Current progress value */
46 INT MinVal; /* Minimum progress value */
47 INT MaxVal; /* Maximum progress value */
48 INT Step; /* Step to use on PMB_STEPIT */
49 COLORREF ColorBar; /* Bar color */
50 COLORREF ColorBk; /* Background color */
51 HFONT Font; /* Handle to font (not unused) */
54 /* Control configuration constants */
58 /***********************************************************************
61 * Invalide the range between old and new pos.
63 static void PROGRESS_Invalidate( PROGRESS_INFO *infoPtr, INT old, INT new )
65 LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
67 int oldPos, newPos, ledWidth;
69 GetClientRect (infoPtr->Self, &rect);
70 InflateRect(&rect, -1, -1);
72 if (style & PBS_VERTICAL)
74 oldPos = rect.bottom - MulDiv (old - infoPtr->MinVal, rect.bottom - rect.top,
75 infoPtr->MaxVal - infoPtr->MinVal);
76 newPos = rect.bottom - MulDiv (new - infoPtr->MinVal, rect.bottom - rect.top,
77 infoPtr->MaxVal - infoPtr->MinVal);
78 ledWidth = MulDiv (rect.right - rect.left, 2, 3);
79 rect.top = min( oldPos, newPos );
80 rect.bottom = max( oldPos, newPos );
81 if (!(style & PBS_SMOOTH)) rect.top -= ledWidth;
82 InvalidateRect( infoPtr->Self, &rect, oldPos < newPos );
86 oldPos = rect.left + MulDiv (old - infoPtr->MinVal, rect.right - rect.left,
87 infoPtr->MaxVal - infoPtr->MinVal);
88 newPos = rect.left + MulDiv (new - infoPtr->MinVal, rect.right - rect.left,
89 infoPtr->MaxVal - infoPtr->MinVal);
90 ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
91 rect.left = min( oldPos, newPos );
92 rect.right = max( oldPos, newPos );
93 if (!(style & PBS_SMOOTH)) rect.right += ledWidth;
94 InvalidateRect( infoPtr->Self, &rect, oldPos > newPos );
99 /***********************************************************************
101 * Draws the progress bar.
103 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
105 HBRUSH hbrBar, hbrBk;
106 int rightBar, rightMost, ledWidth;
110 TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);
112 /* get the required bar brush */
113 if (infoPtr->ColorBar == CLR_DEFAULT)
114 hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
116 hbrBar = CreateSolidBrush (infoPtr->ColorBar);
118 if (infoPtr->ColorBk == CLR_DEFAULT)
119 hbrBk = GetSysColorBrush(COLOR_3DFACE);
121 hbrBk = CreateSolidBrush(infoPtr->ColorBk);
123 /* get client rectangle */
124 GetClientRect (infoPtr->Self, &rect);
125 FrameRect( hdc, &rect, hbrBk );
126 InflateRect(&rect, -1, -1);
128 /* get the window style */
129 dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
131 /* compute extent of progress bar */
132 if (dwStyle & PBS_VERTICAL) {
133 rightBar = rect.bottom -
134 MulDiv (infoPtr->CurVal - infoPtr->MinVal,
135 rect.bottom - rect.top,
136 infoPtr->MaxVal - infoPtr->MinVal);
137 ledWidth = MulDiv (rect.right - rect.left, 2, 3);
138 rightMost = rect.top;
140 rightBar = rect.left +
141 MulDiv (infoPtr->CurVal - infoPtr->MinVal,
142 rect.right - rect.left,
143 infoPtr->MaxVal - infoPtr->MinVal);
144 ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
145 rightMost = rect.right;
148 /* now draw the bar */
149 if (dwStyle & PBS_SMOOTH)
151 if (dwStyle & PBS_VERTICAL)
153 INT old_top = rect.top;
155 FillRect(hdc, &rect, hbrBar);
156 rect.bottom = rect.top;
158 FillRect(hdc, &rect, hbrBk);
162 INT old_right = rect.right;
163 rect.right = rightBar;
164 FillRect(hdc, &rect, hbrBar);
165 rect.left = rect.right;
166 rect.right = old_right;
167 FillRect(hdc, &rect, hbrBk);
170 if (dwStyle & PBS_VERTICAL) {
171 while(rect.bottom > rightBar) {
172 rect.top = rect.bottom - ledWidth;
173 if (rect.top < rightMost)
174 rect.top = rightMost;
175 FillRect(hdc, &rect, hbrBar);
176 rect.bottom = rect.top;
178 if (rect.top <= rightBar) break;
179 FillRect(hdc, &rect, hbrBk);
180 rect.bottom = rect.top;
182 rect.top = rightMost;
183 FillRect(hdc, &rect, hbrBk);
185 while(rect.left < rightBar) {
186 rect.right = rect.left + ledWidth;
187 if (rect.right > rightMost)
188 rect.right = rightMost;
189 FillRect(hdc, &rect, hbrBar);
190 rect.left = rect.right;
191 rect.right += LED_GAP;
192 if (rect.right >= rightBar) break;
193 FillRect(hdc, &rect, hbrBk);
194 rect.left = rect.right;
196 rect.right = rightMost;
197 FillRect(hdc, &rect, hbrBk);
201 /* delete bar brush */
202 if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (hbrBar);
203 if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (hbrBk);
209 /***********************************************************************
211 * Draw the progress bar. The background need not be erased.
212 * If dc!=0, it draws on it
214 static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
217 if (hdc) return PROGRESS_Draw (infoPtr, hdc);
218 hdc = BeginPaint (infoPtr->Self, &ps);
219 PROGRESS_Draw (infoPtr, hdc);
220 EndPaint (infoPtr->Self, &ps);
225 /***********************************************************************
227 * Makes sure the current position (CurVal) is within bounds.
229 static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
231 if(infoPtr->CurVal < infoPtr->MinVal)
232 infoPtr->CurVal = infoPtr->MinVal;
233 if(infoPtr->CurVal > infoPtr->MaxVal)
234 infoPtr->CurVal = infoPtr->MaxVal;
238 /***********************************************************************
240 * Set new Font for progress bar
242 static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
244 HFONT hOldFont = infoPtr->Font;
245 infoPtr->Font = hFont;
246 /* Since infoPtr->Font is not used, there is no need for repaint */
250 static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
252 DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));
254 /* if nothing changes, simply return */
255 if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
257 infoPtr->MinVal = low;
258 infoPtr->MaxVal = high;
259 PROGRESS_CoercePos(infoPtr);
260 InvalidateRect(infoPtr->Self, NULL, TRUE);
264 /***********************************************************************
267 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
268 WPARAM wParam, LPARAM lParam)
270 PROGRESS_INFO *infoPtr;
272 TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
274 infoPtr = (PROGRESS_INFO *)GetWindowLongW(hwnd, 0);
276 if (!infoPtr && message != WM_CREATE)
277 return DefWindowProcW( hwnd, message, wParam, lParam );
282 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
283 dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
284 dwExStyle |= WS_EX_STATICEDGE;
285 SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
286 /* Force recalculation of a non-client area */
287 SetWindowPos(hwnd, 0, 0, 0, 0, 0,
288 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
290 /* allocate memory for info struct */
291 infoPtr = (PROGRESS_INFO *)COMCTL32_Alloc (sizeof(PROGRESS_INFO));
292 if (!infoPtr) return -1;
293 SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
295 /* initialize the info struct */
296 infoPtr->Self = hwnd;
298 infoPtr->MaxVal = 100;
301 infoPtr->ColorBar = CLR_DEFAULT;
302 infoPtr->ColorBk = CLR_DEFAULT;
304 TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
309 TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
310 COMCTL32_Free (infoPtr);
311 SetWindowLongW(hwnd, 0, 0);
315 return (LRESULT)infoPtr->Font;
318 return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
321 return PROGRESS_Paint (infoPtr, (HDC)wParam);
326 oldVal = infoPtr->CurVal;
328 infoPtr->CurVal += (INT)wParam;
329 PROGRESS_CoercePos (infoPtr);
330 TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
331 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
339 oldVal = infoPtr->CurVal;
340 if(oldVal != wParam) {
341 infoPtr->CurVal = (INT)wParam;
342 PROGRESS_CoercePos(infoPtr);
343 TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
344 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
350 return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
355 oldStep = infoPtr->Step;
356 infoPtr->Step = (INT)wParam;
363 oldVal = infoPtr->CurVal;
364 infoPtr->CurVal += infoPtr->Step;
365 if(infoPtr->CurVal > infoPtr->MaxVal)
366 infoPtr->CurVal = infoPtr->MinVal;
367 if(oldVal != infoPtr->CurVal)
369 TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
370 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
376 return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
380 ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
381 ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
383 return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
386 return infoPtr->CurVal;
388 case PBM_SETBARCOLOR:
389 infoPtr->ColorBar = (COLORREF)lParam;
390 InvalidateRect(hwnd, NULL, TRUE);
394 infoPtr->ColorBk = (COLORREF)lParam;
395 InvalidateRect(hwnd, NULL, TRUE);
399 if ((message >= WM_USER) && (message < WM_APP))
400 ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
401 return DefWindowProcW( hwnd, message, wParam, lParam );
406 /***********************************************************************
407 * PROGRESS_Register [Internal]
409 * Registers the progress bar window class.
411 VOID PROGRESS_Register (void)
415 ZeroMemory (&wndClass, sizeof(wndClass));
416 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
417 wndClass.lpfnWndProc = (WNDPROC)ProgressWindowProc;
418 wndClass.cbClsExtra = 0;
419 wndClass.cbWndExtra = sizeof (PROGRESS_INFO *);
420 wndClass.hCursor = LoadCursorW (0, IDC_ARROWW);
421 wndClass.lpszClassName = PROGRESS_CLASSW;
423 RegisterClassW (&wndClass);
427 /***********************************************************************
428 * PROGRESS_Unregister [Internal]
430 * Unregisters the progress bar window class.
432 VOID PROGRESS_Unregister (void)
434 UnregisterClassW (PROGRESS_CLASSW, NULL);