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