kernel32: Make CopyFile() call CopyFileEx() instead of the other way around.
[wine] / dlls / shell32 / tests / appbar.c
1 /* Unit tests for appbars
2  *
3  * Copyright 2008 Vincent Povirk for CodeWeavers
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 <windows.h>
23
24 #include "wine/test.h"
25
26 #define MSG_APPBAR WM_APP
27
28 static const CHAR testwindow_class[] = "testwindow";
29
30 static HMONITOR (WINAPI *pMonitorFromWindow)(HWND, DWORD);
31
32 typedef BOOL (*boolean_function)(void);
33
34 struct testwindow_info
35 {
36     HWND hwnd;
37     BOOL registered;
38     BOOL to_be_deleted;
39     RECT desired_rect;
40     UINT edge;
41     RECT allocated_rect;
42 };
43
44 static struct testwindow_info windows[3];
45
46 static int expected_bottom;
47
48 static void testwindow_setpos(HWND hwnd)
49 {
50     struct testwindow_info* info = (struct testwindow_info*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
51     APPBARDATA abd;
52     BOOL ret;
53
54     ok(info != NULL, "got unexpected ABN_POSCHANGED notification\n");
55
56     if (!info || !info->registered)
57     {
58         return;
59     }
60
61     if (info->to_be_deleted)
62     {
63         win_skip("Some Win95 and NT4 systems send messages to removed taskbars\n");
64         return;
65     }
66
67     abd.cbSize = sizeof(abd);
68     abd.hWnd = hwnd;
69     abd.uEdge = info->edge;
70     abd.rc = info->desired_rect;
71     ret = SHAppBarMessage(ABM_QUERYPOS, &abd);
72     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
73     switch (info->edge)
74     {
75         case ABE_BOTTOM:
76             ok(info->desired_rect.top == abd.rc.top, "ABM_QUERYPOS changed top of rect from %i to %i\n", info->desired_rect.top, abd.rc.top);
77             abd.rc.top = abd.rc.bottom - (info->desired_rect.bottom - info->desired_rect.top);
78             break;
79         case ABE_LEFT:
80             ok(info->desired_rect.right == abd.rc.right, "ABM_QUERYPOS changed right of rect from %i to %i\n", info->desired_rect.right, abd.rc.right);
81             abd.rc.right = abd.rc.left + (info->desired_rect.right - info->desired_rect.left);
82             break;
83         case ABE_RIGHT:
84             ok(info->desired_rect.left == abd.rc.left, "ABM_QUERYPOS changed left of rect from %i to %i\n", info->desired_rect.left, abd.rc.left);
85             abd.rc.left = abd.rc.right - (info->desired_rect.right - info->desired_rect.left);
86             break;
87         case ABE_TOP:
88             ok(info->desired_rect.bottom == abd.rc.bottom, "ABM_QUERYPOS changed bottom of rect from %i to %i\n", info->desired_rect.bottom, abd.rc.bottom);
89             abd.rc.bottom = abd.rc.top + (info->desired_rect.bottom - info->desired_rect.top);
90             break;
91     }
92
93     ret = SHAppBarMessage(ABM_SETPOS, &abd);
94     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
95
96     info->allocated_rect = abd.rc;
97     MoveWindow(hwnd, abd.rc.left, abd.rc.top, abd.rc.right-abd.rc.left, abd.rc.bottom-abd.rc.top, TRUE);
98 }
99
100 static LRESULT CALLBACK testwindow_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
101 {
102     switch(msg)
103     {
104         case MSG_APPBAR:
105         {
106             switch(wparam)
107             {
108                 case ABN_POSCHANGED:
109                     testwindow_setpos(hwnd);
110                     break;
111             }
112             return 0;
113         }
114     }
115
116     return DefWindowProc(hwnd, msg, wparam, lparam);
117 }
118
119 /* process pending messages until a condition is true or 3 seconds pass */
120 static void do_events_until(boolean_function test)
121 {
122     MSG msg;
123     UINT_PTR timerid;
124     BOOL timedout=FALSE;
125
126     timerid = SetTimer(0, 0, 3000, NULL);
127
128     while (1)
129     {
130         while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
131         {
132             if (msg.hwnd == 0 && msg.message == WM_TIMER && msg.wParam == timerid)
133                 timedout = TRUE;
134             TranslateMessage(&msg);
135             DispatchMessageA(&msg);
136         }
137         if (timedout || test())
138             break;
139         WaitMessage();
140     }
141
142     KillTimer(0, timerid);
143 }
144
145 /* process any pending messages */
146 static void do_events(void)
147 {
148     MSG msg;
149
150     while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
151     {
152         TranslateMessage(&msg);
153         DispatchMessageA(&msg);
154     }
155 }
156
157 static BOOL no_appbars_intersect(void)
158 {
159     int i, j;
160     RECT rc;
161
162     for (i=0; i<2; i++)
163     {
164         for (j=i+1; j<3; j++)
165         {
166             if (windows[i].registered && windows[j].registered &&
167                 IntersectRect(&rc, &windows[i].allocated_rect, &windows[j].allocated_rect))
168                 return FALSE;
169         }
170     }
171     return TRUE;
172 }
173
174 static BOOL got_expected_bottom(void)
175 {
176     return (no_appbars_intersect() && windows[1].allocated_rect.bottom == expected_bottom);
177 }
178
179 static void register_testwindow_class(void)
180 {
181     WNDCLASSEXA cls;
182
183     ZeroMemory(&cls, sizeof(cls));
184     cls.cbSize = sizeof(cls);
185     cls.style = 0;
186     cls.lpfnWndProc = testwindow_wndproc;
187     cls.hInstance = NULL;
188     cls.hCursor = LoadCursor(0, IDC_ARROW);
189     cls.hbrBackground = (HBRUSH) COLOR_WINDOW;
190     cls.lpszClassName = testwindow_class;
191
192     RegisterClassExA(&cls);
193 }
194
195 #define test_window_rects(a, b) \
196     ok(!IntersectRect(&rc, &windows[a].allocated_rect, &windows[b].allocated_rect), \
197         "rectangles intersect (%i,%i,%i,%i)/(%i,%i,%i,%i)\n", \
198         windows[a].allocated_rect.left, windows[a].allocated_rect.top, windows[a].allocated_rect.right, windows[a].allocated_rect.bottom, \
199         windows[b].allocated_rect.left, windows[b].allocated_rect.top, windows[b].allocated_rect.right, windows[b].allocated_rect.bottom)
200
201 static void test_setpos(void)
202 {
203     APPBARDATA abd;
204     RECT rc;
205     int screen_width, screen_height;
206     BOOL ret;
207     int org_bottom1;
208
209     screen_width = GetSystemMetrics(SM_CXSCREEN);
210     screen_height = GetSystemMetrics(SM_CYSCREEN);
211
212     /* create and register windows[0] */
213     windows[0].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
214         testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
215         NULL, NULL, NULL, NULL);
216     ok(windows[0].hwnd != NULL, "couldn't create window\n");
217     do_events();
218     abd.cbSize = sizeof(abd);
219     abd.hWnd = windows[0].hwnd;
220     abd.uCallbackMessage = MSG_APPBAR;
221     ret = SHAppBarMessage(ABM_NEW, &abd);
222     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
223
224     /* ABM_NEW should return FALSE if the window is already registered */
225     ret = SHAppBarMessage(ABM_NEW, &abd);
226     ok(ret == FALSE, "SHAppBarMessage returned %i\n", ret);
227     do_events();
228
229     /* dock windows[0] to the bottom of the screen */
230     windows[0].registered = TRUE;
231     windows[0].to_be_deleted = FALSE;
232     windows[0].edge = ABE_BOTTOM;
233     windows[0].desired_rect.left = 0;
234     windows[0].desired_rect.right = screen_width;
235     windows[0].desired_rect.top = screen_height - 15;
236     windows[0].desired_rect.bottom = screen_height;
237     SetWindowLongPtr(windows[0].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[0]);
238     testwindow_setpos(windows[0].hwnd);
239     do_events();
240
241     /* create and register windows[1] */
242     windows[1].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
243         testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
244         NULL, NULL, NULL, NULL);
245     ok(windows[1].hwnd != NULL, "couldn't create window\n");
246     abd.hWnd = windows[1].hwnd;
247     ret = SHAppBarMessage(ABM_NEW, &abd);
248     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
249
250     /* dock windows[1] to the bottom of the screen */
251     windows[1].registered = TRUE;
252     windows[1].to_be_deleted = FALSE;
253     windows[1].edge = ABE_BOTTOM;
254     windows[1].desired_rect.left = 0;
255     windows[1].desired_rect.right = screen_width;
256     windows[1].desired_rect.top = screen_height - 10;
257     windows[1].desired_rect.bottom = screen_height;
258     SetWindowLongPtr(windows[1].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[1]);
259     testwindow_setpos(windows[1].hwnd);
260
261     /* the windows are adjusted to they don't overlap */
262     do_events_until(no_appbars_intersect);
263     test_window_rects(0, 1);
264
265     /* make windows[0] larger, forcing windows[1] to move out of its way */
266     windows[0].desired_rect.top = screen_height - 20;
267     testwindow_setpos(windows[0].hwnd);
268     do_events_until(no_appbars_intersect);
269     test_window_rects(0, 1);
270
271     /* create and register windows[2] */
272     windows[2].hwnd = CreateWindowExA(WS_EX_TOOLWINDOW|WS_EX_TOPMOST,
273         testwindow_class, testwindow_class, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0,
274         NULL, NULL, NULL, NULL);
275     ok(windows[2].hwnd != NULL, "couldn't create window\n");
276     do_events();
277
278     abd.hWnd = windows[2].hwnd;
279     ret = SHAppBarMessage(ABM_NEW, &abd);
280     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
281
282     /* dock windows[2] to the bottom of the screen */
283     windows[2].registered = TRUE;
284     windows[2].to_be_deleted = FALSE;
285     windows[2].edge = ABE_BOTTOM;
286     windows[2].desired_rect.left = 0;
287     windows[2].desired_rect.right = screen_width;
288     windows[2].desired_rect.top = screen_height - 10;
289     windows[2].desired_rect.bottom = screen_height;
290     SetWindowLongPtr(windows[2].hwnd, GWLP_USERDATA, (LONG_PTR)&windows[2]);
291     testwindow_setpos(windows[2].hwnd);
292
293     do_events_until(no_appbars_intersect);
294     test_window_rects(0, 1);
295     test_window_rects(0, 2);
296     test_window_rects(1, 2);
297
298     /* move windows[2] to the right side of the screen */
299     windows[2].edge = ABE_RIGHT;
300     windows[2].desired_rect.left = screen_width - 15;
301     windows[2].desired_rect.right = screen_width;
302     windows[2].desired_rect.top = 0;
303     windows[2].desired_rect.bottom = screen_height;
304     testwindow_setpos(windows[2].hwnd);
305
306     do_events_until(no_appbars_intersect);
307     test_window_rects(0, 1);
308     test_window_rects(0, 2);
309     test_window_rects(1, 2);
310
311     /* move windows[1] to the top of the screen */
312     windows[1].edge = ABE_TOP;
313     windows[1].desired_rect.left = 0;
314     windows[1].desired_rect.right = screen_width;
315     windows[1].desired_rect.top = 0;
316     windows[1].desired_rect.bottom = 15;
317     testwindow_setpos(windows[1].hwnd);
318
319     do_events_until(no_appbars_intersect);
320     test_window_rects(0, 1);
321     test_window_rects(0, 2);
322     test_window_rects(1, 2);
323
324     /* move windows[1] back to the bottom of the screen */
325     windows[1].edge = ABE_BOTTOM;
326     windows[1].desired_rect.left = 0;
327     windows[1].desired_rect.right = screen_width;
328     windows[1].desired_rect.top = screen_height - 10;
329     windows[1].desired_rect.bottom = screen_height;
330     testwindow_setpos(windows[1].hwnd);
331
332     do_events_until(no_appbars_intersect);
333     test_window_rects(0, 1);
334     test_window_rects(0, 2);
335     test_window_rects(1, 2);
336
337     /* removing windows[0] will cause windows[1] to move down into its space */
338     expected_bottom = max(windows[0].allocated_rect.bottom, windows[1].allocated_rect.bottom);
339     org_bottom1 = windows[1].allocated_rect.bottom;
340     ok(windows[0].allocated_rect.bottom > windows[1].allocated_rect.bottom,
341         "Expected windows[0] to be lower than windows[1]\n");
342
343     abd.hWnd = windows[0].hwnd;
344     windows[0].to_be_deleted = TRUE;
345     ret = SHAppBarMessage(ABM_REMOVE, &abd);
346     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
347     windows[0].registered = FALSE;
348     DestroyWindow(windows[0].hwnd);
349
350     do_events_until(got_expected_bottom);
351
352     if (windows[1].allocated_rect.bottom == org_bottom1)
353         win_skip("Some broken Vista boxes don't move the higher appbar down\n");
354     else
355         ok(windows[1].allocated_rect.bottom == expected_bottom,
356             "windows[1]'s bottom is %i, expected %i\n",
357             windows[1].allocated_rect.bottom, expected_bottom);
358
359     test_window_rects(1, 2);
360
361     /* remove the other windows */
362     abd.hWnd = windows[1].hwnd;
363     windows[1].to_be_deleted = TRUE;
364     ret = SHAppBarMessage(ABM_REMOVE, &abd);
365     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
366     windows[1].registered = FALSE;
367     DestroyWindow(windows[1].hwnd);
368
369     abd.hWnd = windows[2].hwnd;
370     windows[2].to_be_deleted = TRUE;
371     ret = SHAppBarMessage(ABM_REMOVE, &abd);
372     ok(ret == TRUE, "SHAppBarMessage returned %i\n", ret);
373     windows[2].registered = FALSE;
374     DestroyWindow(windows[2].hwnd);
375 }
376
377 static void test_appbarget(void)
378 {
379     APPBARDATA abd;
380     HWND hwnd, foregnd, unset_hwnd;
381     UINT_PTR ret;
382
383     memset(&abd, 0xcc, sizeof(abd));
384     memset(&unset_hwnd, 0xcc, sizeof(unset_hwnd));
385     abd.cbSize = sizeof(abd);
386     abd.uEdge = ABE_BOTTOM;
387
388     hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
389     ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd);
390     ok(abd.hWnd == unset_hwnd, "hWnd overwritten %p\n",abd.hWnd);
391
392     if (!pMonitorFromWindow)
393     {
394         win_skip("MonitorFromWindow is not available\n");
395     }
396     else
397     {
398         /* Presumably one can pass a hwnd with ABM_GETAUTOHIDEBAR to specify a monitor.
399            Pass the foreground window and check */
400         foregnd = GetForegroundWindow();
401         if(foregnd)
402         {
403             abd.hWnd = foregnd;
404             hwnd = (HWND)SHAppBarMessage(ABM_GETAUTOHIDEBAR, &abd);
405             ok(hwnd == NULL || IsWindow(hwnd), "ret %p which is not a window\n", hwnd);
406             ok(abd.hWnd == foregnd, "hWnd overwritten\n");
407             if(hwnd)
408             {
409                 HMONITOR appbar_mon, foregnd_mon;
410                 appbar_mon = pMonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
411                 foregnd_mon = pMonitorFromWindow(foregnd, MONITOR_DEFAULTTONEAREST);
412                 ok(appbar_mon == foregnd_mon, "Windows on different monitors\n");
413             }
414         }
415     }
416
417     memset(&abd, 0xcc, sizeof(abd));
418     abd.cbSize = sizeof(abd);
419     ret = SHAppBarMessage(ABM_GETTASKBARPOS, &abd);
420     if(ret)
421     {
422         ok(abd.hWnd == (HWND)0xcccccccc, "hWnd overwritten\n");
423         ok(abd.uEdge <= ABE_BOTTOM ||
424             broken(abd.uEdge == 0xcccccccc), /* Some Win95 and NT4 */
425             "uEdge not returned\n");
426         ok(abd.rc.left != 0xcccccccc, "rc not updated\n");
427     }
428
429     return;
430 }
431
432 START_TEST(appbar)
433 {
434     HMODULE huser32;
435
436     huser32 = GetModuleHandleA("user32.dll");
437     pMonitorFromWindow = (void*)GetProcAddress(huser32, "MonitorFromWindow");
438
439     register_testwindow_class();
440
441     test_setpos();
442     test_appbarget();
443 }