wordpad: Allow objects & images to be added with native riched20.
[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 = GetModuleHandleA(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     if (hdlg == INVALID_HANDLE_VALUE)
114     {
115         win_skip("comctl32 4.70 needs dwSize adjustment\n");
116         psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
117         hdlg = (HWND)PropertySheetA(&psh);
118     }
119     DestroyWindow(hdlg);
120 }
121
122 static void test_nopage(void)
123 {
124     HPROPSHEETPAGE hpsp[1];
125     PROPSHEETPAGEA psp;
126     PROPSHEETHEADERA psh;
127     HWND hdlg;
128
129     memset(&psp, 0, sizeof(psp));
130     psp.dwSize = sizeof(psp);
131     psp.dwFlags = 0;
132     psp.hInstance = GetModuleHandleA(NULL);
133     U(psp).pszTemplate = "prop_page1";
134     U2(psp).pszIcon = NULL;
135     psp.pfnDlgProc = page_dlg_proc;
136     psp.lParam = 0;
137
138     hpsp[0] = CreatePropertySheetPageA(&psp);
139
140     memset(&psh, 0, sizeof(psh));
141     psh.dwSize = sizeof(psh);
142     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
143     psh.pszCaption = "test caption";
144     psh.nPages = 1;
145     psh.hwndParent = GetDesktopWindow();
146     U3(psh).phpage = hpsp;
147     psh.pfnCallback = sheet_callback;
148
149     hdlg = (HWND)PropertySheetA(&psh);
150     if (hdlg == INVALID_HANDLE_VALUE)
151     {
152         win_skip("comctl32 4.70 needs dwSize adjustment\n");
153         psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
154         hdlg = (HWND)PropertySheetA(&psh);
155     }
156     ShowWindow(hdlg,SW_NORMAL);
157     SendMessage(hdlg, PSM_REMOVEPAGE, 0, 0);
158     RedrawWindow(hdlg,NULL,NULL,RDW_UPDATENOW|RDW_ERASENOW);
159     DestroyWindow(hdlg);
160 }
161
162 static int CALLBACK disableowner_callback(HWND hwnd, UINT msg, LPARAM lparam)
163 {
164     switch(msg)
165     {
166     case PSCB_INITIALIZED:
167       {
168         ok(IsWindowEnabled(parent) == 0, "parent window should be disabled\n");
169         PostQuitMessage(0);
170         return FALSE;
171       }
172     }
173     return FALSE;
174 }
175
176 static void register_parent_wnd_class(void)
177 {
178     WNDCLASSA cls;
179
180     cls.style = 0;
181     cls.lpfnWndProc = DefWindowProcA;
182     cls.cbClsExtra = 0;
183     cls.cbWndExtra = 0;
184     cls.hInstance = GetModuleHandleA(NULL);
185     cls.hIcon = 0;
186     cls.hCursor = LoadCursorA(0, IDC_ARROW);
187     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
188     cls.lpszMenuName = NULL;
189     cls.lpszClassName = "parent class";
190     RegisterClassA(&cls);
191 }
192
193 static void test_disableowner(void)
194 {
195     HPROPSHEETPAGE hpsp[1];
196     PROPSHEETPAGEA psp;
197     PROPSHEETHEADERA psh;
198     INT_PTR p;
199
200     register_parent_wnd_class();
201     parent = CreateWindowA("parent class", "", WS_CAPTION | WS_SYSMENU | WS_VISIBLE, 100, 100, 100, 100, GetDesktopWindow(), NULL, GetModuleHandleA(NULL), 0);
202
203     memset(&psp, 0, sizeof(psp));
204     psp.dwSize = sizeof(psp);
205     psp.dwFlags = 0;
206     psp.hInstance = GetModuleHandleA(NULL);
207     U(psp).pszTemplate = "prop_page1";
208     U2(psp).pszIcon = NULL;
209     psp.pfnDlgProc = NULL;
210     psp.lParam = 0;
211
212     hpsp[0] = CreatePropertySheetPageA(&psp);
213
214     memset(&psh, 0, sizeof(psh));
215     psh.dwSize = sizeof(psh);
216     psh.dwFlags = PSH_USECALLBACK;
217     psh.pszCaption = "test caption";
218     psh.nPages = 1;
219     psh.hwndParent = parent;
220     U3(psh).phpage = hpsp;
221     psh.pfnCallback = disableowner_callback;
222
223     p = PropertySheetA(&psh);
224     todo_wine
225     ok(p == 0, "Expected 0, got %ld\n", p);
226     ok(IsWindowEnabled(parent) != 0, "parent window should be enabled\n");
227     DestroyWindow(parent);
228 }
229
230 static INT_PTR CALLBACK nav_page_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
231 {
232     switch(msg){
233     case WM_NOTIFY:
234         {
235             LPNMHDR hdr = (LPNMHDR)lparam;
236             switch(hdr->code){
237             case PSN_SETACTIVE:
238                 active_page = PropSheet_HwndToIndex(hdr->hwndFrom, hwnd);
239                 return TRUE;
240             case PSN_KILLACTIVE:
241                 /* prevent navigation away from the fourth page */
242                 if(active_page == 3){
243                     SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
244                     return TRUE;
245                 }
246             }
247             break;
248         }
249     }
250     return FALSE;
251 }
252
253 static void test_wiznavigation(void)
254 {
255     HPROPSHEETPAGE hpsp[4];
256     PROPSHEETPAGEA psp[4];
257     PROPSHEETHEADERA psh;
258     HWND hdlg, control;
259     LONG_PTR controlID;
260     LRESULT defidres;
261     BOOL hwndtoindex_supported = TRUE;
262     const INT nextID = 12324;
263     const INT backID = 12323;
264
265     /* create the property sheet pages */
266     memset(psp, 0, sizeof(PROPSHEETPAGEA) * 4);
267
268     psp[0].dwSize = sizeof(PROPSHEETPAGEA);
269     psp[0].hInstance = GetModuleHandleA(NULL);
270     U(psp[0]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_INTRO);
271     psp[0].pfnDlgProc = nav_page_proc;
272     hpsp[0] = CreatePropertySheetPageA(&psp[0]);
273
274     psp[1].dwSize = sizeof(PROPSHEETPAGEA);
275     psp[1].hInstance = GetModuleHandleA(NULL);
276     U(psp[1]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_EDIT);
277     psp[1].pfnDlgProc = nav_page_proc;
278     hpsp[1] = CreatePropertySheetPageA(&psp[1]);
279
280     psp[2].dwSize = sizeof(PROPSHEETPAGEA);
281     psp[2].hInstance = GetModuleHandleA(NULL);
282     U(psp[2]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_RADIO);
283     psp[2].pfnDlgProc = nav_page_proc;
284     hpsp[2] = CreatePropertySheetPageA(&psp[2]);
285
286     psp[3].dwSize = sizeof(PROPSHEETPAGEA);
287     psp[3].hInstance = GetModuleHandleA(NULL);
288     U(psp[3]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_EXIT);
289     psp[3].pfnDlgProc = nav_page_proc;
290     hpsp[3] = CreatePropertySheetPageA(&psp[3]);
291
292     /* set up the property sheet dialog */
293     memset(&psh, 0, sizeof(psh));
294     psh.dwSize = sizeof(psh);
295     psh.dwFlags = PSH_MODELESS | PSH_WIZARD;
296     psh.pszCaption = "A Wizard";
297     psh.nPages = 4;
298     psh.hwndParent = GetDesktopWindow();
299     U3(psh).phpage = hpsp;
300     hdlg = (HWND)PropertySheetA(&psh);
301     if (hdlg == INVALID_HANDLE_VALUE)
302     {
303         win_skip("comctl32 4.70 needs dwSize adjustment\n");
304         psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
305         hdlg = (HWND)PropertySheetA(&psh);
306     }
307
308     ok(active_page == 0, "Active page should be 0. Is: %d\n", active_page);
309
310     control = GetFocus();
311     controlID = GetWindowLongPtr(control, GWLP_ID);
312     ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
313
314     /* simulate pressing the Next button */
315     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
316     if (!active_page) hwndtoindex_supported = FALSE;
317     if (hwndtoindex_supported)
318         ok(active_page == 1, "Active page should be 1 after pressing Next. Is: %d\n", active_page);
319
320     control = GetFocus();
321     controlID = GetWindowLongPtr(control, GWLP_ID);
322     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);
323
324     defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
325     ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
326
327     /* set the focus to the second edit box on this page */
328     SetFocus(GetNextDlgTabItem(hdlg, control, FALSE));
329
330     /* press next again */
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
335     control = GetFocus();
336     controlID = GetWindowLongPtr(control, GWLP_ID);
337     ok(controlID == IDC_PS_RADIO1, "Focus should have been set to item on third page. Expected: %d, Found %ld\n", IDC_PS_RADIO1, controlID);
338
339     /* back button */
340     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
341     if (hwndtoindex_supported)
342         ok(active_page == 1, "Active page should be 1 after pressing Back. Is: %d\n", active_page);
343
344     control = GetFocus();
345     controlID = GetWindowLongPtr(control, GWLP_ID);
346     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);
347
348     defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
349     ok(defidres == MAKELRESULT(backID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", backID, LOWORD(defidres));
350
351     /* press next twice */
352     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
353     if (hwndtoindex_supported)
354         ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %d\n", active_page);
355     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
356     if (hwndtoindex_supported)
357         ok(active_page == 3, "Active page should be 3 after pressing Next. Is: %d\n", active_page);
358     else
359         active_page = 3;
360
361     control = GetFocus();
362     controlID = GetWindowLongPtr(control, GWLP_ID);
363     ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
364
365     /* try to navigate away, but shouldn't be able to */
366     SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
367     ok(active_page == 3, "Active page should still be 3 after pressing Back. Is: %d\n", active_page);
368
369     defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
370     ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
371
372     DestroyWindow(hdlg);
373 }
374
375 static void test_buttons(void)
376 {
377     HPROPSHEETPAGE hpsp[1];
378     PROPSHEETPAGEA psp;
379     PROPSHEETHEADERA psh;
380     HWND hdlg;
381     HWND button;
382     RECT rc;
383     int prevRight, top;
384
385     memset(&psp, 0, sizeof(psp));
386     psp.dwSize = sizeof(psp);
387     psp.dwFlags = 0;
388     psp.hInstance = GetModuleHandleA(NULL);
389     U(psp).pszTemplate = "prop_page1";
390     U2(psp).pszIcon = NULL;
391     psp.pfnDlgProc = page_dlg_proc;
392     psp.lParam = 0;
393
394     hpsp[0] = CreatePropertySheetPageA(&psp);
395
396     memset(&psh, 0, sizeof(psh));
397     psh.dwSize = sizeof(psh);
398     psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
399     psh.pszCaption = "test caption";
400     psh.nPages = 1;
401     psh.hwndParent = GetDesktopWindow();
402     U3(psh).phpage = hpsp;
403     psh.pfnCallback = sheet_callback;
404
405     hdlg = (HWND)PropertySheetA(&psh);
406     if (hdlg == INVALID_HANDLE_VALUE)
407     {
408         win_skip("comctl32 4.70 needs dwSize adjustment\n");
409         psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
410         hdlg = (HWND)PropertySheetA(&psh);
411     }
412
413     /* OK button */
414     button = GetDlgItem(hdlg, IDOK);
415     GetWindowRect(button, &rc);
416     prevRight = rc.right;
417     top = rc.top;
418
419     /* Cancel button */
420     button = GetDlgItem(hdlg, IDCANCEL);
421     GetWindowRect(button, &rc);
422     ok(rc.top == top, "Cancel button should have same top as OK button\n");
423     ok(rc.left > prevRight, "Cancel button should be to the right of OK button\n");
424     prevRight = rc.right;
425
426     button = GetDlgItem(hdlg, IDC_APPLY_BUTTON);
427     GetWindowRect(button, &rc);
428     ok(rc.top == top, "Apply button should have same top as OK button\n");
429     ok(rc.left > prevRight, "Apply button should be to the right of Cancel button\n");
430     prevRight = rc.right;
431
432     button = GetDlgItem(hdlg, IDHELP);
433     GetWindowRect(button, &rc);
434     ok(rc.top == top, "Help button should have same top as OK button\n");
435     ok(rc.left > prevRight, "Help button should be to the right of Apply button\n");
436
437     DestroyWindow(hdlg);
438 }
439
440 static BOOL add_button_has_been_pressed;
441
442 static INT_PTR CALLBACK
443 page_with_custom_default_button_dlg_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
444 {
445     switch (msg)
446     {
447     case WM_COMMAND:
448         switch(LOWORD(wparam))
449         {
450         case IDC_PS_PUSHBUTTON1:
451             switch(HIWORD(wparam))
452             {
453             case BN_CLICKED:
454                 add_button_has_been_pressed = TRUE;
455                 return TRUE;
456             }
457             break;
458         }
459         break;
460     }
461     return FALSE;
462 }
463
464 static void test_custom_default_button(void)
465 {
466     HWND hdlg;
467     PROPSHEETPAGEA psp[1];
468     PROPSHEETHEADERA psh;
469     MSG msg;
470     LRESULT result;
471
472     psp[0].dwSize = sizeof (PROPSHEETPAGEA);
473     psp[0].dwFlags = PSP_USETITLE;
474     psp[0].hInstance = GetModuleHandleA(NULL);
475     U(psp[0]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_WITH_CUSTOM_DEFAULT_BUTTON);
476     U2(psp[0]).pszIcon = NULL;
477     psp[0].pfnDlgProc = page_with_custom_default_button_dlg_proc;
478     psp[0].pszTitle = "Page1";
479     psp[0].lParam = 0;
480
481     psh.dwSize = sizeof (PROPSHEETHEADERA);
482     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_MODELESS;
483     psh.hwndParent = GetDesktopWindow();
484     psh.hInstance = GetModuleHandleA(NULL);
485     U(psh).pszIcon = NULL;
486     psh.pszCaption =  "PropertySheet1";
487     psh.nPages = 1;
488     U3(psh).ppsp = psp;
489     U2(psh).nStartPage = 0;
490
491     /* The goal of the test is to make sure that the Add button is pressed
492      * when the ENTER key is pressed and a different control, a combobox,
493      * has the keyboard focus. */
494     add_button_has_been_pressed = FALSE;
495
496     /* Create the modeless property sheet. */
497     hdlg = (HWND)PropertySheetA(&psh);
498     ok(hdlg != INVALID_HANDLE_VALUE, "Cannot create the property sheet\n");
499
500     /* Set the Add button as the default button. */
501     SendMessage(hdlg, DM_SETDEFID, (WPARAM)IDC_PS_PUSHBUTTON1, 0);
502
503     /* Make sure the default button is the Add button. */
504     result = SendMessage(hdlg, DM_GETDEFID, 0, 0);
505     ok(DC_HASDEFID == HIWORD(result), "The property sheet does not have a default button\n");
506     ok(IDC_PS_PUSHBUTTON1 == LOWORD(result), "The default button is not the Add button\n");
507
508     /* At this point, the combobox should have keyboard focus, so we press ENTER.
509      * Pull the lever, Kronk! */
510     keybd_event(VK_RETURN, 0, 0, 0);
511
512     /* Process all the messages in the queue for this thread. */
513     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
514     {
515         if (!PropSheet_IsDialogMessage(hdlg, &msg))
516         {
517             TranslateMessage(&msg);
518             DispatchMessage(&msg);
519         }
520     }
521
522     todo_wine
523     ok(add_button_has_been_pressed, "The Add button has not been pressed!\n");
524
525     DestroyWindow(hdlg);
526 }
527
528 START_TEST(propsheet)
529 {
530     test_title();
531     test_nopage();
532     test_disableowner();
533     test_wiznavigation();
534     test_buttons();
535     test_custom_default_button();
536 }