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