comctl32/listview: Free ID array when removing all items.
[wine] / dlls / comctl32 / tests / propsheet.c
1 /* Unit test suite for property sheet control.
2  *
3  * Copyright 2006 Huw Davies
4  * Copyright 2009 Jan de Mooij
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <windows.h>
22 #include <commctrl.h>
23
24 #include "resources.h"
25
26 #include "wine/test.h"
27
28 static HWND parent;
29 static HWND sheethwnd;
30
31 static LONG active_page = -1;
32
33 #define IDC_APPLY_BUTTON 12321
34
35 static int CALLBACK sheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
36 {
37     switch(msg)
38     {
39     case PSCB_INITIALIZED:
40       {
41         char caption[256];
42         GetWindowTextA(hwnd, caption, sizeof(caption));
43         ok(!strcmp(caption,"test caption"), "caption: %s\n", caption);
44         sheethwnd = hwnd;
45         return 0;
46       }
47     }
48     return 0;
49 }
50
51 static INT_PTR CALLBACK page_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
52                                       LPARAM lparam)
53 {
54     switch(msg)
55     {
56     case WM_INITDIALOG:
57       {
58         HWND sheet = GetParent(hwnd);
59         char caption[256];
60         GetWindowTextA(sheet, caption, sizeof(caption));
61         ok(!strcmp(caption,"test caption"), "caption: %s\n", caption);
62         return TRUE;
63       }
64
65     case WM_NOTIFY:
66       {
67         NMHDR *nmhdr = (NMHDR *)lparam;
68         switch(nmhdr->code)
69         {
70         case PSN_APPLY:
71             return TRUE;
72         default:
73             return FALSE;
74         }
75       }
76     case WM_NCDESTROY:
77         ok(!SendMessageA(sheethwnd, PSM_INDEXTOHWND, 400, 0),"Should always be 0\n");
78         return TRUE;
79
80     default:
81         return FALSE;
82     }
83 }
84
85 static void test_title(void)
86 {
87     HPROPSHEETPAGE hpsp[1];
88     PROPSHEETPAGEA psp;
89     PROPSHEETHEADERA psh;
90     HWND hdlg;
91
92     memset(&psp, 0, sizeof(psp));
93     psp.dwSize = sizeof(psp);
94     psp.dwFlags = 0;
95     psp.hInstance = GetModuleHandleW(NULL);
96     U(psp).pszTemplate = "prop_page1";
97     U2(psp).pszIcon = NULL;
98     psp.pfnDlgProc = page_dlg_proc;
99     psp.lParam = 0;
100
101     hpsp[0] = CreatePropertySheetPageA(&psp);
102
103     memset(&psh, 0, sizeof(psh));
104     psh.dwSize = sizeof(psh);
105     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
106     psh.pszCaption = "test caption";
107     psh.nPages = 1;
108     psh.hwndParent = GetDesktopWindow();
109     U3(psh).phpage = hpsp;
110     psh.pfnCallback = sheet_callback;
111
112     hdlg = (HWND)PropertySheetA(&psh);
113     DestroyWindow(hdlg);
114 }
115
116 static void test_nopage(void)
117 {
118     HPROPSHEETPAGE hpsp[1];
119     PROPSHEETPAGEA psp;
120     PROPSHEETHEADERA psh;
121     HWND hdlg;
122
123     memset(&psp, 0, sizeof(psp));
124     psp.dwSize = sizeof(psp);
125     psp.dwFlags = 0;
126     psp.hInstance = GetModuleHandleW(NULL);
127     U(psp).pszTemplate = "prop_page1";
128     U2(psp).pszIcon = NULL;
129     psp.pfnDlgProc = page_dlg_proc;
130     psp.lParam = 0;
131
132     hpsp[0] = CreatePropertySheetPageA(&psp);
133
134     memset(&psh, 0, sizeof(psh));
135     psh.dwSize = sizeof(psh);
136     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
137     psh.pszCaption = "test caption";
138     psh.nPages = 1;
139     psh.hwndParent = GetDesktopWindow();
140     U3(psh).phpage = hpsp;
141     psh.pfnCallback = sheet_callback;
142
143     hdlg = (HWND)PropertySheetA(&psh);
144     ShowWindow(hdlg,SW_NORMAL);
145     SendMessage(hdlg, PSM_REMOVEPAGE, 0, 0);
146     RedrawWindow(hdlg,NULL,NULL,RDW_UPDATENOW|RDW_ERASENOW);
147     DestroyWindow(hdlg);
148 }
149
150 static int CALLBACK disableowner_callback(HWND hwnd, UINT msg, LPARAM lparam)
151 {
152     switch(msg)
153     {
154     case PSCB_INITIALIZED:
155       {
156         ok(IsWindowEnabled(parent) == 0, "parent window should be disabled\n");
157         PostQuitMessage(0);
158         return FALSE;
159       }
160     }
161     return FALSE;
162 }
163
164 static void register_parent_wnd_class(void)
165 {
166     WNDCLASSA cls;
167
168     cls.style = 0;
169     cls.lpfnWndProc = DefWindowProcA;
170     cls.cbClsExtra = 0;
171     cls.cbWndExtra = 0;
172     cls.hInstance = GetModuleHandleA(NULL);
173     cls.hIcon = 0;
174     cls.hCursor = LoadCursorA(0, IDC_ARROW);
175     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
176     cls.lpszMenuName = NULL;
177     cls.lpszClassName = "parent class";
178     RegisterClassA(&cls);
179 }
180
181 static void test_disableowner(void)
182 {
183     HPROPSHEETPAGE hpsp[1];
184     PROPSHEETPAGEA psp;
185     PROPSHEETHEADERA psh;
186
187     register_parent_wnd_class();
188     parent = CreateWindowA("parent class", "", WS_CAPTION | WS_SYSMENU | WS_VISIBLE, 100, 100, 100, 100, GetDesktopWindow(), NULL, GetModuleHandleA(NULL), 0);
189
190     memset(&psp, 0, sizeof(psp));
191     psp.dwSize = sizeof(psp);
192     psp.dwFlags = 0;
193     psp.hInstance = GetModuleHandleW(NULL);
194     U(psp).pszTemplate = "prop_page1";
195     U2(psp).pszIcon = NULL;
196     psp.pfnDlgProc = NULL;
197     psp.lParam = 0;
198
199     hpsp[0] = CreatePropertySheetPageA(&psp);
200
201     memset(&psh, 0, sizeof(psh));
202     psh.dwSize = sizeof(psh);
203     psh.dwFlags = PSH_USECALLBACK;
204     psh.pszCaption = "test caption";
205     psh.nPages = 1;
206     psh.hwndParent = parent;
207     U3(psh).phpage = hpsp;
208     psh.pfnCallback = disableowner_callback;
209
210     PropertySheetA(&psh);
211     ok(IsWindowEnabled(parent) != 0, "parent window should be enabled\n");
212     DestroyWindow(parent);
213 }
214
215 static INT_PTR CALLBACK nav_page_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
216 {
217     switch(msg){
218     case WM_NOTIFY:
219         {
220             LPNMHDR hdr = (LPNMHDR)lparam;
221             switch(hdr->code){
222             case PSN_SETACTIVE:
223                 active_page = PropSheet_HwndToIndex(hdr->hwndFrom, hwnd);
224                 return TRUE;
225             case PSN_KILLACTIVE:
226                 /* prevent navigation away from the fourth page */
227                 if(active_page == 3){
228                     SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
229                     return TRUE;
230                 }
231             }
232             break;
233         }
234     }
235     return FALSE;
236 }
237
238 static void test_wiznavigation(void)
239 {
240     HPROPSHEETPAGE hpsp[4];
241     PROPSHEETPAGEA psp[4];
242     PROPSHEETHEADERA psh;
243     HWND hdlg, control;
244     LONG_PTR controlID;
245     LRESULT defidres;
246     BOOL hwndtoindex_supported = TRUE;
247     const INT nextID = 12324;
248     const INT backID = 12323;
249
250     /* create the property sheet pages */
251     memset(psp, 0, sizeof(PROPSHEETPAGEA) * 4);
252
253     psp[0].dwSize = sizeof(PROPSHEETPAGEA);
254     psp[0].hInstance = GetModuleHandleW(NULL);
255     U(psp[0]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_INTRO);
256     psp[0].pfnDlgProc = nav_page_proc;
257     hpsp[0] = CreatePropertySheetPageA(&psp[0]);
258
259     psp[1].dwSize = sizeof(PROPSHEETPAGEA);
260     psp[1].hInstance = GetModuleHandleW(NULL);
261     U(psp[1]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_EDIT);
262     psp[1].pfnDlgProc = nav_page_proc;
263     hpsp[1] = CreatePropertySheetPageA(&psp[1]);
264
265     psp[2].dwSize = sizeof(PROPSHEETPAGEA);
266     psp[2].hInstance = GetModuleHandleW(NULL);
267     U(psp[2]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_RADIO);
268     psp[2].pfnDlgProc = nav_page_proc;
269     hpsp[2] = CreatePropertySheetPageA(&psp[2]);
270
271     psp[3].dwSize = sizeof(PROPSHEETPAGEA);
272     psp[3].hInstance = GetModuleHandleW(NULL);
273     U(psp[3]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_EXIT);
274     psp[3].pfnDlgProc = nav_page_proc;
275     hpsp[3] = CreatePropertySheetPageA(&psp[3]);
276
277     /* set up the property sheet dialog */
278     memset(&psh, 0, sizeof(psh));
279     psh.dwSize = sizeof(psh);
280     psh.dwFlags = PSH_MODELESS | PSH_WIZARD;
281     psh.pszCaption = "A Wizard";
282     psh.nPages = 4;
283     psh.hwndParent = GetDesktopWindow();
284     U3(psh).phpage = hpsp;
285     hdlg = (HWND)PropertySheetA(&psh);
286
287     ok(active_page == 0, "Active page should be 0. Is: %d\n", active_page);
288
289     control = GetFocus();
290     controlID = GetWindowLongPtr(control, GWLP_ID);
291     ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
292
293     /* simulate pressing the Next button */
294     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
295     if (!active_page) hwndtoindex_supported = FALSE;
296     if (hwndtoindex_supported)
297         ok(active_page == 1, "Active page should be 1 after pressing Next. Is: %d\n", active_page);
298
299     control = GetFocus();
300     controlID = GetWindowLongPtr(control, GWLP_ID);
301     ok(controlID == IDC_PS_EDIT1, "Focus should be set to the first item on the second page. Expected: %d, Found: %ld\n", IDC_PS_EDIT1, controlID);
302
303     defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
304     ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
305
306     /* set the focus to the second edit box on this page */
307     SetFocus(GetNextDlgTabItem(hdlg, control, FALSE));
308
309     /* press next again */
310     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
311     if (hwndtoindex_supported)
312         ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %d\n", active_page);
313
314     control = GetFocus();
315     controlID = GetWindowLongPtr(control, GWLP_ID);
316     ok(controlID == IDC_PS_RADIO1, "Focus should have been set to item on third page. Expected: %d, Found %ld\n", IDC_PS_RADIO1, controlID);
317
318     /* back button */
319     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
320     if (hwndtoindex_supported)
321         ok(active_page == 1, "Active page should be 1 after pressing Back. Is: %d\n", active_page);
322
323     control = GetFocus();
324     controlID = GetWindowLongPtr(control, GWLP_ID);
325     ok(controlID == IDC_PS_EDIT1, "Focus should have been set to the first item on second page. Expected: %d, Found %ld\n", IDC_PS_EDIT1, controlID);
326
327     defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
328     ok(defidres == MAKELRESULT(backID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", backID, LOWORD(defidres));
329
330     /* press next twice */
331     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
332     if (hwndtoindex_supported)
333         ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %d\n", active_page);
334     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
335     if (hwndtoindex_supported)
336         ok(active_page == 3, "Active page should be 3 after pressing Next. Is: %d\n", active_page);
337     else
338         active_page = 3;
339
340     control = GetFocus();
341     controlID = GetWindowLongPtr(control, GWLP_ID);
342     ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
343
344     /* try to navigate away, but shouldn't be able to */
345     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
346     ok(active_page == 3, "Active page should still be 3 after pressing Back. Is: %d\n", active_page);
347
348     defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
349     ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
350
351     DestroyWindow(hdlg);
352 }
353 static void test_buttons(void)
354 {
355     HPROPSHEETPAGE hpsp[1];
356     PROPSHEETPAGEA psp;
357     PROPSHEETHEADERA psh;
358     HWND hdlg;
359     HWND button;
360     RECT rc;
361     int prevRight, top;
362
363     memset(&psp, 0, sizeof(psp));
364     psp.dwSize = sizeof(psp);
365     psp.dwFlags = 0;
366     psp.hInstance = GetModuleHandleW(NULL);
367     U(psp).pszTemplate = "prop_page1";
368     U2(psp).pszIcon = NULL;
369     psp.pfnDlgProc = page_dlg_proc;
370     psp.lParam = 0;
371
372     hpsp[0] = CreatePropertySheetPageA(&psp);
373
374     memset(&psh, 0, sizeof(psh));
375     psh.dwSize = sizeof(psh);
376     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
377     psh.pszCaption = "test caption";
378     psh.nPages = 1;
379     psh.hwndParent = GetDesktopWindow();
380     U3(psh).phpage = hpsp;
381     psh.pfnCallback = sheet_callback;
382
383     hdlg = (HWND)PropertySheetA(&psh);
384
385     /* OK button */
386     button = GetDlgItem(hdlg, IDOK);
387     GetWindowRect(button, &rc);
388     prevRight = rc.right;
389     top = rc.top;
390
391     /* Cancel button */
392     button = GetDlgItem(hdlg, IDCANCEL);
393     GetWindowRect(button, &rc);
394     ok(rc.top == top, "Cancel button should have same top as OK button\n");
395     ok(rc.left > prevRight, "Cancel button should be to the right of OK button\n");
396     prevRight = rc.right;
397
398     button = GetDlgItem(hdlg, IDC_APPLY_BUTTON);
399     GetWindowRect(button, &rc);
400     ok(rc.top == top, "Apply button should have same top as OK button\n");
401     ok(rc.left > prevRight, "Apply button should be to the right of Cancel button\n");
402     prevRight = rc.right;
403
404     button = GetDlgItem(hdlg, IDHELP);
405     GetWindowRect(button, &rc);
406     ok(rc.top == top, "Help button should have same top as OK button\n");
407     ok(rc.left > prevRight, "Help button should be to the right of Apply button\n");
408
409     DestroyWindow(hdlg);
410 }
411
412 START_TEST(propsheet)
413 {
414     test_title();
415     test_nopage();
416     test_disableowner();
417     test_wiznavigation();
418     test_buttons();
419 }