Fixed header dependencies to be fully compatible with the Windows
[wine/multimedia.git] / dlls / comctl32 / progress.c
blob0a10b92926d0f1da061f7a2a9b5a53818e63b598
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 <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 "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(progress);
47 typedef struct
49 HWND Self; /* The window handle for this control */
50 INT CurVal; /* Current progress value */
51 INT MinVal; /* Minimum progress value */
52 INT MaxVal; /* Maximum progress value */
53 INT Step; /* Step to use on PMB_STEPIT */
54 COLORREF ColorBar; /* Bar color */
55 COLORREF ColorBk; /* Background color */
56 HFONT Font; /* Handle to font (not unused) */
57 } PROGRESS_INFO;
59 /* Control configuration constants */
61 #define LED_GAP 2
63 /***********************************************************************
64 * PROGRESS_Invalidate
66 * Invalide the range between old and new pos.
68 static void PROGRESS_Invalidate( PROGRESS_INFO *infoPtr, INT old, INT new )
70 LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
71 RECT rect;
72 int oldPos, newPos, ledWidth;
74 GetClientRect (infoPtr->Self, &rect);
75 InflateRect(&rect, -1, -1);
77 if (style & PBS_VERTICAL)
79 oldPos = rect.bottom - MulDiv (old - infoPtr->MinVal, rect.bottom - rect.top,
80 infoPtr->MaxVal - infoPtr->MinVal);
81 newPos = rect.bottom - MulDiv (new - infoPtr->MinVal, rect.bottom - rect.top,
82 infoPtr->MaxVal - infoPtr->MinVal);
83 ledWidth = MulDiv (rect.right - rect.left, 2, 3);
84 rect.top = min( oldPos, newPos );
85 rect.bottom = max( oldPos, newPos );
86 if (!(style & PBS_SMOOTH)) rect.top -= ledWidth;
87 InvalidateRect( infoPtr->Self, &rect, oldPos < newPos );
89 else
91 oldPos = rect.left + MulDiv (old - infoPtr->MinVal, rect.right - rect.left,
92 infoPtr->MaxVal - infoPtr->MinVal);
93 newPos = rect.left + MulDiv (new - infoPtr->MinVal, rect.right - rect.left,
94 infoPtr->MaxVal - infoPtr->MinVal);
95 ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
96 rect.left = min( oldPos, newPos );
97 rect.right = max( oldPos, newPos );
98 if (!(style & PBS_SMOOTH)) rect.right += ledWidth;
99 InvalidateRect( infoPtr->Self, &rect, oldPos > newPos );
104 /***********************************************************************
105 * PROGRESS_Draw
106 * Draws the progress bar.
108 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
110 HBRUSH hbrBar, hbrBk;
111 int rightBar, rightMost, ledWidth;
112 RECT rect;
113 DWORD dwStyle;
115 TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);
117 /* get the required bar brush */
118 if (infoPtr->ColorBar == CLR_DEFAULT)
119 hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
120 else
121 hbrBar = CreateSolidBrush (infoPtr->ColorBar);
123 if (infoPtr->ColorBk == CLR_DEFAULT)
124 hbrBk = GetSysColorBrush(COLOR_3DFACE);
125 else
126 hbrBk = CreateSolidBrush(infoPtr->ColorBk);
128 /* get client rectangle */
129 GetClientRect (infoPtr->Self, &rect);
130 FrameRect( hdc, &rect, hbrBk );
131 InflateRect(&rect, -1, -1);
133 /* get the window style */
134 dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
136 /* compute extent of progress bar */
137 if (dwStyle & PBS_VERTICAL) {
138 rightBar = rect.bottom -
139 MulDiv (infoPtr->CurVal - infoPtr->MinVal,
140 rect.bottom - rect.top,
141 infoPtr->MaxVal - infoPtr->MinVal);
142 ledWidth = MulDiv (rect.right - rect.left, 2, 3);
143 rightMost = rect.top;
144 } else {
145 rightBar = rect.left +
146 MulDiv (infoPtr->CurVal - infoPtr->MinVal,
147 rect.right - rect.left,
148 infoPtr->MaxVal - infoPtr->MinVal);
149 ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
150 rightMost = rect.right;
153 /* now draw the bar */
154 if (dwStyle & PBS_SMOOTH)
156 if (dwStyle & PBS_VERTICAL)
158 INT old_top = rect.top;
159 rect.top = rightBar;
160 FillRect(hdc, &rect, hbrBar);
161 rect.bottom = rect.top;
162 rect.top = old_top;
163 FillRect(hdc, &rect, hbrBk);
165 else
167 INT old_right = rect.right;
168 rect.right = rightBar;
169 FillRect(hdc, &rect, hbrBar);
170 rect.left = rect.right;
171 rect.right = old_right;
172 FillRect(hdc, &rect, hbrBk);
174 } else {
175 if (dwStyle & PBS_VERTICAL) {
176 while(rect.bottom > rightBar) {
177 rect.top = rect.bottom - ledWidth;
178 if (rect.top < rightMost)
179 rect.top = rightMost;
180 FillRect(hdc, &rect, hbrBar);
181 rect.bottom = rect.top;
182 rect.top -= LED_GAP;
183 if (rect.top <= rightBar) break;
184 FillRect(hdc, &rect, hbrBk);
185 rect.bottom = rect.top;
187 rect.top = rightMost;
188 FillRect(hdc, &rect, hbrBk);
189 } else {
190 while(rect.left < rightBar) {
191 rect.right = rect.left + ledWidth;
192 if (rect.right > rightMost)
193 rect.right = rightMost;
194 FillRect(hdc, &rect, hbrBar);
195 rect.left = rect.right;
196 rect.right += LED_GAP;
197 if (rect.right >= rightBar) break;
198 FillRect(hdc, &rect, hbrBk);
199 rect.left = rect.right;
201 rect.right = rightMost;
202 FillRect(hdc, &rect, hbrBk);
206 /* delete bar brush */
207 if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (hbrBar);
208 if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (hbrBk);
210 return 0;
214 /***********************************************************************
215 * PROGRESS_Paint
216 * Draw the progress bar. The background need not be erased.
217 * If dc!=0, it draws on it
219 static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
221 PAINTSTRUCT ps;
222 if (hdc) return PROGRESS_Draw (infoPtr, hdc);
223 hdc = BeginPaint (infoPtr->Self, &ps);
224 PROGRESS_Draw (infoPtr, hdc);
225 EndPaint (infoPtr->Self, &ps);
226 return 0;
230 /***********************************************************************
231 * PROGRESS_CoercePos
232 * Makes sure the current position (CurVal) is within bounds.
234 static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
236 if(infoPtr->CurVal < infoPtr->MinVal)
237 infoPtr->CurVal = infoPtr->MinVal;
238 if(infoPtr->CurVal > infoPtr->MaxVal)
239 infoPtr->CurVal = infoPtr->MaxVal;
243 /***********************************************************************
244 * PROGRESS_SetFont
245 * Set new Font for progress bar
247 static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
249 HFONT hOldFont = infoPtr->Font;
250 infoPtr->Font = hFont;
251 /* Since infoPtr->Font is not used, there is no need for repaint */
252 return hOldFont;
255 static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
257 DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));
259 /* if nothing changes, simply return */
260 if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
262 infoPtr->MinVal = low;
263 infoPtr->MaxVal = high;
264 PROGRESS_CoercePos(infoPtr);
265 InvalidateRect(infoPtr->Self, NULL, TRUE);
266 return res;
269 /***********************************************************************
270 * ProgressWindowProc
272 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
273 WPARAM wParam, LPARAM lParam)
275 PROGRESS_INFO *infoPtr;
277 TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
279 infoPtr = (PROGRESS_INFO *)GetWindowLongW(hwnd, 0);
281 if (!infoPtr && message != WM_CREATE)
282 return DefWindowProcW( hwnd, message, wParam, lParam );
284 switch(message) {
285 case WM_CREATE:
287 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
288 dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
289 dwExStyle |= WS_EX_STATICEDGE;
290 SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
291 /* Force recalculation of a non-client area */
292 SetWindowPos(hwnd, 0, 0, 0, 0, 0,
293 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
295 /* allocate memory for info struct */
296 infoPtr = (PROGRESS_INFO *)COMCTL32_Alloc (sizeof(PROGRESS_INFO));
297 if (!infoPtr) return -1;
298 SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
300 /* initialize the info struct */
301 infoPtr->Self = hwnd;
302 infoPtr->MinVal = 0;
303 infoPtr->MaxVal = 100;
304 infoPtr->CurVal = 0;
305 infoPtr->Step = 10;
306 infoPtr->ColorBar = CLR_DEFAULT;
307 infoPtr->ColorBk = CLR_DEFAULT;
308 infoPtr->Font = 0;
309 TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
310 return 0;
313 case WM_DESTROY:
314 TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
315 COMCTL32_Free (infoPtr);
316 SetWindowLongW(hwnd, 0, 0);
317 return 0;
319 case WM_GETFONT:
320 return (LRESULT)infoPtr->Font;
322 case WM_SETFONT:
323 return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
325 case WM_PAINT:
326 return PROGRESS_Paint (infoPtr, (HDC)wParam);
328 case PBM_DELTAPOS:
330 INT oldVal;
331 oldVal = infoPtr->CurVal;
332 if(wParam != 0) {
333 infoPtr->CurVal += (INT)wParam;
334 PROGRESS_CoercePos (infoPtr);
335 TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
336 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
338 return oldVal;
341 case PBM_SETPOS:
343 UINT oldVal;
344 oldVal = infoPtr->CurVal;
345 if(oldVal != wParam) {
346 infoPtr->CurVal = (INT)wParam;
347 PROGRESS_CoercePos(infoPtr);
348 TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
349 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
351 return oldVal;
354 case PBM_SETRANGE:
355 return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
357 case PBM_SETSTEP:
359 INT oldStep;
360 oldStep = infoPtr->Step;
361 infoPtr->Step = (INT)wParam;
362 return oldStep;
365 case PBM_STEPIT:
367 INT oldVal;
368 oldVal = infoPtr->CurVal;
369 infoPtr->CurVal += infoPtr->Step;
370 if(infoPtr->CurVal > infoPtr->MaxVal)
371 infoPtr->CurVal = infoPtr->MinVal;
372 if(oldVal != infoPtr->CurVal)
374 TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
375 PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
377 return oldVal;
380 case PBM_SETRANGE32:
381 return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
383 case PBM_GETRANGE:
384 if (lParam) {
385 ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
386 ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
388 return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
390 case PBM_GETPOS:
391 return infoPtr->CurVal;
393 case PBM_SETBARCOLOR:
394 infoPtr->ColorBar = (COLORREF)lParam;
395 InvalidateRect(hwnd, NULL, TRUE);
396 return 0;
398 case PBM_SETBKCOLOR:
399 infoPtr->ColorBk = (COLORREF)lParam;
400 InvalidateRect(hwnd, NULL, TRUE);
401 return 0;
403 default:
404 if ((message >= WM_USER) && (message < WM_APP))
405 ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
406 return DefWindowProcW( hwnd, message, wParam, lParam );
411 /***********************************************************************
412 * PROGRESS_Register [Internal]
414 * Registers the progress bar window class.
416 VOID PROGRESS_Register (void)
418 WNDCLASSW wndClass;
420 ZeroMemory (&wndClass, sizeof(wndClass));
421 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
422 wndClass.lpfnWndProc = (WNDPROC)ProgressWindowProc;
423 wndClass.cbClsExtra = 0;
424 wndClass.cbWndExtra = sizeof (PROGRESS_INFO *);
425 wndClass.hCursor = LoadCursorW (0, IDC_ARROWW);
426 wndClass.lpszClassName = PROGRESS_CLASSW;
428 RegisterClassW (&wndClass);
432 /***********************************************************************
433 * PROGRESS_Unregister [Internal]
435 * Unregisters the progress bar window class.
437 VOID PROGRESS_Unregister (void)
439 UnregisterClassW (PROGRESS_CLASSW, NULL);