1 /* Unit tests for the progress bar control.
2  *
3  * Copyright 2005 Michael Kaufmann
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include <stdarg.h>
21 
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winuser.h"
26 #include "commctrl.h"
27 
28 #include "wine/test.h"
29 
30 #include "v6util.h"
31 
32 static HWND hProgressParentWnd;
33 static const char progressTestClass[] = "ProgressBarTestClass";
34 static BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
35 
36 static HWND create_progress(DWORD style)
37 {
38     return CreateWindowExA(0, PROGRESS_CLASSA, "", WS_VISIBLE | style,
39       0, 0, 100, 20, NULL, NULL, GetModuleHandleA(NULL), 0);
40 }
41 
42 /* try to make sure pending X events have been processed before continuing */
43 static void flush_events(void)
44 {
45     MSG msg;
46     int diff = 100;
47     DWORD time = GetTickCount() + diff;
48 
49     while (diff > 0)
50     {
51         if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min(10,diff), QS_ALLINPUT ) == WAIT_TIMEOUT) break;
52         while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
53         diff = time - GetTickCount();
54     }
55 }
56 
57 static LRESULT CALLBACK progress_test_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
58 {
59     switch(msg) {
60 
61     case WM_DESTROY:
62         PostQuitMessage(0);
63         break;
64 
65     default:
66         return DefWindowProcA(hWnd, msg, wParam, lParam);
67     }
68 
69     return 0L;
70 }
71 
72 static WNDPROC progress_wndproc;
73 static BOOL erased;
74 static RECT last_paint_rect;
75 
76 static LRESULT CALLBACK progress_subclass_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
77 {
78     if (msg == WM_PAINT)
79     {
80         GetUpdateRect(hWnd, &last_paint_rect, FALSE);
81     }
82     else if (msg == WM_ERASEBKGND)
83     {
84         erased = TRUE;
85     }
86     return CallWindowProcA(progress_wndproc, hWnd, msg, wParam, lParam);
87 }
88 
89 
90 static void update_window(HWND hWnd)
91 {
92     UpdateWindow(hWnd);
93     ok(!GetUpdateRect(hWnd, NULL, FALSE), "GetUpdateRect must return zero after UpdateWindow\n");
94 }
95 
96 
97 static void init(void)
98 {
99     WNDCLASSA wc;
100     RECT rect;
101     BOOL ret;
102 
103     wc.style = CS_HREDRAW | CS_VREDRAW;
104     wc.cbClsExtra = 0;
105     wc.cbWndExtra = 0;
106     wc.hInstance = GetModuleHandleA(NULL);
107     wc.hIcon = NULL;
108     wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
109     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
110     wc.lpszMenuName = NULL;
111     wc.lpszClassName = progressTestClass;
112     wc.lpfnWndProc = progress_test_wnd_proc;
113     RegisterClassA(&wc);
114 
115     SetRect(&rect, 0, 0, 400, 20);
116     ret = AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
117     ok(ret, "got %d\n", ret);
118 
119     hProgressParentWnd = CreateWindowExA(0, progressTestClass, "Progress Bar Test", WS_OVERLAPPEDWINDOW,
120       CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, GetModuleHandleA(NULL), 0);
121     ok(hProgressParentWnd != NULL, "failed to create parent wnd\n");
122 
123 }
124 
125 static void cleanup(void)
126 {
127     MSG msg;
128 
129     PostMessageA(hProgressParentWnd, WM_CLOSE, 0, 0);
130     while (GetMessageA(&msg,0,0,0)) {
131         TranslateMessage(&msg);
132         DispatchMessageA(&msg);
133     }
134 
135     UnregisterClassA(progressTestClass, GetModuleHandleA(NULL));
136 }
137 
138 
139 /*
140  * Tests if a progress bar repaints itself immediately when it receives
141  * some specific messages.
142  */
143 static void test_redraw(void)
144 {
145     RECT client_rect, rect;
146     HWND hProgressWnd;
147     LRESULT ret;
148 
149     GetClientRect(hProgressParentWnd, &rect);
150     hProgressWnd = CreateWindowExA(0, PROGRESS_CLASSA, "", WS_CHILD | WS_VISIBLE,
151       0, 0, rect.right, rect.bottom, hProgressParentWnd, NULL, GetModuleHandleA(NULL), 0);
152     ok(hProgressWnd != NULL, "Failed to create progress bar.\n");
153     progress_wndproc = (WNDPROC)SetWindowLongPtrA(hProgressWnd, GWLP_WNDPROC, (LPARAM)progress_subclass_proc);
154 
155     ShowWindow(hProgressParentWnd, SW_SHOWNORMAL);
156     ok(GetUpdateRect(hProgressParentWnd, NULL, FALSE), "GetUpdateRect: There should be a region that needs to be updated\n");
157     flush_events();
158     update_window(hProgressParentWnd);
159 
160     SendMessageA(hProgressWnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
161     SendMessageA(hProgressWnd, PBM_SETPOS, 10, 0);
162     SendMessageA(hProgressWnd, PBM_SETSTEP, 20, 0);
163     update_window(hProgressWnd);
164 
165     /* PBM_SETPOS */
166     ok(SendMessageA(hProgressWnd, PBM_SETPOS, 50, 0) == 10, "PBM_SETPOS must return the previous position\n");
167     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_SETPOS: The progress bar should be redrawn immediately\n");
168 
169     /* PBM_DELTAPOS */
170     ok(SendMessageA(hProgressWnd, PBM_DELTAPOS, 15, 0) == 50, "PBM_DELTAPOS must return the previous position\n");
171     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_DELTAPOS: The progress bar should be redrawn immediately\n");
172 
173     /* PBM_SETPOS */
174     ok(SendMessageA(hProgressWnd, PBM_SETPOS, 80, 0) == 65, "PBM_SETPOS must return the previous position\n");
175     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_SETPOS: The progress bar should be redrawn immediately\n");
176 
177     /* PBM_STEPIT */
178     ok(SendMessageA(hProgressWnd, PBM_STEPIT, 0, 0) == 80, "PBM_STEPIT must return the previous position\n");
179     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_STEPIT: The progress bar should be redrawn immediately\n");
180     ret = SendMessageA(hProgressWnd, PBM_GETPOS, 0, 0);
181     if (ret == 0)
182         win_skip("PBM_GETPOS needs comctl32 > 4.70\n");
183     else
184         ok(ret == 100, "PBM_GETPOS returned a wrong position : %d\n", (UINT)ret);
185 
186     /* PBM_SETRANGE and PBM_SETRANGE32:
187     Usually the progress bar doesn't repaint itself immediately. If the
188     position is not in the new range, it does.
189     Don't test this, it may change in future Windows versions. */
190 
191     SendMessageA(hProgressWnd, PBM_SETPOS, 0, 0);
192     update_window(hProgressWnd);
193 
194     /* increase to 10 - no background erase required */
195     erased = FALSE;
196     SetRectEmpty(&last_paint_rect);
197     SendMessageA(hProgressWnd, PBM_SETPOS, 10, 0);
198     GetClientRect(hProgressWnd, &client_rect);
199     ok(EqualRect(&last_paint_rect, &client_rect), "last_paint_rect was %s instead of %s\n",
200        wine_dbgstr_rect(&last_paint_rect), wine_dbgstr_rect(&client_rect));
201     update_window(hProgressWnd);
202     ok(!erased, "Progress bar shouldn't have erased the background\n");
203 
204     /* decrease to 0 - background erase will be required */
205     erased = FALSE;
206     SetRectEmpty(&last_paint_rect);
207     SendMessageA(hProgressWnd, PBM_SETPOS, 0, 0);
208     GetClientRect(hProgressWnd, &client_rect);
209     ok(EqualRect(&last_paint_rect, &client_rect), "last_paint_rect was %s instead of %s\n",
210        wine_dbgstr_rect(&last_paint_rect), wine_dbgstr_rect(&client_rect));
211     update_window(hProgressWnd);
212     ok(erased, "Progress bar should have erased the background\n");
213 
214     DestroyWindow(hProgressWnd);
215 }
216 
217 static void test_setcolors(void)
218 {
219     HWND progress;
220     COLORREF clr;
221 
222     progress = create_progress(PBS_SMOOTH);
223 
224     clr = SendMessageA(progress, PBM_SETBARCOLOR, 0, 0);
225     ok(clr == CLR_DEFAULT, "got %x\n", clr);
226 
227     clr = SendMessageA(progress, PBM_SETBARCOLOR, 0, RGB(0, 255, 0));
228     ok(clr == 0, "got %x\n", clr);
229 
230     clr = SendMessageA(progress, PBM_SETBARCOLOR, 0, CLR_DEFAULT);
231     ok(clr == RGB(0, 255, 0), "got %x\n", clr);
232 
233     clr = SendMessageA(progress, PBM_SETBKCOLOR, 0, 0);
234     ok(clr == CLR_DEFAULT, "got %x\n", clr);
235 
236     clr = SendMessageA(progress, PBM_SETBKCOLOR, 0, RGB(255, 0, 0));
237     ok(clr == 0, "got %x\n", clr);
238 
239     clr = SendMessageA(progress, PBM_SETBKCOLOR, 0, CLR_DEFAULT);
240     ok(clr == RGB(255, 0, 0), "got %x\n", clr);
241 
242     DestroyWindow(progress);
243 }
244 
245 static void test_PBM_STEPIT(void)
246 {
247     struct stepit_test
248     {
249         int min;
250         int max;
251         int step;
252     } stepit_tests[] =
253     {
254         { 3, 15,  5 },
255         { 3, 15, -5 },
256         { 3, 15, 50 },
257     };
258     HWND progress;
259     int i, j;
260 
261     for (i = 0; i < ARRAY_SIZE(stepit_tests); i++)
262     {
263         struct stepit_test *test = &stepit_tests[i];
264         LRESULT ret;
265 
266         progress = create_progress(0);
267 
268         ret = SendMessageA(progress, PBM_SETRANGE32, test->min, test->max);
269         ok(ret != 0, "Unexpected return value.\n");
270 
271         SendMessageA(progress, PBM_SETPOS, test->min, 0);
272         SendMessageA(progress, PBM_SETSTEP, test->step, 0);
273 
274         for (j = 0; j < test->max; j++)
275         {
276             int pos = SendMessageA(progress, PBM_GETPOS, 0, 0);
277             int current;
278 
279             pos += test->step;
280             if (pos > test->max)
281                 pos = (pos - test->min) % (test->max - test->min) + test->min;
282             if (pos < test->min)
283                 pos = (pos - test->min) % (test->max - test->min) + test->max;
284 
285             SendMessageA(progress, PBM_STEPIT, 0, 0);
286 
287             current = SendMessageA(progress, PBM_GETPOS, 0, 0);
288             ok(current == pos, "Unexpected position %d, expected %d.\n", current, pos);
289         }
290 
291         DestroyWindow(progress);
292     }
293 }
294 
295 static void init_functions(void)
296 {
297     HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
298 
299 #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
300     X(InitCommonControlsEx);
301 #undef X
302 }
303 
304 START_TEST(progress)
305 {
306     INITCOMMONCONTROLSEX iccex;
307     ULONG_PTR ctx_cookie;
308     HANDLE hCtx;
309 
310     init_functions();
311 
312     iccex.dwSize = sizeof(iccex);
313     iccex.dwICC  = ICC_PROGRESS_CLASS;
314     pInitCommonControlsEx(&iccex);
315 
316     init();
317 
318     test_redraw();
319     test_setcolors();
320     test_PBM_STEPIT();
321 
322     if (!load_v6_module(&ctx_cookie, &hCtx))
323         return;
324 
325     test_setcolors();
326     test_PBM_STEPIT();
327 
328     unload_v6_module(ctx_cookie, hCtx);
329 
330     cleanup();
331 }
332