We should callback for inexistent subitems.
[wine/multimedia.git] / dlls / comctl32 / progress.c
blob2c50ddac982068f3aec4460a79eea7b9d3e32042
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 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.
30 * TODO
31 * --support PBS_MARQUE
35 #include <string.h>
36 #include "winbase.h"
37 #include "commctrl.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(progress);
42 typedef struct
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) */
52 } PROGRESS_INFO;
54 /* Control configuration constants */
56 #define LED_GAP 2
58 /***********************************************************************
59 * PROGRESS_Invalidate
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);
66 RECT rect;
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 );
84 else
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 /***********************************************************************
100 * PROGRESS_Draw
101 * Draws the progress bar.
103 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
105 HBRUSH hbrBar, hbrBk;
106 int rightBar, rightMost, ledWidth;
107 RECT rect;
108 DWORD dwStyle;
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);
115 else
116 hbrBar = CreateSolidBrush (infoPtr->ColorBar);
118 if (infoPtr->ColorBk == CLR_DEFAULT)
119 hbrBk = GetSysColorBrush(COLOR_3DFACE);
120 else
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;
139 } else {
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;
154 rect.top = rightBar;
155 FillRect(hdc, &rect, hbrBar);
156 rect.bottom = rect.top;
157 rect.top = old_top;
158 FillRect(hdc, &rect, hbrBk);
160 else
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);
169 } else {
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;
177 rect.top -= LED_GAP;
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);
184 } else {
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);
205 return 0;
209 /***********************************************************************
210 * PROGRESS_Paint
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)
216 PAINTSTRUCT ps;
217 if (hdc) return PROGRESS_Draw (infoPtr, hdc);
218 hdc = BeginPaint (infoPtr->Self, &ps);
219 PROGRESS_Draw (infoPtr, hdc);
220 EndPaint (infoPtr->Self, &ps);
221 return 0;
225 /***********************************************************************
226 * PROGRESS_CoercePos
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 /***********************************************************************
239 * PROGRESS_SetFont
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 */
247 return hOldFont;
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 return res;
263 /***********************************************************************
264 * ProgressWindowProc
266 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
267 WPARAM wParam, LPARAM lParam)
269 PROGRESS_INFO *infoPtr;
271 TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
273 infoPtr = (PROGRESS_INFO *)GetWindowLongW(hwnd, 0);
275 if (!infoPtr && message != WM_CREATE)
276 return DefWindowProcW( hwnd, message, wParam, lParam );
278 switch(message) {
279 case WM_CREATE:
281 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
282 dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
283 dwExStyle |= WS_EX_STATICEDGE;
284 SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
285 /* Force recalculation of a non-client area */
286 SetWindowPos(hwnd, 0, 0, 0, 0, 0,
287 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
289 /* allocate memory for info struct */
290 infoPtr = (PROGRESS_INFO *)COMCTL32_Alloc (sizeof(PROGRESS_INFO));
291 if (!infoPtr) return -1;
292 SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
294 /* initialize the info struct */
295 infoPtr->Self = hwnd;
296 infoPtr->MinVal = 0;
297 infoPtr->MaxVal = 100;
298 infoPtr->CurVal = 0;
299 infoPtr->Step = 10;
300 infoPtr->ColorBar = CLR_DEFAULT;
301 infoPtr->ColorBk = CLR_DEFAULT;
302 infoPtr->Font = 0;
303 TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
304 return 0;
307 case WM_DESTROY:
308 TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
309 COMCTL32_Free (infoPtr);
310 SetWindowLongW(hwnd, 0, 0);
311 return 0;
313 case WM_GETFONT:
314 return (LRESULT)infoPtr->Font;
316 case WM_SETFONT:
317 return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
319 case WM_PAINT:
320 return PROGRESS_Paint (infoPtr, (HDC)wParam);
322 case PBM_DELTAPOS:
324 INT oldVal;
325 oldVal = infoPtr->CurVal;
326 if(wParam != 0) {
327 infoPtr->CurVal += (INT)wParam;
328 PROGRESS_CoercePos (infoPtr);
329 TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
330 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
332 return oldVal;
335 case PBM_SETPOS:
337 INT oldVal;
338 oldVal = infoPtr->CurVal;
339 if(oldVal != wParam) {
340 infoPtr->CurVal = (INT)wParam;
341 PROGRESS_CoercePos(infoPtr);
342 TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
343 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
345 return oldVal;
348 case PBM_SETRANGE:
349 return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
351 case PBM_SETSTEP:
353 INT oldStep;
354 oldStep = infoPtr->Step;
355 infoPtr->Step = (INT)wParam;
356 return oldStep;
359 case PBM_STEPIT:
361 INT oldVal;
362 oldVal = infoPtr->CurVal;
363 infoPtr->CurVal += infoPtr->Step;
364 if(infoPtr->CurVal > infoPtr->MaxVal)
365 infoPtr->CurVal = infoPtr->MinVal;
366 if(oldVal != infoPtr->CurVal)
368 TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
369 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
371 return oldVal;
374 case PBM_SETRANGE32:
375 return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
377 case PBM_GETRANGE:
378 if (lParam) {
379 ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
380 ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
382 return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
384 case PBM_GETPOS:
385 return infoPtr->CurVal;
387 case PBM_SETBARCOLOR:
388 infoPtr->ColorBar = (COLORREF)lParam;
389 InvalidateRect(hwnd, NULL, TRUE);
390 return 0;
392 case PBM_SETBKCOLOR:
393 infoPtr->ColorBk = (COLORREF)lParam;
394 InvalidateRect(hwnd, NULL, TRUE);
395 return 0;
397 default:
398 if ((message >= WM_USER) && (message < WM_APP))
399 ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
400 return DefWindowProcW( hwnd, message, wParam, lParam );
405 /***********************************************************************
406 * PROGRESS_Register [Internal]
408 * Registers the progress bar window class.
410 VOID PROGRESS_Register (void)
412 WNDCLASSW wndClass;
414 ZeroMemory (&wndClass, sizeof(wndClass));
415 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
416 wndClass.lpfnWndProc = (WNDPROC)ProgressWindowProc;
417 wndClass.cbClsExtra = 0;
418 wndClass.cbWndExtra = sizeof (PROGRESS_INFO *);
419 wndClass.hCursor = LoadCursorW (0, IDC_ARROWW);
420 wndClass.lpszClassName = PROGRESS_CLASSW;
422 RegisterClassW (&wndClass);
426 /***********************************************************************
427 * PROGRESS_Unregister [Internal]
429 * Unregisters the progress bar window class.
431 VOID PROGRESS_Unregister (void)
433 UnregisterClassW (PROGRESS_CLASSW, (HINSTANCE)NULL);