Correct errors with move to kernel time functions.
[wine/multimedia.git] / dlls / comctl32 / progress.c
blob8b0e56410afb7c2fc96da60d187f35766fbc7d3c
1 /*
2 * Progress control
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
21 * NOTE
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.
30 * TODO
31 * --support PBS_MARQUE
35 #include <stdarg.h>
36 #include <string.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "winnls.h"
42 #include "commctrl.h"
43 #include "comctl32.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(progress);
48 typedef struct
50 HWND Self; /* The window handle for this control */
51 INT CurVal; /* Current progress value */
52 INT MinVal; /* Minimum progress value */
53 INT MaxVal; /* Maximum progress value */
54 INT Step; /* Step to use on PMB_STEPIT */
55 COLORREF ColorBar; /* Bar color */
56 COLORREF ColorBk; /* Background color */
57 HFONT Font; /* Handle to font (not unused) */
58 } PROGRESS_INFO;
60 /* Control configuration constants */
62 #define LED_GAP 2
64 /***********************************************************************
65 * PROGRESS_Invalidate
67 * Invalide the range between old and new pos.
69 static void PROGRESS_Invalidate( PROGRESS_INFO *infoPtr, INT old, INT new )
71 LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
72 RECT rect;
73 int oldPos, newPos, ledWidth;
75 GetClientRect (infoPtr->Self, &rect);
76 InflateRect(&rect, -1, -1);
78 if (style & PBS_VERTICAL)
80 oldPos = rect.bottom - MulDiv (old - infoPtr->MinVal, rect.bottom - rect.top,
81 infoPtr->MaxVal - infoPtr->MinVal);
82 newPos = rect.bottom - MulDiv (new - infoPtr->MinVal, rect.bottom - rect.top,
83 infoPtr->MaxVal - infoPtr->MinVal);
84 ledWidth = MulDiv (rect.right - rect.left, 2, 3);
85 rect.top = min( oldPos, newPos );
86 rect.bottom = max( oldPos, newPos );
87 if (!(style & PBS_SMOOTH)) rect.top -= ledWidth;
88 InvalidateRect( infoPtr->Self, &rect, oldPos < newPos );
90 else
92 oldPos = rect.left + MulDiv (old - infoPtr->MinVal, rect.right - rect.left,
93 infoPtr->MaxVal - infoPtr->MinVal);
94 newPos = rect.left + MulDiv (new - infoPtr->MinVal, rect.right - rect.left,
95 infoPtr->MaxVal - infoPtr->MinVal);
96 ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
97 rect.left = min( oldPos, newPos );
98 rect.right = max( oldPos, newPos );
99 if (!(style & PBS_SMOOTH)) rect.right += ledWidth;
100 InvalidateRect( infoPtr->Self, &rect, oldPos > newPos );
105 /***********************************************************************
106 * PROGRESS_Draw
107 * Draws the progress bar.
109 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
111 HBRUSH hbrBar, hbrBk;
112 int rightBar, rightMost, ledWidth;
113 RECT rect;
114 DWORD dwStyle;
116 TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);
118 /* get the required bar brush */
119 if (infoPtr->ColorBar == CLR_DEFAULT)
120 hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
121 else
122 hbrBar = CreateSolidBrush (infoPtr->ColorBar);
124 if (infoPtr->ColorBk == CLR_DEFAULT)
125 hbrBk = GetSysColorBrush(COLOR_3DFACE);
126 else
127 hbrBk = CreateSolidBrush(infoPtr->ColorBk);
129 /* get client rectangle */
130 GetClientRect (infoPtr->Self, &rect);
131 FrameRect( hdc, &rect, hbrBk );
132 InflateRect(&rect, -1, -1);
134 /* get the window style */
135 dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
137 /* compute extent of progress bar */
138 if (dwStyle & PBS_VERTICAL) {
139 rightBar = rect.bottom -
140 MulDiv (infoPtr->CurVal - infoPtr->MinVal,
141 rect.bottom - rect.top,
142 infoPtr->MaxVal - infoPtr->MinVal);
143 ledWidth = MulDiv (rect.right - rect.left, 2, 3);
144 rightMost = rect.top;
145 } else {
146 rightBar = rect.left +
147 MulDiv (infoPtr->CurVal - infoPtr->MinVal,
148 rect.right - rect.left,
149 infoPtr->MaxVal - infoPtr->MinVal);
150 ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
151 rightMost = rect.right;
154 /* now draw the bar */
155 if (dwStyle & PBS_SMOOTH)
157 if (dwStyle & PBS_VERTICAL)
159 INT old_top = rect.top;
160 rect.top = rightBar;
161 FillRect(hdc, &rect, hbrBar);
162 rect.bottom = rect.top;
163 rect.top = old_top;
164 FillRect(hdc, &rect, hbrBk);
166 else
168 INT old_right = rect.right;
169 rect.right = rightBar;
170 FillRect(hdc, &rect, hbrBar);
171 rect.left = rect.right;
172 rect.right = old_right;
173 FillRect(hdc, &rect, hbrBk);
175 } else {
176 if (dwStyle & PBS_VERTICAL) {
177 while(rect.bottom > rightBar) {
178 rect.top = rect.bottom - ledWidth;
179 if (rect.top < rightMost)
180 rect.top = rightMost;
181 FillRect(hdc, &rect, hbrBar);
182 rect.bottom = rect.top;
183 rect.top -= LED_GAP;
184 if (rect.top <= rightBar) break;
185 FillRect(hdc, &rect, hbrBk);
186 rect.bottom = rect.top;
188 rect.top = rightMost;
189 FillRect(hdc, &rect, hbrBk);
190 } else {
191 while(rect.left < rightBar) {
192 rect.right = rect.left + ledWidth;
193 if (rect.right > rightMost)
194 rect.right = rightMost;
195 FillRect(hdc, &rect, hbrBar);
196 rect.left = rect.right;
197 rect.right += LED_GAP;
198 if (rect.right >= rightBar) break;
199 FillRect(hdc, &rect, hbrBk);
200 rect.left = rect.right;
202 rect.right = rightMost;
203 FillRect(hdc, &rect, hbrBk);
207 /* delete bar brush */
208 if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (hbrBar);
209 if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (hbrBk);
211 return 0;
215 /***********************************************************************
216 * PROGRESS_Paint
217 * Draw the progress bar. The background need not be erased.
218 * If dc!=0, it draws on it
220 static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
222 PAINTSTRUCT ps;
223 if (hdc) return PROGRESS_Draw (infoPtr, hdc);
224 hdc = BeginPaint (infoPtr->Self, &ps);
225 PROGRESS_Draw (infoPtr, hdc);
226 EndPaint (infoPtr->Self, &ps);
227 return 0;
231 /***********************************************************************
232 * PROGRESS_CoercePos
233 * Makes sure the current position (CurVal) is within bounds.
235 static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
237 if(infoPtr->CurVal < infoPtr->MinVal)
238 infoPtr->CurVal = infoPtr->MinVal;
239 if(infoPtr->CurVal > infoPtr->MaxVal)
240 infoPtr->CurVal = infoPtr->MaxVal;
244 /***********************************************************************
245 * PROGRESS_SetFont
246 * Set new Font for progress bar
248 static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
250 HFONT hOldFont = infoPtr->Font;
251 infoPtr->Font = hFont;
252 /* Since infoPtr->Font is not used, there is no need for repaint */
253 return hOldFont;
256 static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
258 DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));
260 /* if nothing changes, simply return */
261 if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
263 infoPtr->MinVal = low;
264 infoPtr->MaxVal = high;
265 PROGRESS_CoercePos(infoPtr);
266 InvalidateRect(infoPtr->Self, NULL, TRUE);
267 return res;
270 /***********************************************************************
271 * ProgressWindowProc
273 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
274 WPARAM wParam, LPARAM lParam)
276 PROGRESS_INFO *infoPtr;
278 TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
280 infoPtr = (PROGRESS_INFO *)GetWindowLongW(hwnd, 0);
282 if (!infoPtr && message != WM_CREATE)
283 return DefWindowProcW( hwnd, message, wParam, lParam );
285 switch(message) {
286 case WM_CREATE:
288 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
289 dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
290 dwExStyle |= WS_EX_STATICEDGE;
291 SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
292 /* Force recalculation of a non-client area */
293 SetWindowPos(hwnd, 0, 0, 0, 0, 0,
294 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
296 /* allocate memory for info struct */
297 infoPtr = (PROGRESS_INFO *)Alloc (sizeof(PROGRESS_INFO));
298 if (!infoPtr) return -1;
299 SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
301 /* initialize the info struct */
302 infoPtr->Self = hwnd;
303 infoPtr->MinVal = 0;
304 infoPtr->MaxVal = 100;
305 infoPtr->CurVal = 0;
306 infoPtr->Step = 10;
307 infoPtr->ColorBar = CLR_DEFAULT;
308 infoPtr->ColorBk = CLR_DEFAULT;
309 infoPtr->Font = 0;
310 TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
311 return 0;
314 case WM_DESTROY:
315 TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
316 Free (infoPtr);
317 SetWindowLongW(hwnd, 0, 0);
318 return 0;
320 case WM_GETFONT:
321 return (LRESULT)infoPtr->Font;
323 case WM_SETFONT:
324 return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
326 case WM_PAINT:
327 return PROGRESS_Paint (infoPtr, (HDC)wParam);
329 case PBM_DELTAPOS:
331 INT oldVal;
332 oldVal = infoPtr->CurVal;
333 if(wParam != 0) {
334 infoPtr->CurVal += (INT)wParam;
335 PROGRESS_CoercePos (infoPtr);
336 TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
337 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
339 return oldVal;
342 case PBM_SETPOS:
344 UINT oldVal;
345 oldVal = infoPtr->CurVal;
346 if(oldVal != wParam) {
347 infoPtr->CurVal = (INT)wParam;
348 PROGRESS_CoercePos(infoPtr);
349 TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
350 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
352 return oldVal;
355 case PBM_SETRANGE:
356 return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
358 case PBM_SETSTEP:
360 INT oldStep;
361 oldStep = infoPtr->Step;
362 infoPtr->Step = (INT)wParam;
363 return oldStep;
366 case PBM_STEPIT:
368 INT oldVal;
369 oldVal = infoPtr->CurVal;
370 infoPtr->CurVal += infoPtr->Step;
371 if(infoPtr->CurVal > infoPtr->MaxVal)
372 infoPtr->CurVal = infoPtr->MinVal;
373 if(oldVal != infoPtr->CurVal)
375 TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
376 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
378 return oldVal;
381 case PBM_SETRANGE32:
382 return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
384 case PBM_GETRANGE:
385 if (lParam) {
386 ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
387 ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
389 return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
391 case PBM_GETPOS:
392 return infoPtr->CurVal;
394 case PBM_SETBARCOLOR:
395 infoPtr->ColorBar = (COLORREF)lParam;
396 InvalidateRect(hwnd, NULL, TRUE);
397 return 0;
399 case PBM_SETBKCOLOR:
400 infoPtr->ColorBk = (COLORREF)lParam;
401 InvalidateRect(hwnd, NULL, TRUE);
402 return 0;
404 default:
405 if ((message >= WM_USER) && (message < WM_APP))
406 ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
407 return DefWindowProcW( hwnd, message, wParam, lParam );
412 /***********************************************************************
413 * PROGRESS_Register [Internal]
415 * Registers the progress bar window class.
417 VOID PROGRESS_Register (void)
419 WNDCLASSW wndClass;
421 ZeroMemory (&wndClass, sizeof(wndClass));
422 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
423 wndClass.lpfnWndProc = (WNDPROC)ProgressWindowProc;
424 wndClass.cbClsExtra = 0;
425 wndClass.cbWndExtra = sizeof (PROGRESS_INFO *);
426 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
427 wndClass.lpszClassName = PROGRESS_CLASSW;
429 RegisterClassW (&wndClass);
433 /***********************************************************************
434 * PROGRESS_Unregister [Internal]
436 * Unregisters the progress bar window class.
438 VOID PROGRESS_Unregister (void)
440 UnregisterClassW (PROGRESS_CLASSW, NULL);