ok() does not support '%S'. Store the Ansi version, convert to Unicode
[wine] / dlls / comctl32 / propsheet.c
1 /*
2  * Property Sheets
3  *
4  * Copyright 1998 Francis Beaudet
5  * Copyright 1999 Thuy Nguyen
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * TODO:
22  *   - Tab order
23  *   - Unicode property sheets
24  */
25
26 #include <string.h>
27
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30 #include "winbase.h"
31 #include "commctrl.h"
32 #include "prsht.h"
33 #include "winnls.h"
34 #include "comctl32.h"
35 #include "heap.h"
36
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39
40 /******************************************************************************
41  * Data structures
42  */
43 #include "pshpack2.h"
44
45 typedef struct
46 {
47   WORD dlgVer;
48   WORD signature;
49   DWORD helpID;
50   DWORD exStyle;
51   DWORD style;
52 } MyDLGTEMPLATEEX;
53
54 typedef struct
55 {
56   DWORD helpid;
57   DWORD exStyle;
58   DWORD style;
59   short x;
60   short y;
61   short cx;
62   short cy;
63   DWORD id;
64 } MyDLGITEMTEMPLATEEX;
65 #include "poppack.h"
66
67 typedef struct tagPropPageInfo
68 {
69   HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
70   HWND hwndPage;
71   BOOL isDirty;
72   LPCWSTR pszText;
73   BOOL hasHelp;
74   BOOL useCallback;
75   BOOL hasIcon;
76 } PropPageInfo;
77
78 typedef struct tagPropSheetInfo
79 {
80   HWND hwnd;
81   PROPSHEETHEADERW ppshheader;
82   LPWSTR strPropertiesFor;
83   int nPages;
84   int active_page;
85   BOOL isModeless;
86   BOOL hasHelp;
87   BOOL hasApply;
88   BOOL useCallback;
89   BOOL restartWindows;
90   BOOL rebootSystem;
91   BOOL activeValid;
92   PropPageInfo* proppage;
93   int x;
94   int y;
95   int width;
96   int height;
97   HIMAGELIST hImageList;
98 } PropSheetInfo;
99
100 typedef struct
101 {
102   int x;
103   int y;
104 } PADDING_INFO;
105
106 /******************************************************************************
107  * Defines and global variables
108  */
109
110 const WCHAR PropSheetInfoStr[] =
111     {'P','r','o','p','e','r','t','y','S','h','e','e','t','I','n','f','o',0 };
112
113 #define MAX_CAPTION_LENGTH 255
114 #define MAX_TABTEXT_LENGTH 255
115 #define MAX_BUTTONTEXT_LENGTH 64
116
117 #define INTRNL_ANY_WIZARD (PSH_WIZARD | PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)
118
119 /******************************************************************************
120  * Prototypes
121  */
122 static BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo);
123 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo);
124 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo);
125 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo);
126 static BOOL PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
127                                        PropSheetInfo * psInfo);
128 static BOOL PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
129                                        PropSheetInfo * psInfo);
130 static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
131                                       PropSheetInfo * psInfo,
132                                       int index);
133 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
134                                        PropSheetInfo * psInfo);
135 static BOOL PROPSHEET_CreatePage(HWND hwndParent, int index,
136                                 const PropSheetInfo * psInfo,
137                                 LPCPROPSHEETPAGEW ppshpage);
138 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo);
139 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
140 static BOOL PROPSHEET_Back(HWND hwndDlg);
141 static BOOL PROPSHEET_Next(HWND hwndDlg);
142 static BOOL PROPSHEET_Finish(HWND hwndDlg);
143 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam);
144 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam);
145 static void PROPSHEET_Help(HWND hwndDlg);
146 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage);
147 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage);
148 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID);
149 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText);
150 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText);
151 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText);
152 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText);
153 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
154 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
155                                 int index,
156                                 int skipdir,
157                                 HPROPSHEETPAGE hpage);
158 static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id);
159 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
160                                        WPARAM wParam, LPARAM lParam);
161 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
162                               HPROPSHEETPAGE hpage);
163
164 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
165                                  int index,
166                                  HPROPSHEETPAGE hpage);
167 static void PROPSHEET_CleanUp();
168 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo);
169 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags);
170 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
171 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg);
172 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID);
173
174 INT_PTR CALLBACK
175 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
176
177 WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
178
179 #define add_flag(a) if (dwFlags & a) {strcat(string, #a );strcat(string," ");}
180 /******************************************************************************
181  *            PROPSHEET_UnImplementedFlags
182  *
183  * Document use of flags we don't implement yet.
184  */
185 static VOID PROPSHEET_UnImplementedFlags(DWORD dwFlags)
186 {
187     CHAR string[256];
188
189     string[0] = '\0';
190
191   /*
192    * unhandled header flags:
193    *  PSH_DEFAULT            0x00000000
194    *  PSH_WIZARDHASFINISH    0x00000010
195    *  PSH_RTLREADING         0x00000800
196    *  PSH_WIZARDCONTEXTHELP  0x00001000
197    *  PSH_WIZARD97           0x00002000  (pre IE 5)
198    *  PSH_WATERMARK          0x00008000
199    *  PSH_USEHBMWATERMARK    0x00010000
200    *  PSH_USEHPLWATERMARK    0x00020000
201    *  PSH_STRETCHWATERMARK   0x00040000
202    *  PSH_HEADER             0x00080000
203    *  PSH_USEHBMHEADER       0x00100000
204    *  PSH_USEPAGELANG        0x00200000
205    *  PSH_WIZARD_LITE        0x00400000      also not in .h
206    *  PSH_WIZARD97           0x01000000  (IE 5 and above)
207    *  PSH_NOCONTEXTHELP      0x02000000      also not in .h
208    */
209
210     add_flag(PSH_WIZARDHASFINISH);
211     add_flag(PSH_RTLREADING);
212     add_flag(PSH_WIZARDCONTEXTHELP);
213     add_flag(PSH_WIZARD97_OLD);
214     add_flag(PSH_WATERMARK);
215     add_flag(PSH_USEHBMWATERMARK);
216     add_flag(PSH_USEHPLWATERMARK);
217     add_flag(PSH_STRETCHWATERMARK);
218     add_flag(PSH_HEADER);
219     add_flag(PSH_USEHBMHEADER);
220     add_flag(PSH_USEPAGELANG);
221     add_flag(PSH_WIZARD_LITE);
222     add_flag(PSH_WIZARD97_NEW);
223     add_flag(PSH_NOCONTEXTHELP);
224     if (string[0] != '\0')
225         FIXME("%s\n", string);
226 }
227 #undef add_flag
228
229 /******************************************************************************
230  *            PROPSHEET_GetPageRect
231  *
232  * Retrieve rect from tab control and map into the dialog for SetWindowPos
233  */
234 static void PROPSHEET_GetPageRect(const PropSheetInfo * psInfo, HWND hwndDlg, RECT *rc)
235 {
236     HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
237
238     GetClientRect(hwndTabCtrl, rc);
239     SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)rc);
240     MapWindowPoints(hwndTabCtrl, hwndDlg, (LPPOINT)rc, 2);
241 }
242
243 /******************************************************************************
244  *            PROPSHEET_FindPageByResId
245  *
246  * Find page index corresponding to page resource id.
247  */
248 INT PROPSHEET_FindPageByResId(PropSheetInfo * psInfo, LRESULT resId)
249 {
250    INT i;
251
252    for (i = 0; i < psInfo->nPages; i++)
253    {
254       LPCPROPSHEETPAGEA lppsp = (LPCPROPSHEETPAGEA)psInfo->proppage[i].hpage;
255
256       /* Fixme: if resource ID is a string shall we use strcmp ??? */
257       if (lppsp->u.pszTemplate == (LPVOID)resId)
258          break;
259    }
260
261    return i;
262 }
263
264 /******************************************************************************
265  *            PROPSHEET_AtoW
266  *
267  * Convert ASCII to Unicode since all data is saved as Unicode.
268  */
269 static void PROPSHEET_AtoW(LPCWSTR *tostr, LPCSTR frstr)
270 {
271     INT len;
272
273     TRACE("<%s>\n", frstr);
274     len = MultiByteToWideChar(CP_ACP, 0, frstr, -1, 0, 0);
275     *tostr = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
276     MultiByteToWideChar(CP_ACP, 0, frstr, -1, (LPWSTR)*tostr, len);
277 }
278
279 /******************************************************************************
280  *            PROPSHEET_CollectSheetInfoA
281  *
282  * Collect relevant data.
283  */
284 static BOOL PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
285                                        PropSheetInfo * psInfo)
286 {
287   DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
288   DWORD dwFlags = lppsh->dwFlags;
289
290   psInfo->hasHelp = dwFlags & PSH_HASHELP;
291   psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
292   psInfo->useCallback = (dwFlags & PSH_USECALLBACK )&& (lppsh->pfnCallback);
293   psInfo->isModeless = dwFlags & PSH_MODELESS;
294
295   memcpy(&psInfo->ppshheader,lppsh,dwSize);
296   TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%ld\ndwFlags\t\t%08lx\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
297         lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance,
298         debugstr_a(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
299
300   PROPSHEET_UnImplementedFlags(lppsh->dwFlags);
301
302   if (HIWORD(lppsh->pszCaption))
303   {
304      int len = strlen(lppsh->pszCaption);
305      psInfo->ppshheader.pszCaption = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof (WCHAR) );
306      MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, (LPWSTR) psInfo->ppshheader.pszCaption, len+1);
307      /* strcpy( (char *)psInfo->ppshheader.pszCaption, lppsh->pszCaption ); */
308   }
309   psInfo->nPages = lppsh->nPages;
310
311   if (dwFlags & PSH_USEPSTARTPAGE)
312   {
313     TRACE("PSH_USEPSTARTPAGE is on");
314     psInfo->active_page = 0;
315   }
316   else
317     psInfo->active_page = lppsh->u2.nStartPage;
318
319   if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
320      psInfo->active_page = 0;
321
322   psInfo->restartWindows = FALSE;
323   psInfo->rebootSystem = FALSE;
324   psInfo->hImageList = 0;
325   psInfo->activeValid = FALSE;
326
327   return TRUE;
328 }
329
330 /******************************************************************************
331  *            PROPSHEET_CollectSheetInfoW
332  *
333  * Collect relevant data.
334  */
335 static BOOL PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
336                                        PropSheetInfo * psInfo)
337 {
338   DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERW));
339   DWORD dwFlags = lppsh->dwFlags;
340
341   psInfo->hasHelp = dwFlags & PSH_HASHELP;
342   psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
343   psInfo->useCallback = (dwFlags & PSH_USECALLBACK) && (lppsh->pfnCallback);
344   psInfo->isModeless = dwFlags & PSH_MODELESS;
345
346   memcpy(&psInfo->ppshheader,lppsh,dwSize);
347   TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%ld\ndwFlags\t\t%08lx\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
348       lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, debugstr_w(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
349
350   PROPSHEET_UnImplementedFlags(lppsh->dwFlags);
351
352   if (HIWORD(lppsh->pszCaption))
353   {
354      int len = strlenW(lppsh->pszCaption);
355      psInfo->ppshheader.pszCaption = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof(WCHAR) );
356      strcpyW( (WCHAR *)psInfo->ppshheader.pszCaption, lppsh->pszCaption );
357   }
358   psInfo->nPages = lppsh->nPages;
359
360   if (dwFlags & PSH_USEPSTARTPAGE)
361   {
362     TRACE("PSH_USEPSTARTPAGE is on");
363     psInfo->active_page = 0;
364   }
365   else
366     psInfo->active_page = lppsh->u2.nStartPage;
367
368   if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
369      psInfo->active_page = 0;
370
371   psInfo->restartWindows = FALSE;
372   psInfo->rebootSystem = FALSE;
373   psInfo->hImageList = 0;
374   psInfo->activeValid = FALSE;
375
376   return TRUE;
377 }
378
379 /******************************************************************************
380  *            PROPSHEET_CollectPageInfo
381  *
382  * Collect property sheet data.
383  * With code taken from DIALOG_ParseTemplate32.
384  */
385 BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
386                                PropSheetInfo * psInfo,
387                                int index)
388 {
389   DLGTEMPLATE* pTemplate;
390   const WORD*  p;
391   DWORD dwFlags;
392   int width, height;
393
394   TRACE("\n");
395   psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
396   psInfo->proppage[index].hwndPage = 0;
397   psInfo->proppage[index].isDirty = FALSE;
398
399   /*
400    * Process property page flags.
401    */
402   dwFlags = lppsp->dwFlags;
403   psInfo->proppage[index].useCallback = (dwFlags & PSP_USECALLBACK) && (lppsp->pfnCallback);
404   psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
405   psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID);
406
407   /* as soon as we have a page with the help flag, set the sheet flag on */
408   if (psInfo->proppage[index].hasHelp)
409     psInfo->hasHelp = TRUE;
410
411   /*
412    * Process page template.
413    */
414   if (dwFlags & PSP_DLGINDIRECT)
415     pTemplate = (DLGTEMPLATE*)lppsp->u.pResource;
416   else
417   {
418     HRSRC hResource = FindResourceW(lppsp->hInstance,
419                                     lppsp->u.pszTemplate,
420                                     RT_DIALOGW);
421     HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
422                                      hResource);
423     pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
424   }
425
426   /*
427    * Extract the size of the page and the caption.
428    */
429   if (!pTemplate)
430       return FALSE;
431
432   p = (const WORD *)pTemplate;
433
434   if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
435   {
436     /* DLGTEMPLATEEX (not defined in any std. header file) */
437
438     p++;       /* dlgVer    */
439     p++;       /* signature */
440     p += 2;    /* help ID   */
441     p += 2;    /* ext style */
442     p += 2;    /* style     */
443   }
444   else
445   {
446     /* DLGTEMPLATE */
447
448     p += 2;    /* style     */
449     p += 2;    /* ext style */
450   }
451
452   p++;    /* nb items */
453   p++;    /*   x      */
454   p++;    /*   y      */
455   width  = (WORD)*p; p++;
456   height = (WORD)*p; p++;
457
458   /* remember the largest width and height */
459   if (width > psInfo->width)
460     psInfo->width = width;
461
462   if (height > psInfo->height)
463     psInfo->height = height;
464
465   /* menu */
466   switch ((WORD)*p)
467   {
468     case 0x0000:
469       p++;
470       break;
471     case 0xffff:
472       p += 2;
473       break;
474     default:
475       p += lstrlenW( (LPCWSTR)p ) + 1;
476       break;
477   }
478
479   /* class */
480   switch ((WORD)*p)
481   {
482     case 0x0000:
483       p++;
484       break;
485     case 0xffff:
486       p += 2;
487       break;
488     default:
489       p += lstrlenW( (LPCWSTR)p ) + 1;
490       break;
491   }
492
493   /* Extract the caption */
494   psInfo->proppage[index].pszText = (LPCWSTR)p;
495   TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p));
496   p += lstrlenW((LPCWSTR)p) + 1;
497
498   if (dwFlags & PSP_USETITLE)
499   {
500     WCHAR szTitle[256];
501     const WCHAR *pTitle;
502     static WCHAR pszNull[] = { '(','n','u','l','l',')',0 };
503     int len;
504
505     if ( !HIWORD( lppsp->pszTitle ) )
506     {
507       if (!LoadStringW( lppsp->hInstance, (UINT)lppsp->pszTitle,szTitle,sizeof szTitle ))
508       {
509         pTitle = pszNull;
510         FIXME("Could not load resource #%04x?\n",LOWORD(lppsp->pszTitle));
511       }
512       else
513         pTitle = szTitle;
514     }
515     else
516       pTitle = lppsp->pszTitle;
517
518     len = strlenW(pTitle);
519     psInfo->proppage[index].pszText = COMCTL32_Alloc( (len+1)*sizeof (WCHAR) );
520     strcpyW( (LPWSTR)psInfo->proppage[index].pszText,pTitle);
521   }
522
523   /*
524    * Build the image list for icons
525    */
526   if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
527   {
528     HICON hIcon;
529     int icon_cx = GetSystemMetrics(SM_CXSMICON);
530     int icon_cy = GetSystemMetrics(SM_CYSMICON);
531
532     if (dwFlags & PSP_USEICONID)
533       hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
534                          icon_cx, icon_cy, LR_DEFAULTCOLOR);
535     else
536       hIcon = lppsp->u2.hIcon;
537
538     if ( hIcon )
539     {
540       if (psInfo->hImageList == 0 )
541         psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);
542
543       ImageList_AddIcon(psInfo->hImageList, hIcon);
544     }
545
546   }
547
548   return TRUE;
549 }
550
551 /******************************************************************************
552  *            PROPSHEET_CreateDialog
553  *
554  * Creates the actual property sheet.
555  */
556 BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
557 {
558   LRESULT ret;
559   LPCVOID template;
560   LPVOID temp = 0;
561   HRSRC hRes;
562   DWORD resSize;
563   WORD resID = IDD_PROPSHEET;
564
565   TRACE("\n");
566   if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
567     resID = IDD_WIZARD;
568
569   if(!(hRes = FindResourceW(COMCTL32_hModule,
570                             MAKEINTRESOURCEW(resID),
571                             RT_DIALOGW)))
572     return FALSE;
573
574   if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
575     return FALSE;
576
577   /*
578    * Make a copy of the dialog template.
579    */
580   resSize = SizeofResource(COMCTL32_hModule, hRes);
581
582   temp = COMCTL32_Alloc(resSize);
583
584   if (!temp)
585     return FALSE;
586
587   memcpy(temp, template, resSize);
588
589   if (psInfo->useCallback)
590     (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
591
592   if (!(psInfo->ppshheader.dwFlags & PSH_MODELESS))
593       ret = DialogBoxIndirectParamW(psInfo->ppshheader.hInstance,
594                                     (LPDLGTEMPLATEW) temp,
595                                     psInfo->ppshheader.hwndParent,
596                                     PROPSHEET_DialogProc,
597                                     (LPARAM)psInfo);
598   else
599       ret = CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
600                                        (LPDLGTEMPLATEW) temp,
601                                        psInfo->ppshheader.hwndParent,
602                                        PROPSHEET_DialogProc,
603                                        (LPARAM)psInfo) ? TRUE : FALSE;
604
605   COMCTL32_Free(temp);
606
607   return ret;
608 }
609
610 /******************************************************************************
611  *            PROPSHEET_SizeMismatch
612  *
613  *     Verify that the tab control and the "largest" property sheet page dlg. template
614  *     match in size.
615  */
616 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo)
617 {
618   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
619   RECT rcOrigTab, rcPage;
620
621   /*
622    * Original tab size.
623    */
624   GetClientRect(hwndTabCtrl, &rcOrigTab);
625   TRACE("orig tab %ld %ld %ld %ld\n", rcOrigTab.left, rcOrigTab.top,
626         rcOrigTab.right, rcOrigTab.bottom);
627
628   /*
629    * Biggest page size.
630    */
631   rcPage.left   = psInfo->x;
632   rcPage.top    = psInfo->y;
633   rcPage.right  = psInfo->width;
634   rcPage.bottom = psInfo->height;
635
636   MapDialogRect(hwndDlg, &rcPage);
637   TRACE("biggest page %ld %ld %ld %ld\n", rcPage.left, rcPage.top,
638         rcPage.right, rcPage.bottom);
639
640   if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
641     return TRUE;
642   if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
643     return TRUE;
644
645   return FALSE;
646 }
647
648 /******************************************************************************
649  *            PROPSHEET_IsTooSmallWizard
650  *
651  * Verify that the default property sheet is big enough.
652  */
653 static BOOL PROPSHEET_IsTooSmallWizard(HWND hwndDlg, PropSheetInfo* psInfo)
654 {
655   RECT rcSheetRect, rcPage, rcLine, rcSheetClient;
656   HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
657   PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
658
659   GetClientRect(hwndDlg, &rcSheetClient);
660   GetWindowRect(hwndDlg, &rcSheetRect);
661   GetWindowRect(hwndLine, &rcLine);
662
663   /* Remove the space below the sunken line */
664   rcSheetClient.bottom -= (rcSheetRect.bottom - rcLine.top);
665
666   /* Remove the buffer zone all around the edge */
667   rcSheetClient.bottom -= (padding.y * 2);
668   rcSheetClient.right -= (padding.x * 2);
669
670   /*
671    * Biggest page size.
672    */
673   rcPage.left   = psInfo->x;
674   rcPage.top    = psInfo->y;
675   rcPage.right  = psInfo->width;
676   rcPage.bottom = psInfo->height;
677
678   MapDialogRect(hwndDlg, &rcPage);
679   TRACE("biggest page %ld %ld %ld %ld\n", rcPage.left, rcPage.top,
680         rcPage.right, rcPage.bottom);
681
682   if (rcPage.right > rcSheetClient.right)
683     return TRUE;
684
685   if (rcPage.bottom > rcSheetClient.bottom)
686     return TRUE;
687
688   return FALSE;
689 }
690
691 /******************************************************************************
692  *            PROPSHEET_AdjustSize
693  *
694  * Resizes the property sheet and the tab control to fit the largest page.
695  */
696 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
697 {
698   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
699   HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
700   RECT rc,tabRect;
701   int tabOffsetX, tabOffsetY, buttonHeight;
702   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
703   RECT units;
704
705   /* Get the height of buttons */
706   GetClientRect(hwndButton, &rc);
707   buttonHeight = rc.bottom;
708
709   /*
710    * Biggest page size.
711    */
712   rc.left   = psInfo->x;
713   rc.top    = psInfo->y;
714   rc.right  = psInfo->width;
715   rc.bottom = psInfo->height;
716
717   MapDialogRect(hwndDlg, &rc);
718
719   /* retrieve the dialog units */
720   units.left = units.right = 4;
721   units.top = units.bottom = 8;
722   MapDialogRect(hwndDlg, &units);
723
724   /*
725    * Resize the tab control.
726    */
727   GetClientRect(hwndTabCtrl,&tabRect);
728
729   SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
730
731   if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
732   {
733       rc.bottom = rc.top + tabRect.bottom - tabRect.top;
734       psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
735   }
736
737   if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
738   {
739       rc.right = rc.left + tabRect.right - tabRect.left;
740       psInfo->width  = MulDiv((rc.right - rc.left),4,units.left);
741   }
742
743   SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
744
745   tabOffsetX = -(rc.left);
746   tabOffsetY = -(rc.top);
747
748   rc.right -= rc.left;
749   rc.bottom -= rc.top;
750   TRACE("setting tab %08lx, rc (0,0)-(%ld,%ld)\n",
751         (DWORD)hwndTabCtrl, rc.right, rc.bottom);
752   SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
753                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
754
755   GetClientRect(hwndTabCtrl, &rc);
756
757   TRACE("tab client rc %ld %ld %ld %ld\n",
758         rc.left, rc.top, rc.right, rc.bottom);
759
760   rc.right += ((padding.x * 2) + tabOffsetX);
761   rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
762
763   /*
764    * Resize the property sheet.
765    */
766   TRACE("setting dialog %08lx, rc (0,0)-(%ld,%ld)\n",
767         (DWORD)hwndDlg, rc.right, rc.bottom);
768   SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
769                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
770   return TRUE;
771 }
772
773 /******************************************************************************
774  *            PROPSHEET_AdjustSizeWizard
775  *
776  * Resizes the property sheet to fit the largest page.
777  */
778 static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, PropSheetInfo* psInfo)
779 {
780   HWND hwndButton = GetDlgItem(hwndDlg, IDCANCEL);
781   HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
782   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
783   RECT rc,tabRect;
784   int buttonHeight, lineHeight;
785   PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
786   RECT units;
787
788   /* Get the height of buttons */
789   GetClientRect(hwndButton, &rc);
790   buttonHeight = rc.bottom;
791
792   GetClientRect(hwndLine, &rc);
793   lineHeight = rc.bottom;
794
795   /* retrieve the dialog units */
796   units.left = units.right = 4;
797   units.top = units.bottom = 8;
798   MapDialogRect(hwndDlg, &units);
799
800   /*
801    * Biggest page size.
802    */
803   rc.left   = psInfo->x;
804   rc.top    = psInfo->y;
805   rc.right  = psInfo->width;
806   rc.bottom = psInfo->height;
807
808   MapDialogRect(hwndDlg, &rc);
809
810   GetClientRect(hwndTabCtrl,&tabRect);
811
812   if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
813   {
814       rc.bottom = rc.top + tabRect.bottom - tabRect.top;
815       psInfo->height = MulDiv((rc.bottom - rc.top), 8, units.top);
816   }
817
818   if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
819   {
820       rc.right = rc.left + tabRect.right - tabRect.left;
821       psInfo->width  = MulDiv((rc.right - rc.left), 4, units.left);
822   }
823
824   TRACE("Biggest page %ld %ld %ld %ld\n", rc.left, rc.top, rc.right, rc.bottom);
825   TRACE("   constants padx=%d, pady=%d, butH=%d, lH=%d\n",
826         padding.x, padding.y, buttonHeight, lineHeight);
827
828   /* Make room */
829   rc.right += (padding.x * 2);
830   rc.bottom += (buttonHeight + (5 * padding.y) + lineHeight);
831
832   /*
833    * Resize the property sheet.
834    */
835   TRACE("setting dialog %08lx, rc (0,0)-(%ld,%ld)\n",
836         (DWORD)hwndDlg, rc.right, rc.bottom);
837   SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
838                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
839   return TRUE;
840 }
841
842 /******************************************************************************
843  *            PROPSHEET_AdjustButtons
844  *
845  * Adjusts the buttons' positions.
846  */
847 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo)
848 {
849   HWND hwndButton = GetDlgItem(hwndParent, IDOK);
850   RECT rcSheet;
851   int x, y;
852   int num_buttons = 2;
853   int buttonWidth, buttonHeight;
854   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
855
856   if (psInfo->hasApply)
857     num_buttons++;
858
859   if (psInfo->hasHelp)
860     num_buttons++;
861
862   /*
863    * Obtain the size of the buttons.
864    */
865   GetClientRect(hwndButton, &rcSheet);
866   buttonWidth = rcSheet.right;
867   buttonHeight = rcSheet.bottom;
868
869   /*
870    * Get the size of the property sheet.
871    */
872   GetClientRect(hwndParent, &rcSheet);
873
874   /*
875    * All buttons will be at this y coordinate.
876    */
877   y = rcSheet.bottom - (padding.y + buttonHeight);
878
879   /*
880    * Position OK button.
881    */
882   hwndButton = GetDlgItem(hwndParent, IDOK);
883
884   x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
885
886   SetWindowPos(hwndButton, 0, x, y, 0, 0,
887                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
888
889   /*
890    * Position Cancel button.
891    */
892   hwndButton = GetDlgItem(hwndParent, IDCANCEL);
893
894   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
895
896   SetWindowPos(hwndButton, 0, x, y, 0, 0,
897                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
898
899   /*
900    * Position Apply button.
901    */
902   hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
903
904   if (psInfo->hasApply)
905   {
906     if (psInfo->hasHelp)
907       x = rcSheet.right - ((padding.x + buttonWidth) * 2);
908     else
909       x = rcSheet.right - (padding.x + buttonWidth);
910
911     SetWindowPos(hwndButton, 0, x, y, 0, 0,
912                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
913
914     EnableWindow(hwndButton, FALSE);
915   }
916   else
917     ShowWindow(hwndButton, SW_HIDE);
918
919   /*
920    * Position Help button.
921    */
922   hwndButton = GetDlgItem(hwndParent, IDHELP);
923
924   if (psInfo->hasHelp)
925   {
926     x = rcSheet.right - (padding.x + buttonWidth);
927
928     SetWindowPos(hwndButton, 0, x, y, 0, 0,
929                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
930   }
931   else
932     ShowWindow(hwndButton, SW_HIDE);
933
934   return TRUE;
935 }
936
937 /******************************************************************************
938  *            PROPSHEET_AdjustButtonsWizard
939  *
940  * Adjusts the buttons' positions.
941  */
942 static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
943                                           PropSheetInfo* psInfo)
944 {
945   HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
946   HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
947   RECT rcSheet;
948   int x, y;
949   int num_buttons = 3;
950   int buttonWidth, buttonHeight, lineHeight, lineWidth;
951   PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
952
953   if (psInfo->hasHelp)
954     num_buttons++;
955
956   /*
957    * Obtain the size of the buttons.
958    */
959   GetClientRect(hwndButton, &rcSheet);
960   buttonWidth = rcSheet.right;
961   buttonHeight = rcSheet.bottom;
962
963   GetClientRect(hwndLine, &rcSheet);
964   lineHeight = rcSheet.bottom;
965
966   /*
967    * Get the size of the property sheet.
968    */
969   GetClientRect(hwndParent, &rcSheet);
970
971   /*
972    * All buttons will be at this y coordinate.
973    */
974   y = rcSheet.bottom - (padding.y + buttonHeight);
975
976   /*
977    * Position the Next and the Finish buttons.
978    */
979   hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
980
981   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
982
983   SetWindowPos(hwndButton, 0, x, y, 0, 0,
984                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
985
986   hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
987
988   SetWindowPos(hwndButton, 0, x, y, 0, 0,
989                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
990
991   ShowWindow(hwndButton, SW_HIDE);
992
993   /*
994    * Position the Back button.
995    */
996   hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
997
998   x -= buttonWidth;
999
1000   SetWindowPos(hwndButton, 0, x, y, 0, 0,
1001                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1002
1003   /*
1004    * Position the Cancel button.
1005    */
1006   hwndButton = GetDlgItem(hwndParent, IDCANCEL);
1007
1008   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 2));
1009
1010   SetWindowPos(hwndButton, 0, x, y, 0, 0,
1011                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1012
1013   /*
1014    * Position Help button.
1015    */
1016   hwndButton = GetDlgItem(hwndParent, IDHELP);
1017
1018   if (psInfo->hasHelp)
1019   {
1020     x = rcSheet.right - (padding.x + buttonWidth);
1021
1022     SetWindowPos(hwndButton, 0, x, y, 0, 0,
1023                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1024   }
1025   else
1026     ShowWindow(hwndButton, SW_HIDE);
1027
1028   /*
1029    * Position and resize the sunken line.
1030    */
1031   x = padding.x;
1032   y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight);
1033
1034   GetClientRect(hwndParent, &rcSheet);
1035   lineWidth = rcSheet.right - (padding.x * 2);
1036
1037   SetWindowPos(hwndLine, 0, x, y, lineWidth, 2,
1038                SWP_NOZORDER | SWP_NOACTIVATE);
1039
1040   return TRUE;
1041 }
1042
1043 /******************************************************************************
1044  *            PROPSHEET_GetPaddingInfo
1045  *
1046  * Returns the layout information.
1047  */
1048 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
1049 {
1050   HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1051   RECT rcTab;
1052   POINT tl;
1053   PADDING_INFO padding;
1054
1055   GetWindowRect(hwndTab, &rcTab);
1056
1057   tl.x = rcTab.left;
1058   tl.y = rcTab.top;
1059
1060   ScreenToClient(hwndDlg, &tl);
1061
1062   padding.x = tl.x;
1063   padding.y = tl.y;
1064
1065   return padding;
1066 }
1067
1068 /******************************************************************************
1069  *            PROPSHEET_GetPaddingInfoWizard
1070  *
1071  * Returns the layout information.
1072  * Vertical spacing is the distance between the line and the buttons.
1073  * Do NOT use the Help button to gather padding information when it isn't mapped
1074  * (PSH_HASHELP), as app writers aren't forced to supply correct coordinates
1075  * for it in this case !
1076  * FIXME: I'm not sure about any other coordinate problems with these evil
1077  * buttons. Fix it in case additional problems appear or maybe calculate
1078  * a padding in a completely different way, as this is somewhat messy.
1079  */
1080 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
1081  psInfo)
1082 {
1083   PADDING_INFO padding;
1084   RECT rc;
1085   HWND hwndControl;
1086   INT idButton;
1087   POINT ptButton, ptLine;
1088
1089   TRACE("\n");
1090   if (psInfo->hasHelp)
1091   {
1092         idButton = IDHELP;
1093   }
1094   else
1095   {
1096     if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1097     {
1098         idButton = IDC_NEXT_BUTTON;
1099     }
1100     else
1101     {
1102         /* hopefully this is ok */
1103         idButton = IDCANCEL;
1104     }
1105   }
1106
1107   hwndControl = GetDlgItem(hwndDlg, idButton);
1108   GetWindowRect(hwndControl, &rc);
1109
1110   ptButton.x = rc.left;
1111   ptButton.y = rc.top;
1112
1113   ScreenToClient(hwndDlg, &ptButton);
1114
1115   /* Line */
1116   hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
1117   GetWindowRect(hwndControl, &rc);
1118
1119   ptLine.x = 0;
1120   ptLine.y = rc.bottom;
1121
1122   ScreenToClient(hwndDlg, &ptLine);
1123
1124   padding.y = ptButton.y - ptLine.y;
1125
1126   if (padding.y < 0)
1127           ERR("padding negative ! Please report this !\n");
1128
1129   /* this is most probably not correct, but the best we have now */
1130   padding.x = padding.y;
1131   return padding;
1132 }
1133
1134 /******************************************************************************
1135  *            PROPSHEET_CreateTabControl
1136  *
1137  * Insert the tabs in the tab control.
1138  */
1139 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
1140                                        PropSheetInfo * psInfo)
1141 {
1142   HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1143   TCITEMW item;
1144   int i, nTabs;
1145   int iImage = 0;
1146
1147   TRACE("\n");
1148   item.mask = TCIF_TEXT;
1149   item.cchTextMax = MAX_TABTEXT_LENGTH;
1150
1151   nTabs = psInfo->nPages;
1152
1153   /*
1154    * Set the image list for icons.
1155    */
1156   if (psInfo->hImageList)
1157   {
1158     SendMessageW(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1159   }
1160
1161   for (i = 0; i < nTabs; i++)
1162   {
1163     if ( psInfo->proppage[i].hasIcon )
1164     {
1165       item.mask |= TCIF_IMAGE;
1166       item.iImage = iImage++;
1167     }
1168     else
1169     {
1170       item.mask &= ~TCIF_IMAGE;
1171     }
1172
1173     item.pszText = (LPWSTR) psInfo->proppage[i].pszText;
1174     SendMessageW(hwndTabCtrl, TCM_INSERTITEMW, (WPARAM)i, (LPARAM)&item);
1175   }
1176
1177   return TRUE;
1178 }
1179 /*
1180  * Get the size of an in-memory Template
1181  *
1182  *( Based on the code of PROPSHEET_CollectPageInfo)
1183  * See also dialog.c/DIALOG_ParseTemplate32().
1184  */
1185
1186 static UINT GetTemplateSize(DLGTEMPLATE* pTemplate)
1187
1188 {
1189   const WORD*  p = (const WORD *)pTemplate;
1190   BOOL  istemplateex = (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF);
1191   WORD nrofitems;
1192
1193   if (istemplateex)
1194   {
1195     /* DLGTEMPLATEEX (not defined in any std. header file) */
1196
1197     TRACE("is DLGTEMPLATEEX\n");
1198     p++;       /* dlgVer    */
1199     p++;       /* signature */
1200     p += 2;    /* help ID   */
1201     p += 2;    /* ext style */
1202     p += 2;    /* style     */
1203   }
1204   else
1205   {
1206     /* DLGTEMPLATE */
1207
1208     TRACE("is DLGTEMPLATE\n");
1209     p += 2;    /* style     */
1210     p += 2;    /* ext style */
1211   }
1212
1213   nrofitems =   (WORD)*p; p++;    /* nb items */
1214   p++;    /*   x      */
1215   p++;    /*   y      */
1216   p++;    /*   width  */
1217   p++;    /*   height */
1218
1219   /* menu */
1220   switch ((WORD)*p)
1221   {
1222     case 0x0000:
1223       p++;
1224       break;
1225     case 0xffff:
1226       p += 2;
1227       break;
1228     default:
1229       TRACE("menu %s\n",debugstr_w((LPCWSTR)p));
1230       p += lstrlenW( (LPCWSTR)p ) + 1;
1231       break;
1232   }
1233
1234   /* class */
1235   switch ((WORD)*p)
1236   {
1237     case 0x0000:
1238       p++;
1239       break;
1240     case 0xffff:
1241       p += 2; /* 0xffff plus predefined window class ordinal value */
1242       break;
1243     default:
1244       TRACE("class %s\n",debugstr_w((LPCWSTR)p));
1245       p += lstrlenW( (LPCWSTR)p ) + 1;
1246       break;
1247   }
1248
1249   /* title */
1250   TRACE("title %s\n",debugstr_w((LPCWSTR)p));
1251   p += lstrlenW((LPCWSTR)p) + 1;
1252
1253   /* font, if DS_SETFONT set */
1254   if ((DS_SETFONT & ((istemplateex)?  ((MyDLGTEMPLATEEX*)pTemplate)->style :
1255                      pTemplate->style)))
1256     {
1257       p+=(istemplateex)?3:1;
1258       TRACE("font %s\n",debugstr_w((LPCWSTR)p));
1259       p += lstrlenW( (LPCWSTR)p ) + 1; /* the font name */
1260     }
1261
1262   /* now process the DLGITEMTEMPLATE(EX) structs (plus custom data)
1263    * that are following the DLGTEMPLATE(EX) data */
1264   TRACE("%d items\n",nrofitems);
1265   while (nrofitems > 0)
1266     {
1267       p = (WORD*)(((DWORD)p + 3) & ~3); /* DWORD align */
1268       
1269       /* skip header */
1270       p += (istemplateex ? sizeof(MyDLGITEMTEMPLATEEX) : sizeof(DLGITEMTEMPLATE))/sizeof(WORD);
1271       
1272       /* check class */
1273       switch ((WORD)*p)
1274         {
1275         case 0x0000:
1276           p++;
1277           break;
1278         case 0xffff:
1279           TRACE("class ordinal 0x%08lx\n",*(DWORD*)p);
1280           p += 2;
1281           break;
1282         default:
1283           TRACE("class %s\n",debugstr_w((LPCWSTR)p));
1284           p += lstrlenW( (LPCWSTR)p ) + 1;
1285           break;
1286         }
1287
1288       /* check title text */
1289       switch ((WORD)*p)
1290         {
1291         case 0x0000:
1292           p++;
1293           break;
1294         case 0xffff:
1295           TRACE("text ordinal 0x%08lx\n",*(DWORD*)p);
1296           p += 2;
1297           break;
1298         default:
1299           TRACE("text %s\n",debugstr_w((LPCWSTR)p));
1300           p += lstrlenW( (LPCWSTR)p ) + 1;
1301           break;
1302         }
1303       p += *p + 1;    /* Skip extra data */
1304       --nrofitems;
1305     }
1306   
1307   TRACE("%p %p size 0x%08x\n",p, (WORD*)pTemplate,sizeof(WORD)*(p - (WORD*)pTemplate));
1308   return (p - (WORD*)pTemplate)*sizeof(WORD);
1309   
1310 }
1311
1312 /******************************************************************************
1313  *            PROPSHEET_CreatePage
1314  *
1315  * Creates a page.
1316  */
1317 static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1318                                 int index,
1319                                 const PropSheetInfo * psInfo,
1320                                 LPCPROPSHEETPAGEW ppshpage)
1321 {
1322   DLGTEMPLATE* pTemplate;
1323   HWND hwndPage;
1324   RECT rc;
1325   PropPageInfo* ppInfo = psInfo->proppage;
1326   PADDING_INFO padding;
1327   UINT pageWidth,pageHeight;
1328   DWORD resSize;
1329   LPVOID temp = NULL;
1330
1331   TRACE("index %d\n", index);
1332
1333   if (ppshpage == NULL)
1334   {
1335     return FALSE;
1336   }
1337
1338   if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1339     {
1340       pTemplate = (DLGTEMPLATE*)ppshpage->u.pResource;
1341       resSize = GetTemplateSize(pTemplate);
1342     }
1343   else
1344   {
1345     HRSRC hResource;
1346     HANDLE hTemplate;
1347
1348     hResource = FindResourceW(ppshpage->hInstance,
1349                                     ppshpage->u.pszTemplate,
1350                                     RT_DIALOGW);
1351     if(!hResource)
1352         return FALSE;
1353
1354     resSize = SizeofResource(ppshpage->hInstance, hResource);
1355
1356     hTemplate = LoadResource(ppshpage->hInstance, hResource);
1357     if(!hTemplate)
1358         return FALSE;
1359
1360     pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
1361     /*
1362      * Make a copy of the dialog template to make it writable
1363      */
1364   }
1365   temp = COMCTL32_Alloc(resSize);
1366   if (!temp)
1367     return FALSE;
1368   
1369   TRACE("copying pTemplate %p into temp %p (%ld)\n", pTemplate, temp, resSize);
1370   memcpy(temp, pTemplate, resSize);
1371   pTemplate = temp;
1372
1373   if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
1374   {
1375     ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD | DS_CONTROL;
1376     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME;
1377     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION;
1378     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU;
1379     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP;
1380     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_DISABLED;
1381     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_VISIBLE;
1382     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_THICKFRAME;
1383   }
1384   else
1385   {
1386     pTemplate->style |= WS_CHILD | DS_CONTROL;
1387     pTemplate->style &= ~DS_MODALFRAME;
1388     pTemplate->style &= ~WS_CAPTION;
1389     pTemplate->style &= ~WS_SYSMENU;
1390     pTemplate->style &= ~WS_POPUP;
1391     pTemplate->style &= ~WS_DISABLED;
1392     pTemplate->style &= ~WS_VISIBLE;
1393     pTemplate->style &= ~WS_THICKFRAME;
1394   }
1395
1396   if (psInfo->proppage[index].useCallback)
1397     (*(ppshpage->pfnCallback))(hwndParent,
1398                                PSPCB_CREATE,
1399                                (LPPROPSHEETPAGEW)ppshpage);
1400
1401   hwndPage = CreateDialogIndirectParamW(ppshpage->hInstance,
1402                                         pTemplate,
1403                                         hwndParent,
1404                                         ppshpage->pfnDlgProc,
1405                                         (LPARAM)ppshpage);
1406   /* Free a no more needed copy */
1407   if(temp)
1408       COMCTL32_Free(temp);
1409
1410   ppInfo[index].hwndPage = hwndPage;
1411
1412   if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) {
1413       /* FIXME: This code may no longer be correct.
1414        *        It was not for the non-wizard path.  (GLA 6/02)
1415        */
1416       rc.left = psInfo->x;
1417       rc.top = psInfo->y;
1418       rc.right = psInfo->width;
1419       rc.bottom = psInfo->height;
1420
1421       MapDialogRect(hwndParent, &rc);
1422
1423       pageWidth = rc.right - rc.left;
1424       pageHeight = rc.bottom - rc.top;
1425
1426       padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
1427       TRACE("setting page %08lx, rc (%ld,%ld)-(%ld,%ld) w=%d, h=%d, padx=%d, pady=%d\n",
1428             (DWORD)hwndPage, rc.left, rc.top, rc.right, rc.bottom,
1429             pageWidth, pageHeight, padding.x, padding.y);
1430       SetWindowPos(hwndPage, HWND_TOP,
1431                    rc.left + padding.x/2,
1432                    rc.top + padding.y/2,
1433                    pageWidth, pageHeight, 0);
1434   }
1435   else {
1436       /*
1437        * Ask the Tab control to reduce the client rectangle to that
1438        * it has available.
1439        */
1440       PROPSHEET_GetPageRect(psInfo, hwndParent, &rc);
1441       pageWidth = rc.right - rc.left;
1442       pageHeight = rc.bottom - rc.top;
1443       TRACE("setting page %08lx, rc (%ld,%ld)-(%ld,%ld) w=%d, h=%d\n",
1444             (DWORD)hwndPage, rc.left, rc.top, rc.right, rc.bottom,
1445             pageWidth, pageHeight);
1446       SetWindowPos(hwndPage, HWND_TOP,
1447                    rc.left, rc.top,
1448                    pageWidth, pageHeight, 0);
1449   }
1450
1451   return TRUE;
1452 }
1453
1454 /******************************************************************************
1455  *            PROPSHEET_ShowPage
1456  *
1457  * Displays or creates the specified page.
1458  */
1459 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
1460 {
1461   HWND hwndTabCtrl;
1462
1463   TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1464   if (index == psInfo->active_page)
1465   {
1466       if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
1467           SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1468       return TRUE;
1469   }
1470
1471   if (psInfo->proppage[index].hwndPage == 0)
1472   {
1473      LPCPROPSHEETPAGEW ppshpage;
1474
1475      ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1476      PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1477   }
1478
1479   if (psInfo->active_page != -1)
1480      ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1481
1482   ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1483
1484   /* Synchronize current selection with tab control
1485    * It seems to be needed even in case of PSH_WIZARD (no tab controls there) */
1486   hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1487   SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1488
1489   psInfo->active_page = index;
1490   psInfo->activeValid = TRUE;
1491
1492   return TRUE;
1493 }
1494
1495 /******************************************************************************
1496  *            PROPSHEET_Back
1497  */
1498 static BOOL PROPSHEET_Back(HWND hwndDlg)
1499 {
1500   PSHNOTIFY psn;
1501   HWND hwndPage;
1502   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1503                                                     PropSheetInfoStr);
1504   LRESULT result;
1505   int idx;
1506
1507   TRACE("active_page %d\n", psInfo->active_page);
1508   if (psInfo->active_page < 0)
1509      return FALSE;
1510
1511   psn.hdr.code     = PSN_WIZBACK;
1512   psn.hdr.hwndFrom = hwndDlg;
1513   psn.hdr.idFrom   = 0;
1514   psn.lParam       = 0;
1515
1516   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1517
1518   result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1519   if (result == -1)
1520     return FALSE;
1521   else if (result == 0)
1522      idx = psInfo->active_page - 1;
1523   else
1524      idx = PROPSHEET_FindPageByResId(psInfo, result);
1525
1526   if (idx >= 0 && idx < psInfo->nPages)
1527   {
1528      if (PROPSHEET_CanSetCurSel(hwndDlg))
1529         PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1530   }
1531   return TRUE;
1532 }
1533
1534 /******************************************************************************
1535  *            PROPSHEET_Next
1536  */
1537 static BOOL PROPSHEET_Next(HWND hwndDlg)
1538 {
1539   PSHNOTIFY psn;
1540   HWND hwndPage;
1541   LRESULT msgResult = 0;
1542   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1543                                                     PropSheetInfoStr);
1544   int idx;
1545
1546   TRACE("active_page %d\n", psInfo->active_page);
1547   if (psInfo->active_page < 0)
1548      return FALSE;
1549
1550   psn.hdr.code     = PSN_WIZNEXT;
1551   psn.hdr.hwndFrom = hwndDlg;
1552   psn.hdr.idFrom   = 0;
1553   psn.lParam       = 0;
1554
1555   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1556
1557   msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1558   if (msgResult == -1)
1559     return FALSE;
1560   else if (msgResult == 0)
1561      idx = psInfo->active_page + 1;
1562   else
1563      idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1564
1565   if (idx < psInfo->nPages )
1566   {
1567      if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1568         PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1569   }
1570
1571   return TRUE;
1572 }
1573
1574 /******************************************************************************
1575  *            PROPSHEET_Finish
1576  */
1577 static BOOL PROPSHEET_Finish(HWND hwndDlg)
1578 {
1579   PSHNOTIFY psn;
1580   HWND hwndPage;
1581   LRESULT msgResult = 0;
1582   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1583                                                     PropSheetInfoStr);
1584
1585   TRACE("active_page %d\n", psInfo->active_page);
1586   if (psInfo->active_page < 0)
1587      return FALSE;
1588
1589   psn.hdr.code     = PSN_WIZFINISH;
1590   psn.hdr.hwndFrom = hwndDlg;
1591   psn.hdr.idFrom   = 0;
1592   psn.lParam       = 0;
1593
1594   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1595
1596   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1597
1598   TRACE("msg result %ld\n", msgResult);
1599
1600   if (msgResult != 0)
1601     return FALSE;
1602
1603   if (psInfo->isModeless)
1604     psInfo->activeValid = FALSE;
1605   else
1606     EndDialog(hwndDlg, TRUE);
1607
1608   return TRUE;
1609 }
1610
1611 /******************************************************************************
1612  *            PROPSHEET_Apply
1613  */
1614 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1615 {
1616   int i;
1617   HWND hwndPage;
1618   PSHNOTIFY psn;
1619   LRESULT msgResult;
1620   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1621                                                     PropSheetInfoStr);
1622
1623   TRACE("active_page %d\n", psInfo->active_page);
1624   if (psInfo->active_page < 0)
1625      return FALSE;
1626
1627   psn.hdr.hwndFrom = hwndDlg;
1628   psn.hdr.idFrom   = 0;
1629   psn.lParam       = 0;
1630
1631
1632   /*
1633    * Send PSN_KILLACTIVE to the current page.
1634    */
1635   psn.hdr.code = PSN_KILLACTIVE;
1636
1637   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1638
1639   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1640     return FALSE;
1641
1642   /*
1643    * Send PSN_APPLY to all pages.
1644    */
1645   psn.hdr.code = PSN_APPLY;
1646   psn.lParam   = lParam;
1647
1648   for (i = 0; i < psInfo->nPages; i++)
1649   {
1650     hwndPage = psInfo->proppage[i].hwndPage;
1651     if (hwndPage)
1652     {
1653        msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1654        if (msgResult == PSNRET_INVALID_NOCHANGEPAGE)
1655           return FALSE;
1656     }
1657   }
1658
1659   if(lParam)
1660   {
1661      psInfo->activeValid = FALSE;
1662   }
1663   else if(psInfo->active_page >= 0)
1664   {
1665      psn.hdr.code = PSN_SETACTIVE;
1666      psn.lParam   = 0;
1667      hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1668      SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1669   }
1670
1671   return TRUE;
1672 }
1673
1674 /******************************************************************************
1675  *            PROPSHEET_Cancel
1676  */
1677 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1678 {
1679   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1680                                                     PropSheetInfoStr);
1681   HWND hwndPage;
1682   PSHNOTIFY psn;
1683   int i;
1684
1685   TRACE("active_page %d\n", psInfo->active_page);
1686   if (psInfo->active_page < 0)
1687      return;
1688
1689   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1690   psn.hdr.code     = PSN_QUERYCANCEL;
1691   psn.hdr.hwndFrom = hwndDlg;
1692   psn.hdr.idFrom   = 0;
1693   psn.lParam       = 0;
1694
1695   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1696     return;
1697
1698   psn.hdr.code = PSN_RESET;
1699   psn.lParam   = lParam;
1700
1701   for (i = 0; i < psInfo->nPages; i++)
1702   {
1703     hwndPage = psInfo->proppage[i].hwndPage;
1704
1705     if (hwndPage)
1706        SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1707   }
1708
1709   if (psInfo->isModeless)
1710   {
1711      /* makes PSM_GETCURRENTPAGEHWND return NULL */
1712      psInfo->activeValid = FALSE;
1713   }
1714   else
1715     EndDialog(hwndDlg, FALSE);
1716 }
1717
1718 /******************************************************************************
1719  *            PROPSHEET_Help
1720  */
1721 static void PROPSHEET_Help(HWND hwndDlg)
1722 {
1723   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1724                                                     PropSheetInfoStr);
1725   HWND hwndPage;
1726   PSHNOTIFY psn;
1727
1728   TRACE("active_page %d\n", psInfo->active_page);
1729   if (psInfo->active_page < 0)
1730      return;
1731
1732   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1733   psn.hdr.code     = PSN_HELP;
1734   psn.hdr.hwndFrom = hwndDlg;
1735   psn.hdr.idFrom   = 0;
1736   psn.lParam       = 0;
1737
1738   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1739 }
1740
1741 /******************************************************************************
1742  *            PROPSHEET_Changed
1743  */
1744 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1745 {
1746   int i;
1747   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1748                                                     PropSheetInfoStr);
1749
1750   TRACE("\n");
1751   if (!psInfo) return;
1752   /*
1753    * Set the dirty flag of this page.
1754    */
1755   for (i = 0; i < psInfo->nPages; i++)
1756   {
1757     if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1758       psInfo->proppage[i].isDirty = TRUE;
1759   }
1760
1761   /*
1762    * Enable the Apply button.
1763    */
1764   if (psInfo->hasApply)
1765   {
1766     HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1767
1768     EnableWindow(hwndApplyBtn, TRUE);
1769   }
1770 }
1771
1772 /******************************************************************************
1773  *            PROPSHEET_UnChanged
1774  */
1775 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1776 {
1777   int i;
1778   BOOL noPageDirty = TRUE;
1779   HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1780   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1781                                                     PropSheetInfoStr);
1782
1783   TRACE("\n");
1784   if ( !psInfo ) return;
1785   for (i = 0; i < psInfo->nPages; i++)
1786   {
1787     /* set the specified page as clean */
1788     if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1789       psInfo->proppage[i].isDirty = FALSE;
1790
1791     /* look to see if there's any dirty pages */
1792     if (psInfo->proppage[i].isDirty)
1793       noPageDirty = FALSE;
1794   }
1795
1796   /*
1797    * Disable Apply button.
1798    */
1799   if (noPageDirty)
1800     EnableWindow(hwndApplyBtn, FALSE);
1801 }
1802
1803 /******************************************************************************
1804  *            PROPSHEET_PressButton
1805  */
1806 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1807 {
1808   TRACE("buttonID %d\n", buttonID);
1809   switch (buttonID)
1810   {
1811     case PSBTN_APPLYNOW:
1812       PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1813       break;
1814     case PSBTN_BACK:
1815       PROPSHEET_Back(hwndDlg);
1816       break;
1817     case PSBTN_CANCEL:
1818       PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1819       break;
1820     case PSBTN_FINISH:
1821       PROPSHEET_Finish(hwndDlg);
1822       break;
1823     case PSBTN_HELP:
1824       PROPSHEET_DoCommand(hwndDlg, IDHELP);
1825       break;
1826     case PSBTN_NEXT:
1827       PROPSHEET_Next(hwndDlg);
1828       break;
1829     case PSBTN_OK:
1830       PROPSHEET_DoCommand(hwndDlg, IDOK);
1831       break;
1832     default:
1833       FIXME("Invalid button index %d\n", buttonID);
1834   }
1835 }
1836
1837
1838 /*************************************************************************
1839  * BOOL PROPSHEET_CanSetCurSel [Internal]
1840  *
1841  * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1842  *
1843  * PARAMS
1844  *     hwndDlg        [I] handle to a Dialog hWnd
1845  *
1846  * RETURNS
1847  *     TRUE if Current Selection can change
1848  *
1849  * NOTES
1850  */
1851 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1852 {
1853   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1854                                                     PropSheetInfoStr);
1855   HWND hwndPage;
1856   PSHNOTIFY psn;
1857   BOOL res = FALSE;
1858
1859   TRACE("active_page %d\n", psInfo->active_page);
1860   if (!psInfo)
1861   {
1862      res = FALSE;
1863      goto end;
1864   }
1865
1866   if (psInfo->active_page < 0)
1867   {
1868      res = TRUE;
1869      goto end;
1870   }
1871
1872   /*
1873    * Notify the current page.
1874    */
1875   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1876   psn.hdr.code     = PSN_KILLACTIVE;
1877   psn.hdr.hwndFrom = hwndDlg;
1878   psn.hdr.idFrom   = 0;
1879   psn.lParam       = 0;
1880
1881   res = !SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1882
1883 end:
1884   TRACE("<-- %d\n", res);
1885   return res;
1886 }
1887
1888 /******************************************************************************
1889  *            PROPSHEET_SetCurSel
1890  */
1891 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
1892                                 int index,
1893                                 int skipdir,
1894                                 HPROPSHEETPAGE hpage
1895                                 )
1896 {
1897   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
1898   HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
1899   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1900
1901   TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
1902   /* hpage takes precedence over index */
1903   if (hpage != NULL)
1904     index = PROPSHEET_GetPageIndex(hpage, psInfo);
1905
1906   if (index < 0 || index >= psInfo->nPages)
1907   {
1908     TRACE("Could not find page to select!\n");
1909     return FALSE;
1910   }
1911
1912   while (1) {
1913     int result;
1914     PSHNOTIFY psn;
1915
1916     if (hwndTabControl)
1917         SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);
1918
1919     psn.hdr.code     = PSN_SETACTIVE;
1920     psn.hdr.hwndFrom = hwndDlg;
1921     psn.hdr.idFrom   = 0;
1922     psn.lParam       = 0;
1923
1924     if (!psInfo->proppage[index].hwndPage) {
1925       LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1926       PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1927     }
1928
1929     result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1930     if (!result)
1931       break;
1932     if (result == -1) {
1933       index+=skipdir;
1934       if (index < 0) {
1935         index = 0;
1936         FIXME("Tried to skip before first property sheet page!\n");
1937         break;
1938       }
1939       if (index >= psInfo->nPages) {
1940         FIXME("Tried to skip after last property sheet page!\n");
1941         index = psInfo->nPages-1;
1942         break;
1943       }
1944     }
1945     else if (result != 0)
1946     {
1947        index = PROPSHEET_FindPageByResId(psInfo, result);
1948        continue;
1949     }
1950   }
1951   /*
1952    * Display the new page.
1953    */
1954   PROPSHEET_ShowPage(hwndDlg, index, psInfo);
1955
1956   if (psInfo->proppage[index].hasHelp)
1957     EnableWindow(hwndHelp, TRUE);
1958   else
1959     EnableWindow(hwndHelp, FALSE);
1960
1961   return TRUE;
1962 }
1963
1964 /******************************************************************************
1965  *            PROPSHEET_SetCurSelId
1966  *
1967  * Selects the page, specified by resource id.
1968  */
1969 static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id)
1970 {
1971       int idx;
1972       PropSheetInfo* psInfo =
1973           (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
1974
1975       idx = PROPSHEET_FindPageByResId(psInfo, id);
1976       if (idx < psInfo->nPages )
1977       {
1978           if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1979               PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1980       }
1981 }
1982
1983 /******************************************************************************
1984  *            PROPSHEET_SetTitleA
1985  */
1986 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
1987 {
1988   if(HIWORD(lpszText))
1989   {
1990      WCHAR szTitle[256];
1991      MultiByteToWideChar(CP_ACP, 0, lpszText, -1,
1992                              szTitle, sizeof szTitle);
1993      PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
1994   }
1995   else
1996   {
1997      PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
1998   }
1999 }
2000
2001 /******************************************************************************
2002  *            PROPSHEET_SetTitleW
2003  */
2004 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
2005 {
2006   PropSheetInfo*        psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2007   WCHAR                 szTitle[256];
2008
2009   TRACE("'%s' (style %08lx)\n", debugstr_w(lpszText), dwStyle);
2010   if (HIWORD(lpszText) == 0) {
2011     if (!LoadStringW(psInfo->ppshheader.hInstance,
2012                      LOWORD(lpszText), szTitle, sizeof(szTitle)-sizeof(WCHAR)))
2013       return;
2014     lpszText = szTitle;
2015   }
2016   if (dwStyle & PSH_PROPTITLE)
2017   {
2018     WCHAR* dest;
2019     int lentitle = strlenW(lpszText);
2020     int lenprop  = strlenW(psInfo->strPropertiesFor);
2021
2022     dest = COMCTL32_Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2023     strcpyW(dest, psInfo->strPropertiesFor);
2024     strcatW(dest, lpszText);
2025
2026     SetWindowTextW(hwndDlg, dest);
2027     COMCTL32_Free(dest);
2028   }
2029   else
2030     SetWindowTextW(hwndDlg, lpszText);
2031 }
2032
2033 /******************************************************************************
2034  *            PROPSHEET_SetFinishTextA
2035  */
2036 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
2037 {
2038   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2039
2040   TRACE("'%s'\n", lpszText);
2041   /* Set text, show and enable the Finish button */
2042   SetWindowTextA(hwndButton, lpszText);
2043   ShowWindow(hwndButton, SW_SHOW);
2044   EnableWindow(hwndButton, TRUE);
2045
2046   /* Make it default pushbutton */
2047   SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2048
2049   /* Hide Back button */
2050   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2051   ShowWindow(hwndButton, SW_HIDE);
2052
2053   /* Hide Next button */
2054   hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2055   ShowWindow(hwndButton, SW_HIDE);
2056 }
2057
2058 /******************************************************************************
2059  *            PROPSHEET_SetFinishTextW
2060  */
2061 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
2062 {
2063   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2064
2065   TRACE("'%s'\n", debugstr_w(lpszText));
2066   /* Set text, show and enable the Finish button */
2067   SetWindowTextW(hwndButton, lpszText);
2068   ShowWindow(hwndButton, SW_SHOW);
2069   EnableWindow(hwndButton, TRUE);
2070
2071   /* Make it default pushbutton */
2072   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2073
2074   /* Hide Back button */
2075   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2076   ShowWindow(hwndButton, SW_HIDE);
2077
2078   /* Hide Next button */
2079   hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2080   ShowWindow(hwndButton, SW_HIDE);
2081 }
2082
2083 /******************************************************************************
2084  *            PROPSHEET_QuerySiblings
2085  */
2086 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
2087                                        WPARAM wParam, LPARAM lParam)
2088 {
2089   int i = 0;
2090   HWND hwndPage;
2091   LRESULT msgResult = 0;
2092   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2093                                                     PropSheetInfoStr);
2094
2095   while ((i < psInfo->nPages) && (msgResult == 0))
2096   {
2097     hwndPage = psInfo->proppage[i].hwndPage;
2098     msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2099     i++;
2100   }
2101
2102   return msgResult;
2103 }
2104
2105
2106 /******************************************************************************
2107  *            PROPSHEET_AddPage
2108  */
2109 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
2110                               HPROPSHEETPAGE hpage)
2111 {
2112   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2113                                                      PropSheetInfoStr);
2114   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2115   TCITEMW item;
2116   LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
2117
2118   TRACE("hpage %p\n", hpage);
2119   /*
2120    * Allocate and fill in a new PropPageInfo entry.
2121    */
2122   psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage,
2123                                                       sizeof(PropPageInfo) *
2124                                                       (psInfo->nPages + 1));
2125   if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages))
2126       return FALSE;
2127
2128   psInfo->proppage[psInfo->nPages].hpage = hpage;
2129
2130   if (ppsp->dwFlags & PSP_PREMATURE)
2131   {
2132      /* Create the page but don't show it */
2133      PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp);
2134   }
2135
2136   /*
2137    * Add a new tab to the tab control.
2138    */
2139   item.mask = TCIF_TEXT;
2140   item.pszText = (LPWSTR) psInfo->proppage[psInfo->nPages].pszText;
2141   item.cchTextMax = MAX_TABTEXT_LENGTH;
2142
2143   if (psInfo->hImageList)
2144   {
2145     SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
2146   }
2147
2148   if ( psInfo->proppage[psInfo->nPages].hasIcon )
2149   {
2150     item.mask |= TCIF_IMAGE;
2151     item.iImage = psInfo->nPages;
2152   }
2153
2154   SendMessageW(hwndTabControl, TCM_INSERTITEMW, psInfo->nPages + 1,
2155                (LPARAM)&item);
2156
2157   psInfo->nPages++;
2158
2159   /* If it is the only page - show it */
2160   if(psInfo->nPages == 1)
2161      PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2162   return TRUE;
2163 }
2164
2165 /******************************************************************************
2166  *            PROPSHEET_RemovePage
2167  */
2168 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
2169                                  int index,
2170                                  HPROPSHEETPAGE hpage)
2171 {
2172   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2173                                                      PropSheetInfoStr);
2174   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2175   PropPageInfo* oldPages;
2176
2177   TRACE("index %d, hpage %p\n", index, hpage);
2178   if (!psInfo) {
2179     return FALSE;
2180   }
2181   oldPages = psInfo->proppage;
2182   /*
2183    * hpage takes precedence over index.
2184    */
2185   if (hpage != 0)
2186   {
2187     index = PROPSHEET_GetPageIndex(hpage, psInfo);
2188   }
2189
2190   /* Make sure that index is within range */
2191   if (index < 0 || index >= psInfo->nPages)
2192   {
2193       TRACE("Could not find page to remove!\n");
2194       return FALSE;
2195   }
2196
2197   TRACE("total pages %d removing page %d active page %d\n",
2198         psInfo->nPages, index, psInfo->active_page);
2199   /*
2200    * Check if we're removing the active page.
2201    */
2202   if (index == psInfo->active_page)
2203   {
2204     if (psInfo->nPages > 1)
2205     {
2206       if (index > 0)
2207       {
2208         /* activate previous page  */
2209         PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2210       }
2211       else
2212       {
2213         /* activate the next page */
2214         PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2215         psInfo->active_page = index;
2216       }
2217     }
2218     else
2219     {
2220       psInfo->active_page = -1;
2221       if (!psInfo->isModeless)
2222       {
2223          EndDialog(hwndDlg, FALSE);
2224          return TRUE;
2225       }
2226     }
2227   }
2228   else if (index < psInfo->active_page)
2229     psInfo->active_page--;
2230
2231   /* Destroy page dialog window */
2232   DestroyWindow(psInfo->proppage[index].hwndPage);
2233
2234   /* Free page resources */
2235   if(psInfo->proppage[index].hpage)
2236   {
2237      PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)psInfo->proppage[index].hpage;
2238
2239      if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[index].pszText)
2240         HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[index].pszText);
2241
2242      DestroyPropertySheetPage(psInfo->proppage[index].hpage);
2243   }
2244
2245   /* Remove the tab */
2246   SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2247
2248   psInfo->nPages--;
2249   psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2250
2251   if (index > 0)
2252     memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
2253
2254   if (index < psInfo->nPages)
2255     memcpy(&psInfo->proppage[index], &oldPages[index + 1],
2256            (psInfo->nPages - index) * sizeof(PropPageInfo));
2257
2258   COMCTL32_Free(oldPages);
2259
2260   return FALSE;
2261 }
2262
2263 /******************************************************************************
2264  *            PROPSHEET_SetWizButtons
2265  *
2266  * This code will work if (and assumes that) the Next button is on top of the
2267  * Finish button. ie. Finish comes after Next in the Z order.
2268  * This means make sure the dialog template reflects this.
2269  *
2270  */
2271 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
2272 {
2273   HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2274   HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2275   HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2276
2277   TRACE("%ld\n", dwFlags);
2278
2279   EnableWindow(hwndBack, FALSE);
2280   EnableWindow(hwndNext, FALSE);
2281   EnableWindow(hwndFinish, FALSE);
2282
2283   if (dwFlags & PSWIZB_BACK)
2284     EnableWindow(hwndBack, TRUE);
2285
2286   if (dwFlags & PSWIZB_NEXT)
2287   {
2288     /* Hide the Finish button */
2289     ShowWindow(hwndFinish, SW_HIDE);
2290
2291     /* Show and enable the Next button */
2292     ShowWindow(hwndNext, SW_SHOW);
2293     EnableWindow(hwndNext, TRUE);
2294
2295     /* Set the Next button as the default pushbutton  */
2296     SendMessageA(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
2297   }
2298
2299   if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
2300   {
2301     /* Hide the Next button */
2302     ShowWindow(hwndNext, SW_HIDE);
2303
2304     /* Show the Finish button */
2305     ShowWindow(hwndFinish, SW_SHOW);
2306
2307     if (dwFlags & PSWIZB_FINISH)
2308       EnableWindow(hwndFinish, TRUE);
2309
2310     /* Set the Finish button as the default pushbutton  */
2311     SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2312   }
2313 }
2314
2315 /******************************************************************************
2316  *            PROPSHEET_GetPageIndex
2317  *
2318  * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
2319  * the array of PropPageInfo.
2320  */
2321 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
2322 {
2323   BOOL found = FALSE;
2324   int index = 0;
2325
2326   TRACE("hpage %p\n", hpage);
2327   while ((index < psInfo->nPages) && (found == FALSE))
2328   {
2329     if (psInfo->proppage[index].hpage == hpage)
2330       found = TRUE;
2331     else
2332       index++;
2333   }
2334
2335   if (found == FALSE)
2336     index = -1;
2337
2338   return index;
2339 }
2340
2341 /******************************************************************************
2342  *            PROPSHEET_CleanUp
2343  */
2344 static void PROPSHEET_CleanUp(HWND hwndDlg)
2345 {
2346   int i;
2347   PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropW(hwndDlg,
2348                                                        PropSheetInfoStr);
2349
2350   TRACE("\n");
2351   if (!psInfo) return;
2352   if (HIWORD(psInfo->ppshheader.pszCaption))
2353       HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->ppshheader.pszCaption);
2354
2355   for (i = 0; i < psInfo->nPages; i++)
2356   {
2357      PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
2358
2359      if(psInfo->proppage[i].hwndPage)
2360         DestroyWindow(psInfo->proppage[i].hwndPage);
2361
2362      if(psp)
2363      {
2364         if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[i].pszText)
2365            HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[i].pszText);
2366
2367         DestroyPropertySheetPage(psInfo->proppage[i].hpage);
2368      }
2369   }
2370
2371   COMCTL32_Free(psInfo->proppage);
2372   COMCTL32_Free(psInfo->strPropertiesFor);
2373   ImageList_Destroy(psInfo->hImageList);
2374
2375   GlobalFree((HGLOBAL)psInfo);
2376 }
2377
2378 /******************************************************************************
2379  *            PropertySheet    (COMCTL32.@)
2380  *            PropertySheetA   (COMCTL32.@)
2381  */
2382 INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2383 {
2384   int bRet = 0;
2385   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2386                                                        sizeof(PropSheetInfo));
2387   UINT i, n;
2388   BYTE* pByte;
2389
2390   TRACE("(%p)\n", lppsh);
2391
2392   PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2393
2394   psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
2395                                                     lppsh->nPages);
2396   pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2397
2398   for (n = i = 0; i < lppsh->nPages; i++, n++)
2399   {
2400     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
2401       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2402     else
2403     {
2404        psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
2405        pByte += ((LPPROPSHEETPAGEA)pByte)->dwSize;
2406     }
2407
2408     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2409                                psInfo, n))
2410     {
2411         if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
2412             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2413         n--;
2414         psInfo->nPages--;
2415     }
2416   }
2417
2418   bRet = PROPSHEET_CreateDialog(psInfo);
2419
2420   return bRet;
2421 }
2422
2423 /******************************************************************************
2424  *            PropertySheetW   (COMCTL32.@)
2425  */
2426 INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2427 {
2428   int bRet = 0;
2429   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2430                                                        sizeof(PropSheetInfo));
2431   UINT i, n;
2432   BYTE* pByte;
2433
2434   TRACE("(%p)\n", lppsh);
2435
2436   PROPSHEET_CollectSheetInfoW(lppsh, psInfo);
2437
2438   psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
2439                                                     lppsh->nPages);
2440   pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2441
2442   for (n = i = 0; i < lppsh->nPages; i++, n++)
2443   {
2444     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
2445       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2446     else
2447     {
2448        psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2449        pByte += ((LPPROPSHEETPAGEW)pByte)->dwSize;
2450     }
2451
2452     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2453                                psInfo, n))
2454     {
2455         if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
2456             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2457         n--;
2458         psInfo->nPages--;
2459     }
2460   }
2461
2462   bRet = PROPSHEET_CreateDialog(psInfo);
2463
2464   return bRet;
2465 }
2466
2467 /******************************************************************************
2468  *            CreatePropertySheetPage    (COMCTL32.@)
2469  *            CreatePropertySheetPageA   (COMCTL32.@)
2470  */
2471 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
2472                           LPCPROPSHEETPAGEA lpPropSheetPage)
2473 {
2474   PROPSHEETPAGEW* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEW));
2475
2476   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEA)));
2477
2478   if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
2479   {
2480       PROPSHEET_AtoW(&ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate);
2481   }
2482   if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2483   {
2484       PROPSHEET_AtoW(&ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon);
2485   }
2486
2487   if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2488   {
2489       PROPSHEET_AtoW(&ppsp->pszTitle, lpPropSheetPage->pszTitle);
2490   }
2491   else if ( !(ppsp->dwFlags & PSP_USETITLE) )
2492       ppsp->pszTitle = NULL;
2493
2494   return (HPROPSHEETPAGE)ppsp;
2495 }
2496
2497 /******************************************************************************
2498  *            CreatePropertySheetPageW   (COMCTL32.@)
2499  */
2500 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
2501 {
2502   PROPSHEETPAGEW* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEW));
2503
2504   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEW)));
2505
2506   if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
2507   {
2508     int len = strlenW(lpPropSheetPage->u.pszTemplate);
2509
2510     ppsp->u.pszTemplate = HeapAlloc( GetProcessHeap(),0,(len+1)*sizeof (WCHAR) );
2511     strcpyW( (WCHAR *)ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate );
2512   }
2513   if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2514   {
2515       int len = strlenW(lpPropSheetPage->u2.pszIcon);
2516       ppsp->u2.pszIcon = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof (WCHAR) );
2517       strcpyW( (WCHAR *)ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon );
2518   }
2519
2520   if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2521   {
2522       int len = strlenW(lpPropSheetPage->pszTitle);
2523       ppsp->pszTitle = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof (WCHAR) );
2524       strcpyW( (WCHAR *)ppsp->pszTitle, lpPropSheetPage->pszTitle );
2525   }
2526   else if ( !(ppsp->dwFlags & PSP_USETITLE) )
2527       ppsp->pszTitle = NULL;
2528
2529   return (HPROPSHEETPAGE)ppsp;
2530 }
2531
2532 /******************************************************************************
2533  *            DestroyPropertySheetPage   (COMCTL32.@)
2534  */
2535 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
2536 {
2537   PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
2538
2539   if (!psp)
2540      return FALSE;
2541
2542   if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u.pszTemplate ) )
2543      HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u.pszTemplate);
2544
2545   if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) )
2546      HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u2.pszIcon);
2547
2548   if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle ))
2549       HeapFree(GetProcessHeap(), 0, (LPVOID)psp->pszTitle);
2550
2551   COMCTL32_Free(hPropPage);
2552
2553   return TRUE;
2554 }
2555
2556 /******************************************************************************
2557  *            PROPSHEET_IsDialogMessage
2558  */
2559 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
2560 {
2561    PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
2562
2563    TRACE("\n");
2564    if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
2565       return FALSE;
2566
2567    if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
2568    {
2569       int new_page = 0;
2570       INT dlgCode = SendMessageA(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
2571
2572       if (!(dlgCode & DLGC_WANTMESSAGE))
2573       {
2574          switch (lpMsg->wParam)
2575          {
2576             case VK_TAB:
2577                if (GetKeyState(VK_SHIFT) & 0x8000)
2578                    new_page = -1;
2579                 else
2580                    new_page = 1;
2581                break;
2582
2583             case VK_NEXT:   new_page = 1;  break;
2584             case VK_PRIOR:  new_page = -1; break;
2585          }
2586       }
2587
2588       if (new_page)
2589       {
2590          if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
2591          {
2592             new_page += psInfo->active_page;
2593
2594             if (new_page < 0)
2595                new_page = psInfo->nPages - 1;
2596             else if (new_page >= psInfo->nPages)
2597                new_page = 0;
2598
2599             PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
2600          }
2601
2602          return TRUE;
2603       }
2604    }
2605
2606    return IsDialogMessageA(hwnd, lpMsg);
2607 }
2608
2609 /******************************************************************************
2610  *            PROPSHEET_DoCommand
2611  */
2612 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID)
2613 {
2614
2615     switch (wID) {
2616
2617     case IDOK:
2618     case IDC_APPLY_BUTTON:
2619         {
2620             HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
2621
2622             if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
2623                 break;
2624
2625             if (wID == IDOK)
2626                 {
2627                     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2628                                                                       PropSheetInfoStr);
2629                     int result = TRUE;
2630
2631                     if (psInfo->restartWindows)
2632                         result = ID_PSRESTARTWINDOWS;
2633
2634                     /* reboot system takes precedence over restart windows */
2635                     if (psInfo->rebootSystem)
2636                         result = ID_PSREBOOTSYSTEM;
2637
2638                     if (psInfo->isModeless)
2639                         psInfo->activeValid = FALSE;
2640                     else
2641                         EndDialog(hwnd, result);
2642                 }
2643             else
2644                 EnableWindow(hwndApplyBtn, FALSE);
2645
2646             break;
2647         }
2648
2649     case IDC_BACK_BUTTON:
2650         PROPSHEET_Back(hwnd);
2651         break;
2652
2653     case IDC_NEXT_BUTTON:
2654         PROPSHEET_Next(hwnd);
2655         break;
2656
2657     case IDC_FINISH_BUTTON:
2658         PROPSHEET_Finish(hwnd);
2659         break;
2660
2661     case IDCANCEL:
2662         PROPSHEET_Cancel(hwnd, 0);
2663         break;
2664
2665     case IDHELP:
2666         PROPSHEET_Help(hwnd);
2667         break;
2668     }
2669
2670     return TRUE;
2671 }
2672
2673 /******************************************************************************
2674  *            PROPSHEET_DialogProc
2675  */
2676 INT_PTR CALLBACK
2677 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2678 {
2679   TRACE("hwnd=%p msg=0x%04x wparam=%x lparam=%lx\n",
2680         hwnd, uMsg, wParam, lParam);
2681
2682   switch (uMsg)
2683   {
2684     case WM_INITDIALOG:
2685     {
2686       PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
2687       WCHAR* strCaption = (WCHAR*)COMCTL32_Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
2688       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2689       LPCPROPSHEETPAGEW ppshpage;
2690       int idx;
2691
2692       SetPropW(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
2693
2694       /*
2695        * psInfo->hwnd is not being used by WINE code - it exists
2696        * for compatibility with "real" Windoze. The same about
2697        * SetWindowLong - WINE is only using the PropSheetInfoStr
2698        * property.
2699        */
2700       psInfo->hwnd = hwnd;
2701       SetWindowLongW(hwnd,DWL_USER,(LONG)psInfo);
2702
2703       /*
2704        * Small icon in the title bar.
2705        */
2706       if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
2707           (psInfo->ppshheader.dwFlags & PSH_USEHICON))
2708       {
2709         HICON hIcon;
2710         int icon_cx = GetSystemMetrics(SM_CXSMICON);
2711         int icon_cy = GetSystemMetrics(SM_CYSMICON);
2712
2713         if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
2714           hIcon = LoadImageW(psInfo->ppshheader.hInstance,
2715                              psInfo->ppshheader.u.pszIcon,
2716                              IMAGE_ICON,
2717                              icon_cx, icon_cy,
2718                              LR_DEFAULTCOLOR);
2719         else
2720           hIcon = psInfo->ppshheader.u.hIcon;
2721
2722         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)hIcon);
2723       }
2724
2725       if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
2726         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)psInfo->ppshheader.u.hIcon);
2727
2728       psInfo->strPropertiesFor = strCaption;
2729
2730       GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
2731
2732       PROPSHEET_CreateTabControl(hwnd, psInfo);
2733
2734       if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
2735       {
2736         if (PROPSHEET_IsTooSmallWizard(hwnd, psInfo))
2737         {
2738           PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
2739           PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
2740         }
2741       }
2742       else
2743       {
2744         if (PROPSHEET_SizeMismatch(hwnd, psInfo))
2745         {
2746           PROPSHEET_AdjustSize(hwnd, psInfo);
2747           PROPSHEET_AdjustButtons(hwnd, psInfo);
2748         }
2749       }
2750
2751       if (psInfo->useCallback)
2752              (*(psInfo->ppshheader.pfnCallback))(hwnd,
2753                                               PSCB_INITIALIZED, (LPARAM)0);
2754
2755       idx = psInfo->active_page;
2756       ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[idx].hpage;
2757       psInfo->active_page = -1;
2758
2759       PROPSHEET_SetCurSel(hwnd, idx, 1, psInfo->proppage[idx].hpage);
2760
2761       /* doing TCM_SETCURSEL seems to be needed even in case of PSH_WIZARD,
2762        * as some programs call TCM_GETCURSEL to get the current selection
2763        * from which to switch to the next page */
2764       SendMessageW(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
2765
2766       if (!HIWORD(psInfo->ppshheader.pszCaption) &&
2767               psInfo->ppshheader.hInstance)
2768       {
2769          WCHAR szText[256];
2770
2771          if (LoadStringW(psInfo->ppshheader.hInstance,
2772                  (UINT)psInfo->ppshheader.pszCaption, szText, 255))
2773             PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
2774       }
2775       else
2776       {
2777          PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
2778                          psInfo->ppshheader.pszCaption);
2779       }
2780
2781       return TRUE;
2782     }
2783
2784     case WM_DESTROY:
2785       PROPSHEET_CleanUp(hwnd);
2786       return TRUE;
2787
2788     case WM_CLOSE:
2789       PROPSHEET_Cancel(hwnd, 1);
2790       return TRUE;
2791
2792     case WM_COMMAND:
2793       return PROPSHEET_DoCommand(hwnd, LOWORD(wParam));
2794
2795     case WM_NOTIFY:
2796     {
2797       NMHDR* pnmh = (LPNMHDR) lParam;
2798
2799       if (pnmh->code == TCN_SELCHANGE)
2800       {
2801         int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
2802         PROPSHEET_SetCurSel(hwnd, index, 1, 0);
2803       }
2804
2805       if(pnmh->code == TCN_SELCHANGING)
2806       {
2807         BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
2808         SetWindowLongW(hwnd, DWL_MSGRESULT, !bRet);
2809         return TRUE;
2810       }
2811
2812       return FALSE;
2813     }
2814
2815     case PSM_GETCURRENTPAGEHWND:
2816     {
2817       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2818                                                         PropSheetInfoStr);
2819       HWND hwndPage = 0;
2820
2821       if (psInfo->activeValid && psInfo->active_page != -1)
2822         hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
2823
2824       SetWindowLongW(hwnd, DWL_MSGRESULT, (LONG)hwndPage);
2825
2826       return TRUE;
2827     }
2828
2829     case PSM_CHANGED:
2830       PROPSHEET_Changed(hwnd, (HWND)wParam);
2831       return TRUE;
2832
2833     case PSM_UNCHANGED:
2834       PROPSHEET_UnChanged(hwnd, (HWND)wParam);
2835       return TRUE;
2836
2837     case PSM_GETTABCONTROL:
2838     {
2839       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2840
2841       SetWindowLongW(hwnd, DWL_MSGRESULT, (LONG)hwndTabCtrl);
2842
2843       return TRUE;
2844     }
2845
2846     case PSM_SETCURSEL:
2847     {
2848       BOOL msgResult;
2849
2850       msgResult = PROPSHEET_CanSetCurSel(hwnd);
2851       if(msgResult != FALSE)
2852       {
2853         msgResult = PROPSHEET_SetCurSel(hwnd,
2854                                        (int)wParam,
2855                                        1,
2856                                        (HPROPSHEETPAGE)lParam);
2857       }
2858
2859       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2860
2861       return TRUE;
2862     }
2863
2864     case PSM_CANCELTOCLOSE:
2865     {
2866       WCHAR buf[MAX_BUTTONTEXT_LENGTH];
2867       HWND hwndOK = GetDlgItem(hwnd, IDOK);
2868       HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
2869
2870       EnableWindow(hwndCancel, FALSE);
2871       if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)))
2872          SetWindowTextW(hwndOK, buf);
2873
2874       return FALSE;
2875     }
2876
2877     case PSM_RESTARTWINDOWS:
2878     {
2879       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2880                                                         PropSheetInfoStr);
2881
2882       psInfo->restartWindows = TRUE;
2883       return TRUE;
2884     }
2885
2886     case PSM_REBOOTSYSTEM:
2887     {
2888       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2889                                                         PropSheetInfoStr);
2890
2891       psInfo->rebootSystem = TRUE;
2892       return TRUE;
2893     }
2894
2895     case PSM_SETTITLEA:
2896       PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
2897       return TRUE;
2898
2899     case PSM_SETTITLEW:
2900       PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
2901       return TRUE;
2902
2903     case PSM_APPLY:
2904     {
2905       BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
2906
2907       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2908
2909       return TRUE;
2910     }
2911
2912     case PSM_QUERYSIBLINGS:
2913     {
2914       LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
2915
2916       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2917
2918       return TRUE;
2919     }
2920
2921     case PSM_ADDPAGE:
2922     {
2923       /*
2924        * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have
2925        *       a return value. This is not true. PSM_ADDPAGE returns TRUE
2926        *       on success or FALSE otherwise, as specified on MSDN Online.
2927        *       Also see the MFC code for
2928        *       CPropertySheet::AddPage(CPropertyPage* pPage).
2929        */
2930
2931       BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
2932
2933       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2934
2935       return TRUE;
2936     }
2937
2938     case PSM_REMOVEPAGE:
2939       PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
2940       return TRUE;
2941
2942     case PSM_ISDIALOGMESSAGE:
2943     {
2944        BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
2945        SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2946        return TRUE;
2947     }
2948
2949     case PSM_PRESSBUTTON:
2950       PROPSHEET_PressButton(hwnd, (int)wParam);
2951       return TRUE;
2952
2953     case PSM_SETFINISHTEXTA:
2954       PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
2955       return TRUE;
2956
2957     case PSM_SETWIZBUTTONS:
2958       PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
2959       return TRUE;
2960
2961     case PSM_SETCURSELID:
2962         PROPSHEET_SetCurSelId(hwnd, (int)lParam);
2963         return TRUE;
2964
2965     case PSM_SETFINISHTEXTW:
2966         PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
2967         return FALSE;
2968
2969     default:
2970       return FALSE;
2971   }
2972
2973   return FALSE;
2974 }