comctl32: Monthcal today link test.
[wine] / dlls / comctl32 / propsheet.c
1 /*
2  * Property Sheets
3  *
4  * Copyright 1998 Francis Beaudet
5  * Copyright 1999 Thuy Nguyen
6  * Copyright 2004 Maxime Bellenge
7  * Copyright 2004 Filip Navara
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  *
23  * This code was audited for completeness against the documented features
24  * of Comctl32.dll version 6.0 on Sep. 12, 2004, by Filip Navara.
25  * 
26  * Unless otherwise noted, we believe this code to be complete, as per
27  * the specification mentioned above.
28  * If you discover missing features, or bugs, please note them below.
29  *
30  * TODO:
31  *   - Tab order
32  *   - Wizard 97 header resizing
33  *   - Enforcing of minimal wizard size
34  *   - Messages:
35  *     o PSM_INSERTPAGE
36  *     o PSM_RECALCPAGESIZES
37  *     o PSM_SETHEADERSUBTITLE
38  *     o PSM_SETHEADERTITLE
39  *     o WM_HELP
40  *     o WM_CONTEXTMENU
41  *   - Notifications:
42  *     o PSN_GETOBJECT
43  *     o PSN_QUERYINITIALFOCUS
44  *     o PSN_TRANSLATEACCELERATOR
45  *   - Styles:
46  *     o PSH_RTLREADING
47  *     o PSH_STRETCHWATERMARK
48  *     o PSH_USEPAGELANG
49  *     o PSH_USEPSTARTPAGE
50  *   - Page styles:
51  *     o PSP_USEFUSIONCONTEXT
52  *     o PSP_USEREFPARENT
53  */
54
55 #include <stdarg.h>
56 #include <string.h>
57
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
60 #include "windef.h"
61 #include "winbase.h"
62 #include "wingdi.h"
63 #include "winuser.h"
64 #include "winnls.h"
65 #include "commctrl.h"
66 #include "prsht.h"
67 #include "comctl32.h"
68 #include "uxtheme.h"
69
70 #include "wine/debug.h"
71 #include "wine/unicode.h"
72
73 /******************************************************************************
74  * Data structures
75  */
76 #include "pshpack2.h"
77
78 typedef struct
79 {
80   WORD dlgVer;
81   WORD signature;
82   DWORD helpID;
83   DWORD exStyle;
84   DWORD style;
85 } MyDLGTEMPLATEEX;
86
87 typedef struct
88 {
89   DWORD helpid;
90   DWORD exStyle;
91   DWORD style;
92   short x;
93   short y;
94   short cx;
95   short cy;
96   DWORD id;
97 } MyDLGITEMTEMPLATEEX;
98 #include "poppack.h"
99
100 typedef struct tagPropPageInfo
101 {
102   HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
103   HWND hwndPage;
104   BOOL isDirty;
105   LPCWSTR pszText;
106   BOOL hasHelp;
107   BOOL useCallback;
108   BOOL hasIcon;
109 } PropPageInfo;
110
111 typedef struct tagPropSheetInfo
112 {
113   HWND hwnd;
114   PROPSHEETHEADERW ppshheader;
115   BOOL unicode;
116   LPWSTR strPropertiesFor;
117   int nPages;
118   int active_page;
119   BOOL isModeless;
120   BOOL hasHelp;
121   BOOL hasApply;
122   BOOL hasFinish;
123   BOOL usePropPage;
124   BOOL useCallback;
125   BOOL activeValid;
126   PropPageInfo* proppage;
127   HFONT hFont;
128   HFONT hFontBold;
129   int width;
130   int height;
131   HIMAGELIST hImageList;
132   BOOL ended;
133   INT result;
134 } PropSheetInfo;
135
136 typedef struct
137 {
138   int x;
139   int y;
140 } PADDING_INFO;
141
142 /******************************************************************************
143  * Defines and global variables
144  */
145
146 static const WCHAR PropSheetInfoStr[] =
147     {'P','r','o','p','e','r','t','y','S','h','e','e','t','I','n','f','o',0 };
148
149 #define PSP_INTERNAL_UNICODE 0x80000000
150
151 #define MAX_CAPTION_LENGTH 255
152 #define MAX_TABTEXT_LENGTH 255
153 #define MAX_BUTTONTEXT_LENGTH 64
154
155 #define INTRNL_ANY_WIZARD (PSH_WIZARD | PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)
156
157 /* Wizard metrics specified in DLUs */
158 #define WIZARD_PADDING 7
159 #define WIZARD_HEADER_HEIGHT 36
160                                 
161 /******************************************************************************
162  * Prototypes
163  */
164 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
165 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText);
166 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
167 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
168                                 int index,
169                                 int skipdir,
170                                 HPROPSHEETPAGE hpage);
171 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, const PropSheetInfo* psInfo);
172 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
173 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID);
174
175 static INT_PTR CALLBACK
176 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
177
178 WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
179
180 #define add_flag(a) if (dwFlags & a) {strcat(string, #a );strcat(string," ");}
181 /******************************************************************************
182  *            PROPSHEET_UnImplementedFlags
183  *
184  * Document use of flags we don't implement yet.
185  */
186 static VOID PROPSHEET_UnImplementedFlags(DWORD dwFlags)
187 {
188     CHAR string[256];
189
190     string[0] = '\0';
191
192   /*
193    * unhandled header flags:
194    *  PSH_RTLREADING         0x00000800
195    *  PSH_STRETCHWATERMARK   0x00040000
196    *  PSH_USEPAGELANG        0x00200000
197    */
198
199     add_flag(PSH_RTLREADING);
200     add_flag(PSH_STRETCHWATERMARK);
201     add_flag(PSH_USEPAGELANG);
202     if (string[0] != '\0')
203         FIXME("%s\n", string);
204 }
205 #undef add_flag
206
207 /******************************************************************************
208  *            PROPSHEET_GetPageRect
209  *
210  * Retrieve rect from tab control and map into the dialog for SetWindowPos
211  */
212 static void PROPSHEET_GetPageRect(const PropSheetInfo * psInfo, HWND hwndDlg,
213                                   RECT *rc, LPCPROPSHEETPAGEW ppshpage)
214 {
215     if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) {     
216         HWND hwndChild;
217         RECT r;
218
219         if (((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
220              (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
221              !(ppshpage->dwFlags & PSP_HIDEHEADER)) ||
222             (psInfo->ppshheader.dwFlags & PSH_WIZARD))
223         {
224             rc->left = rc->top = WIZARD_PADDING;
225         }
226         else
227         {
228             rc->left = rc->top = 0;
229         }
230         rc->right = psInfo->width - rc->left;
231         rc->bottom = psInfo->height - rc->top;
232         MapDialogRect(hwndDlg, rc);
233
234         if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
235             (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
236             !(ppshpage->dwFlags & PSP_HIDEHEADER))
237         {
238             hwndChild = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
239             GetClientRect(hwndChild, &r);
240             MapWindowPoints(hwndChild, hwndDlg, (LPPOINT) &r, 2);
241             rc->top += r.bottom + 1;
242         }
243     } else {
244         HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
245         GetClientRect(hwndTabCtrl, rc);
246         SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)rc);
247         MapWindowPoints(hwndTabCtrl, hwndDlg, (LPPOINT)rc, 2);
248     }
249 }
250
251 /******************************************************************************
252  *            PROPSHEET_FindPageByResId
253  *
254  * Find page index corresponding to page resource id.
255  */
256 static INT PROPSHEET_FindPageByResId(const PropSheetInfo * psInfo, LRESULT resId)
257 {
258    INT i;
259
260    for (i = 0; i < psInfo->nPages; i++)
261    {
262       LPCPROPSHEETPAGEA lppsp = (LPCPROPSHEETPAGEA)psInfo->proppage[i].hpage;
263
264       /* Fixme: if resource ID is a string shall we use strcmp ??? */
265       if (lppsp->u.pszTemplate == (LPVOID)resId)
266          break;
267    }
268
269    return i;
270 }
271
272 /******************************************************************************
273  *            PROPSHEET_AtoW
274  *
275  * Convert ASCII to Unicode since all data is saved as Unicode.
276  */
277 static void PROPSHEET_AtoW(LPCWSTR *tostr, LPCSTR frstr)
278 {
279     INT len;
280     WCHAR *to;
281
282     TRACE("<%s>\n", frstr);
283     len = MultiByteToWideChar(CP_ACP, 0, frstr, -1, 0, 0);
284     to = Alloc(len * sizeof(WCHAR));
285     MultiByteToWideChar(CP_ACP, 0, frstr, -1, to, len);
286     *tostr = to;
287 }
288
289 /******************************************************************************
290  *            PROPSHEET_CollectSheetInfoCommon
291  *
292  * Common code for PROPSHEET_CollectSheetInfoA/W
293  */
294 static void PROPSHEET_CollectSheetInfoCommon(PropSheetInfo * psInfo, DWORD dwFlags)
295 {
296   PROPSHEET_UnImplementedFlags(dwFlags);
297
298   psInfo->hasHelp = dwFlags & PSH_HASHELP;
299   psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
300   psInfo->hasFinish = dwFlags & PSH_WIZARDHASFINISH;
301   psInfo->isModeless = dwFlags & PSH_MODELESS;
302   psInfo->usePropPage = dwFlags & PSH_PROPSHEETPAGE;
303   if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
304      psInfo->active_page = 0;
305
306   psInfo->result = 0;
307   psInfo->hImageList = 0;
308   psInfo->activeValid = FALSE;
309 }
310
311 /******************************************************************************
312  *            PROPSHEET_CollectSheetInfoA
313  *
314  * Collect relevant data.
315  */
316 static void PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
317                                        PropSheetInfo * psInfo)
318 {
319   DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
320   DWORD dwFlags = lppsh->dwFlags;
321
322   psInfo->useCallback = (dwFlags & PSH_USECALLBACK )&& (lppsh->pfnCallback);
323
324   memcpy(&psInfo->ppshheader,lppsh,dwSize);
325   TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
326         lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance,
327         debugstr_a(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
328
329   if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
330      psInfo->ppshheader.pszCaption = NULL;
331   else
332   {
333      if (HIWORD(lppsh->pszCaption))
334      {
335         int len = MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, NULL, 0);
336         WCHAR *caption = Alloc( len*sizeof (WCHAR) );
337
338         MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, caption, len);
339         psInfo->ppshheader.pszCaption = caption;
340      }
341   }
342   psInfo->nPages = lppsh->nPages;
343
344   if (dwFlags & PSH_USEPSTARTPAGE)
345   {
346     TRACE("PSH_USEPSTARTPAGE is on\n");
347     psInfo->active_page = 0;
348   }
349   else
350     psInfo->active_page = lppsh->u2.nStartPage;
351
352   PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
353 }
354
355 /******************************************************************************
356  *            PROPSHEET_CollectSheetInfoW
357  *
358  * Collect relevant data.
359  */
360 static void PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
361                                        PropSheetInfo * psInfo)
362 {
363   DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERW));
364   DWORD dwFlags = lppsh->dwFlags;
365
366   psInfo->useCallback = (dwFlags & PSH_USECALLBACK) && (lppsh->pfnCallback);
367
368   memcpy(&psInfo->ppshheader,lppsh,dwSize);
369   TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%d\ndwFlags\t\t%08x\nhwndParent\t%p\nhInstance\t%p\npszCaption\t%s\nnPages\t\t%d\npfnCallback\t%p\n",
370       lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, debugstr_w(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
371
372   if (lppsh->dwFlags & INTRNL_ANY_WIZARD)
373      psInfo->ppshheader.pszCaption = NULL;
374   else
375   {
376      if (HIWORD(lppsh->pszCaption))
377      {
378         int len = strlenW(lppsh->pszCaption);
379         WCHAR *caption = Alloc( (len+1)*sizeof(WCHAR) );
380
381         psInfo->ppshheader.pszCaption = strcpyW( caption, lppsh->pszCaption );
382      }
383   }
384   psInfo->nPages = lppsh->nPages;
385
386   if (dwFlags & PSH_USEPSTARTPAGE)
387   {
388     TRACE("PSH_USEPSTARTPAGE is on\n");
389     psInfo->active_page = 0;
390   }
391   else
392     psInfo->active_page = lppsh->u2.nStartPage;
393
394   PROPSHEET_CollectSheetInfoCommon(psInfo, dwFlags);
395 }
396
397 /******************************************************************************
398  *            PROPSHEET_CollectPageInfo
399  *
400  * Collect property sheet data.
401  * With code taken from DIALOG_ParseTemplate32.
402  */
403 static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
404                                PropSheetInfo * psInfo,
405                                int index, BOOL resize)
406 {
407   const DLGTEMPLATE* pTemplate;
408   const WORD*  p;
409   DWORD dwFlags;
410   int width, height;
411
412   if (!lppsp)
413     return FALSE;
414
415   TRACE("\n");
416   psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
417   psInfo->proppage[index].hwndPage = 0;
418   psInfo->proppage[index].isDirty = FALSE;
419
420   /*
421    * Process property page flags.
422    */
423   dwFlags = lppsp->dwFlags;
424   psInfo->proppage[index].useCallback = (dwFlags & PSP_USECALLBACK) && (lppsp->pfnCallback);
425   psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
426   psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID);
427
428   /* as soon as we have a page with the help flag, set the sheet flag on */
429   if (psInfo->proppage[index].hasHelp)
430     psInfo->hasHelp = TRUE;
431
432   /*
433    * Process page template.
434    */
435   if (dwFlags & PSP_DLGINDIRECT)
436     pTemplate = lppsp->u.pResource;
437   else if(dwFlags & PSP_INTERNAL_UNICODE )
438   {
439     HRSRC hResource = FindResourceW(lppsp->hInstance,
440                                     lppsp->u.pszTemplate,
441                                     (LPWSTR)RT_DIALOG);
442     HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
443                                      hResource);
444     pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
445   }
446   else
447   {
448     HRSRC hResource = FindResourceA(lppsp->hInstance,
449                                     (LPCSTR)lppsp->u.pszTemplate,
450                                     (LPSTR)RT_DIALOG);
451     HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
452                                      hResource);
453     pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
454   }
455
456   /*
457    * Extract the size of the page and the caption.
458    */
459   if (!pTemplate)
460       return FALSE;
461
462   p = (const WORD *)pTemplate;
463
464   if (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
465   {
466     /* DLGTEMPLATEEX (not defined in any std. header file) */
467
468     p++;       /* dlgVer    */
469     p++;       /* signature */
470     p += 2;    /* help ID   */
471     p += 2;    /* ext style */
472     p += 2;    /* style     */
473   }
474   else
475   {
476     /* DLGTEMPLATE */
477
478     p += 2;    /* style     */
479     p += 2;    /* ext style */
480   }
481
482   p++;    /* nb items */
483   p++;    /*   x      */
484   p++;    /*   y      */
485   width  = (WORD)*p; p++;
486   height = (WORD)*p; p++;
487
488   /* Special calculation for interior wizard pages so the largest page is
489    * calculated correctly. We need to add all the padding and space occupied
490    * by the header so the width and height sums up to the whole wizard client
491    * area. */
492   if ((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
493       (psInfo->ppshheader.dwFlags & PSH_HEADER) &&
494       !(dwFlags & PSP_HIDEHEADER))
495   {
496       height += 2 * WIZARD_PADDING + WIZARD_HEADER_HEIGHT;
497       width += 2 * WIZARD_PADDING;
498   }
499   if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
500   {
501       height += 2 * WIZARD_PADDING;
502       width += 2 * WIZARD_PADDING;
503   }
504
505   /* remember the largest width and height */
506   if (resize)
507   {
508       if (width > psInfo->width)
509         psInfo->width = width;
510
511       if (height > psInfo->height)
512         psInfo->height = height;
513   }
514
515   /* menu */
516   switch ((WORD)*p)
517   {
518     case 0x0000:
519       p++;
520       break;
521     case 0xffff:
522       p += 2;
523       break;
524     default:
525       p += lstrlenW( (LPCWSTR)p ) + 1;
526       break;
527   }
528
529   /* class */
530   switch ((WORD)*p)
531   {
532     case 0x0000:
533       p++;
534       break;
535     case 0xffff:
536       p += 2;
537       break;
538     default:
539       p += lstrlenW( (LPCWSTR)p ) + 1;
540       break;
541   }
542
543   /* Extract the caption */
544   psInfo->proppage[index].pszText = (LPCWSTR)p;
545   TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p));
546   p += lstrlenW((LPCWSTR)p) + 1;
547
548   if (dwFlags & PSP_USETITLE)
549   {
550     WCHAR szTitle[256];
551     const WCHAR *pTitle;
552     static const WCHAR pszNull[] = { '(','n','u','l','l',')',0 };
553     WCHAR *text;
554     int len;
555
556     if ( !HIWORD( lppsp->pszTitle ) )
557     {
558       if (!LoadStringW( lppsp->hInstance, (DWORD_PTR)lppsp->pszTitle,szTitle,sizeof(szTitle) ))
559       {
560         pTitle = pszNull;
561         FIXME("Could not load resource #%04x?\n",LOWORD(lppsp->pszTitle));
562       }
563       else
564         pTitle = szTitle;
565     }
566     else
567       pTitle = lppsp->pszTitle;
568
569     len = strlenW(pTitle);
570     text = Alloc( (len+1)*sizeof (WCHAR) );
571     psInfo->proppage[index].pszText = strcpyW( text, pTitle);
572   }
573
574   /*
575    * Build the image list for icons
576    */
577   if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
578   {
579     HICON hIcon;
580     int icon_cx = GetSystemMetrics(SM_CXSMICON);
581     int icon_cy = GetSystemMetrics(SM_CYSMICON);
582
583     if (dwFlags & PSP_USEICONID)
584       hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
585                          icon_cx, icon_cy, LR_DEFAULTCOLOR);
586     else
587       hIcon = lppsp->u2.hIcon;
588
589     if ( hIcon )
590     {
591       if (psInfo->hImageList == 0 )
592         psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);
593
594       ImageList_AddIcon(psInfo->hImageList, hIcon);
595     }
596
597   }
598
599   return TRUE;
600 }
601
602 /******************************************************************************
603  *            PROPSHEET_CreateDialog
604  *
605  * Creates the actual property sheet.
606  */
607 static INT_PTR PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
608 {
609   LRESULT ret;
610   LPCVOID template;
611   LPVOID temp = 0;
612   HRSRC hRes;
613   DWORD resSize;
614   WORD resID = IDD_PROPSHEET;
615
616   TRACE("\n");
617   if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
618     resID = IDD_WIZARD;
619
620   if( psInfo->unicode )
621   {
622     if(!(hRes = FindResourceW(COMCTL32_hModule,
623                             MAKEINTRESOURCEW(resID),
624                             (LPWSTR)RT_DIALOG)))
625       return -1;
626   }
627   else
628   {
629     if(!(hRes = FindResourceA(COMCTL32_hModule,
630                             MAKEINTRESOURCEA(resID),
631                             (LPSTR)RT_DIALOG)))
632       return -1;
633   }
634
635   if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
636     return -1;
637
638   /*
639    * Make a copy of the dialog template.
640    */
641   resSize = SizeofResource(COMCTL32_hModule, hRes);
642
643   temp = Alloc(resSize);
644
645   if (!temp)
646     return -1;
647
648   memcpy(temp, template, resSize);
649
650   if (psInfo->ppshheader.dwFlags & PSH_NOCONTEXTHELP)
651   {
652     if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
653       ((MyDLGTEMPLATEEX*)temp)->style &= ~DS_CONTEXTHELP;
654     else
655       ((DLGTEMPLATE*)temp)->style &= ~DS_CONTEXTHELP;
656   }
657   if ((psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) &&
658       (psInfo->ppshheader.dwFlags & PSH_WIZARDCONTEXTHELP))
659   {
660     if (((MyDLGTEMPLATEEX*)temp)->signature == 0xFFFF)
661       ((MyDLGTEMPLATEEX*)temp)->style |= DS_CONTEXTHELP;
662     else
663       ((DLGTEMPLATE*)temp)->style |= DS_CONTEXTHELP;
664   }
665
666   if (psInfo->useCallback)
667     (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
668
669   /* NOTE: MSDN states "Returns a positive value if successful, or -1
670    * otherwise for modal property sheets.", but this is wrong. The
671    * actual return value is either TRUE (success), FALSE (cancel) or
672    * -1 (error). */
673   if( psInfo->unicode )
674   {
675     ret = (INT_PTR)CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
676                                           (LPDLGTEMPLATEW) temp,
677                                           psInfo->ppshheader.hwndParent,
678                                           PROPSHEET_DialogProc,
679                                           (LPARAM)psInfo);
680     if ( !ret ) ret = -1;
681   }
682   else
683   {
684     ret = (INT_PTR)CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
685                                           (LPDLGTEMPLATEA) temp,
686                                           psInfo->ppshheader.hwndParent,
687                                           PROPSHEET_DialogProc,
688                                           (LPARAM)psInfo);
689     if ( !ret ) ret = -1;
690   }
691
692   Free(temp);
693
694   return ret;
695 }
696
697 /******************************************************************************
698  *            PROPSHEET_SizeMismatch
699  *
700  *     Verify that the tab control and the "largest" property sheet page dlg. template
701  *     match in size.
702  */
703 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, const PropSheetInfo* psInfo)
704 {
705   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
706   RECT rcOrigTab, rcPage;
707
708   /*
709    * Original tab size.
710    */
711   GetClientRect(hwndTabCtrl, &rcOrigTab);
712   TRACE("orig tab %d %d %d %d\n", rcOrigTab.left, rcOrigTab.top,
713         rcOrigTab.right, rcOrigTab.bottom);
714
715   /*
716    * Biggest page size.
717    */
718   rcPage.left   = 0;
719   rcPage.top    = 0;
720   rcPage.right  = psInfo->width;
721   rcPage.bottom = psInfo->height;
722
723   MapDialogRect(hwndDlg, &rcPage);
724   TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
725         rcPage.right, rcPage.bottom);
726
727   if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
728     return TRUE;
729   if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
730     return TRUE;
731
732   return FALSE;
733 }
734
735 /******************************************************************************
736  *            PROPSHEET_AdjustSize
737  *
738  * Resizes the property sheet and the tab control to fit the largest page.
739  */
740 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
741 {
742   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
743   HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
744   RECT rc,tabRect;
745   int tabOffsetX, tabOffsetY, buttonHeight;
746   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
747   RECT units;
748
749   /* Get the height of buttons */
750   GetClientRect(hwndButton, &rc);
751   buttonHeight = rc.bottom;
752
753   /*
754    * Biggest page size.
755    */
756   rc.left   = 0;
757   rc.top    = 0;
758   rc.right  = psInfo->width;
759   rc.bottom = psInfo->height;
760
761   MapDialogRect(hwndDlg, &rc);
762
763   /* retrieve the dialog units */
764   units.left = units.right = 4;
765   units.top = units.bottom = 8;
766   MapDialogRect(hwndDlg, &units);
767
768   /*
769    * Resize the tab control.
770    */
771   GetClientRect(hwndTabCtrl,&tabRect);
772
773   SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
774
775   if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
776   {
777       rc.bottom = rc.top + tabRect.bottom - tabRect.top;
778       psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
779   }
780
781   if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
782   {
783       rc.right = rc.left + tabRect.right - tabRect.left;
784       psInfo->width  = MulDiv((rc.right - rc.left),4,units.left);
785   }
786
787   SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
788
789   tabOffsetX = -(rc.left);
790   tabOffsetY = -(rc.top);
791
792   rc.right -= rc.left;
793   rc.bottom -= rc.top;
794   TRACE("setting tab %p, rc (0,0)-(%d,%d)\n",
795         hwndTabCtrl, rc.right, rc.bottom);
796   SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
797                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
798
799   GetClientRect(hwndTabCtrl, &rc);
800
801   TRACE("tab client rc %d %d %d %d\n",
802         rc.left, rc.top, rc.right, rc.bottom);
803
804   rc.right += ((padding.x * 2) + tabOffsetX);
805   rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
806
807   /*
808    * Resize the property sheet.
809    */
810   TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
811         hwndDlg, rc.right, rc.bottom);
812   SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
813                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
814   return TRUE;
815 }
816
817 /******************************************************************************
818  *            PROPSHEET_AdjustSizeWizard
819  *
820  * Resizes the property sheet to fit the largest page.
821  */
822 static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, const PropSheetInfo* psInfo)
823 {
824   HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
825   RECT rc, lineRect, dialogRect;
826
827   /* Biggest page size */
828   rc.left   = 0;
829   rc.top    = 0;
830   rc.right  = psInfo->width;
831   rc.bottom = psInfo->height;
832   MapDialogRect(hwndDlg, &rc);
833
834   TRACE("Biggest page %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom);
835
836   /* Add space for the buttons row */
837   GetWindowRect(hwndLine, &lineRect);
838   MapWindowPoints(NULL, hwndDlg, (LPPOINT)&lineRect, 2);
839   GetClientRect(hwndDlg, &dialogRect);
840   rc.bottom += dialogRect.bottom - lineRect.top - 1;
841
842   /* Convert the client coordinates to window coordinates */
843   AdjustWindowRect(&rc, GetWindowLongW(hwndDlg, GWL_STYLE), FALSE);
844
845   /* Resize the property sheet */
846   TRACE("setting dialog %p, rc (0,0)-(%d,%d)\n",
847         hwndDlg, rc.right, rc.bottom);
848   SetWindowPos(hwndDlg, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
849                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
850
851   return TRUE;
852 }
853
854 /******************************************************************************
855  *            PROPSHEET_AdjustButtons
856  *
857  * Adjusts the buttons' positions.
858  */
859 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, const PropSheetInfo* psInfo)
860 {
861   HWND hwndButton = GetDlgItem(hwndParent, IDOK);
862   RECT rcSheet;
863   int x, y;
864   int num_buttons = 2;
865   int buttonWidth, buttonHeight;
866   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
867
868   if (psInfo->hasApply)
869     num_buttons++;
870
871   if (psInfo->hasHelp)
872     num_buttons++;
873
874   /*
875    * Obtain the size of the buttons.
876    */
877   GetClientRect(hwndButton, &rcSheet);
878   buttonWidth = rcSheet.right;
879   buttonHeight = rcSheet.bottom;
880
881   /*
882    * Get the size of the property sheet.
883    */
884   GetClientRect(hwndParent, &rcSheet);
885
886   /*
887    * All buttons will be at this y coordinate.
888    */
889   y = rcSheet.bottom - (padding.y + buttonHeight);
890
891   /*
892    * Position OK button and make it default.
893    */
894   hwndButton = GetDlgItem(hwndParent, IDOK);
895
896   x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
897
898   SetWindowPos(hwndButton, 0, x, y, 0, 0,
899                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
900
901   SendMessageW(hwndParent, DM_SETDEFID, IDOK, 0);
902
903
904   /*
905    * Position Cancel button.
906    */
907   hwndButton = GetDlgItem(hwndParent, IDCANCEL);
908
909   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
910
911   SetWindowPos(hwndButton, 0, x, y, 0, 0,
912                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
913
914   /*
915    * Position Apply button.
916    */
917   hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
918
919   if (psInfo->hasApply)
920   {
921     if (psInfo->hasHelp)
922       x = rcSheet.right - ((padding.x + buttonWidth) * 2);
923     else
924       x = rcSheet.right - (padding.x + buttonWidth);
925
926     SetWindowPos(hwndButton, 0, x, y, 0, 0,
927                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
928
929     EnableWindow(hwndButton, FALSE);
930   }
931   else
932     ShowWindow(hwndButton, SW_HIDE);
933
934   /*
935    * Position Help button.
936    */
937   hwndButton = GetDlgItem(hwndParent, IDHELP);
938
939   if (psInfo->hasHelp)
940   {
941     x = rcSheet.right - (padding.x + buttonWidth);
942
943     SetWindowPos(hwndButton, 0, x, y, 0, 0,
944                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
945   }
946   else
947     ShowWindow(hwndButton, SW_HIDE);
948
949   return TRUE;
950 }
951
952 /******************************************************************************
953  *            PROPSHEET_AdjustButtonsWizard
954  *
955  * Adjusts the buttons' positions.
956  */
957 static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
958                                           const PropSheetInfo* psInfo)
959 {
960   HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
961   HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
962   HWND hwndLineHeader = GetDlgItem(hwndParent, IDC_SUNKEN_LINEHEADER);
963   RECT rcSheet;
964   int x, y;
965   int num_buttons = 3;
966   int buttonWidth, buttonHeight, lineHeight, lineWidth;
967   PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
968
969   if (psInfo->hasHelp)
970     num_buttons++;
971   if (psInfo->hasFinish)
972     num_buttons++;
973
974   /*
975    * Obtain the size of the buttons.
976    */
977   GetClientRect(hwndButton, &rcSheet);
978   buttonWidth = rcSheet.right;
979   buttonHeight = rcSheet.bottom;
980
981   GetClientRect(hwndLine, &rcSheet);
982   lineHeight = rcSheet.bottom;
983
984   /*
985    * Get the size of the property sheet.
986    */
987   GetClientRect(hwndParent, &rcSheet);
988
989   /*
990    * All buttons will be at this y coordinate.
991    */
992   y = rcSheet.bottom - (padding.y + buttonHeight);
993   
994   /*
995    * Position the Back button.
996    */
997   hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
998
999   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1)) - buttonWidth;
1000
1001   SetWindowPos(hwndButton, 0, x, y, 0, 0,
1002                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1003
1004   /*
1005    * Position the Next button.
1006    */
1007   hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
1008   
1009   x += buttonWidth;
1010   
1011   SetWindowPos(hwndButton, 0, x, y, 0, 0,
1012                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1013
1014   /*
1015    * Position the Finish button.
1016    */
1017   hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
1018   
1019   if (psInfo->hasFinish)
1020     x += padding.x + buttonWidth;
1021
1022   SetWindowPos(hwndButton, 0, x, y, 0, 0,
1023                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1024
1025   if (!psInfo->hasFinish)
1026     ShowWindow(hwndButton, SW_HIDE);
1027
1028   /*
1029    * Position the Cancel button.
1030    */
1031   hwndButton = GetDlgItem(hwndParent, IDCANCEL);
1032
1033   x += padding.x + buttonWidth;
1034
1035   SetWindowPos(hwndButton, 0, x, y, 0, 0,
1036                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1037
1038   /*
1039    * Position Help button.
1040    */
1041   hwndButton = GetDlgItem(hwndParent, IDHELP);
1042
1043   if (psInfo->hasHelp)
1044   {
1045     x += padding.x + buttonWidth;
1046
1047     SetWindowPos(hwndButton, 0, x, y, 0, 0,
1048                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1049   }
1050   else
1051     ShowWindow(hwndButton, SW_HIDE);
1052
1053   if (psInfo->ppshheader.dwFlags &
1054       (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)) 
1055       padding.x = 0;
1056
1057   /*
1058    * Position and resize the sunken line.
1059    */
1060   x = padding.x;
1061   y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight);
1062
1063   lineWidth = rcSheet.right - (padding.x * 2);
1064   SetWindowPos(hwndLine, 0, x, y, lineWidth, 2,
1065                SWP_NOZORDER | SWP_NOACTIVATE);
1066
1067   /*
1068    * Position and resize the header sunken line.
1069    */
1070   
1071   SetWindowPos(hwndLineHeader, 0, 0, 0, rcSheet.right, 2,
1072                SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
1073   if (!(psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)))
1074       ShowWindow(hwndLineHeader, SW_HIDE);
1075
1076   return TRUE;
1077 }
1078
1079 /******************************************************************************
1080  *            PROPSHEET_GetPaddingInfo
1081  *
1082  * Returns the layout information.
1083  */
1084 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
1085 {
1086   HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1087   RECT rcTab;
1088   POINT tl;
1089   PADDING_INFO padding;
1090
1091   GetWindowRect(hwndTab, &rcTab);
1092
1093   tl.x = rcTab.left;
1094   tl.y = rcTab.top;
1095
1096   ScreenToClient(hwndDlg, &tl);
1097
1098   padding.x = tl.x;
1099   padding.y = tl.y;
1100
1101   return padding;
1102 }
1103
1104 /******************************************************************************
1105  *            PROPSHEET_GetPaddingInfoWizard
1106  *
1107  * Returns the layout information.
1108  * Vertical spacing is the distance between the line and the buttons.
1109  * Do NOT use the Help button to gather padding information when it isn't mapped
1110  * (PSH_HASHELP), as app writers aren't forced to supply correct coordinates
1111  * for it in this case !
1112  * FIXME: I'm not sure about any other coordinate problems with these evil
1113  * buttons. Fix it in case additional problems appear or maybe calculate
1114  * a padding in a completely different way, as this is somewhat messy.
1115  */
1116 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
1117  psInfo)
1118 {
1119   PADDING_INFO padding;
1120   RECT rc;
1121   HWND hwndControl;
1122   INT idButton;
1123   POINT ptButton, ptLine;
1124
1125   TRACE("\n");
1126   if (psInfo->hasHelp)
1127   {
1128         idButton = IDHELP;
1129   }
1130   else
1131   {
1132     if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1133     {
1134         idButton = IDC_NEXT_BUTTON;
1135     }
1136     else
1137     {
1138         /* hopefully this is ok */
1139         idButton = IDCANCEL;
1140     }
1141   }
1142
1143   hwndControl = GetDlgItem(hwndDlg, idButton);
1144   GetWindowRect(hwndControl, &rc);
1145
1146   ptButton.x = rc.left;
1147   ptButton.y = rc.top;
1148
1149   ScreenToClient(hwndDlg, &ptButton);
1150
1151   /* Line */
1152   hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
1153   GetWindowRect(hwndControl, &rc);
1154
1155   ptLine.x = rc.left;
1156   ptLine.y = rc.bottom;
1157
1158   ScreenToClient(hwndDlg, &ptLine);
1159
1160   padding.y = ptButton.y - ptLine.y;
1161
1162   if (padding.y < 0)
1163           ERR("padding negative ! Please report this !\n");
1164
1165   /* this is most probably not correct, but the best we have now */
1166   padding.x = padding.y;
1167   return padding;
1168 }
1169
1170 /******************************************************************************
1171  *            PROPSHEET_CreateTabControl
1172  *
1173  * Insert the tabs in the tab control.
1174  */
1175 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
1176                                        const PropSheetInfo * psInfo)
1177 {
1178   HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1179   TCITEMW item;
1180   int i, nTabs;
1181   int iImage = 0;
1182
1183   TRACE("\n");
1184   item.mask = TCIF_TEXT;
1185   item.cchTextMax = MAX_TABTEXT_LENGTH;
1186
1187   nTabs = psInfo->nPages;
1188
1189   /*
1190    * Set the image list for icons.
1191    */
1192   if (psInfo->hImageList)
1193   {
1194     SendMessageW(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1195   }
1196
1197   SendMessageW(GetDlgItem(hwndTabCtrl, IDC_TABCONTROL), WM_SETREDRAW, 0, 0);
1198   for (i = 0; i < nTabs; i++)
1199   {
1200     if ( psInfo->proppage[i].hasIcon )
1201     {
1202       item.mask |= TCIF_IMAGE;
1203       item.iImage = iImage++;
1204     }
1205     else
1206     {
1207       item.mask &= ~TCIF_IMAGE;
1208     }
1209
1210     item.pszText = (LPWSTR) psInfo->proppage[i].pszText;
1211     SendMessageW(hwndTabCtrl, TCM_INSERTITEMW, (WPARAM)i, (LPARAM)&item);
1212   }
1213   SendMessageW(GetDlgItem(hwndTabCtrl, IDC_TABCONTROL), WM_SETREDRAW, 1, 0);
1214
1215   return TRUE;
1216 }
1217
1218 /******************************************************************************
1219  *            PROPSHEET_WizardSubclassProc
1220  *
1221  * Subclassing window procedure for wizard extrior pages to prevent drawing
1222  * background and so drawing above the watermark.
1223  */
1224 static LRESULT CALLBACK
1225 PROPSHEET_WizardSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRef)
1226 {
1227   switch (uMsg)
1228   {
1229     case WM_ERASEBKGND:
1230       return TRUE;
1231
1232     case WM_CTLCOLORSTATIC:
1233       SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
1234       return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
1235   }
1236
1237   return DefSubclassProc(hwnd, uMsg, wParam, lParam);
1238 }
1239
1240 /*
1241  * Get the size of an in-memory Template
1242  *
1243  *( Based on the code of PROPSHEET_CollectPageInfo)
1244  * See also dialog.c/DIALOG_ParseTemplate32().
1245  */
1246
1247 static UINT GetTemplateSize(const DLGTEMPLATE* pTemplate)
1248
1249 {
1250   const WORD*  p = (const WORD *)pTemplate;
1251   BOOL  istemplateex = (((const MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF);
1252   WORD nrofitems;
1253   UINT ret;
1254
1255   if (istemplateex)
1256   {
1257     /* DLGTEMPLATEEX (not defined in any std. header file) */
1258
1259     TRACE("is DLGTEMPLATEEX\n");
1260     p++;       /* dlgVer    */
1261     p++;       /* signature */
1262     p += 2;    /* help ID   */
1263     p += 2;    /* ext style */
1264     p += 2;    /* style     */
1265   }
1266   else
1267   {
1268     /* DLGTEMPLATE */
1269
1270     TRACE("is DLGTEMPLATE\n");
1271     p += 2;    /* style     */
1272     p += 2;    /* ext style */
1273   }
1274
1275   nrofitems =   (WORD)*p; p++;    /* nb items */
1276   p++;    /*   x      */
1277   p++;    /*   y      */
1278   p++;    /*   width  */
1279   p++;    /*   height */
1280
1281   /* menu */
1282   switch ((WORD)*p)
1283   {
1284     case 0x0000:
1285       p++;
1286       break;
1287     case 0xffff:
1288       p += 2;
1289       break;
1290     default:
1291       TRACE("menu %s\n",debugstr_w((LPCWSTR)p));
1292       p += lstrlenW( (LPCWSTR)p ) + 1;
1293       break;
1294   }
1295
1296   /* class */
1297   switch ((WORD)*p)
1298   {
1299     case 0x0000:
1300       p++;
1301       break;
1302     case 0xffff:
1303       p += 2; /* 0xffff plus predefined window class ordinal value */
1304       break;
1305     default:
1306       TRACE("class %s\n",debugstr_w((LPCWSTR)p));
1307       p += lstrlenW( (LPCWSTR)p ) + 1;
1308       break;
1309   }
1310
1311   /* title */
1312   TRACE("title %s\n",debugstr_w((LPCWSTR)p));
1313   p += lstrlenW((LPCWSTR)p) + 1;
1314
1315   /* font, if DS_SETFONT set */
1316   if ((DS_SETFONT & ((istemplateex)?  ((const MyDLGTEMPLATEEX*)pTemplate)->style :
1317                      pTemplate->style)))
1318     {
1319       p+=(istemplateex)?3:1;
1320       TRACE("font %s\n",debugstr_w((LPCWSTR)p));
1321       p += lstrlenW( (LPCWSTR)p ) + 1; /* the font name */
1322     }
1323
1324   /* now process the DLGITEMTEMPLATE(EX) structs (plus custom data)
1325    * that are following the DLGTEMPLATE(EX) data */
1326   TRACE("%d items\n",nrofitems);
1327   while (nrofitems > 0)
1328     {
1329       p = (WORD*)(((DWORD_PTR)p + 3) & ~3); /* DWORD align */
1330       
1331       /* skip header */
1332       p += (istemplateex ? sizeof(MyDLGITEMTEMPLATEEX) : sizeof(DLGITEMTEMPLATE))/sizeof(WORD);
1333       
1334       /* check class */
1335       switch ((WORD)*p)
1336         {
1337         case 0x0000:
1338           p++;
1339           break;
1340         case 0xffff:
1341           TRACE("class ordinal 0x%08x\n",*(const DWORD*)p);
1342           p += 2;
1343           break;
1344         default:
1345           TRACE("class %s\n",debugstr_w((LPCWSTR)p));
1346           p += lstrlenW( (LPCWSTR)p ) + 1;
1347           break;
1348         }
1349
1350       /* check title text */
1351       switch ((WORD)*p)
1352         {
1353         case 0x0000:
1354           p++;
1355           break;
1356         case 0xffff:
1357           TRACE("text ordinal 0x%08x\n",*(const DWORD*)p);
1358           p += 2;
1359           break;
1360         default:
1361           TRACE("text %s\n",debugstr_w((LPCWSTR)p));
1362           p += lstrlenW( (LPCWSTR)p ) + 1;
1363           break;
1364         }
1365       p += *p / sizeof(WORD) + 1;    /* Skip extra data */
1366       --nrofitems;
1367     }
1368   
1369   ret = (p - (const WORD*)pTemplate) * sizeof(WORD);
1370   TRACE("%p %p size 0x%08x\n", p, pTemplate, ret);
1371   return ret;
1372 }
1373
1374 /******************************************************************************
1375  *            PROPSHEET_CreatePage
1376  *
1377  * Creates a page.
1378  */
1379 static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1380                                 int index,
1381                                 const PropSheetInfo * psInfo,
1382                                 LPCPROPSHEETPAGEW ppshpage)
1383 {
1384   DLGTEMPLATE* pTemplate;
1385   HWND hwndPage;
1386   DWORD resSize;
1387   LPVOID temp = NULL;
1388
1389   TRACE("index %d\n", index);
1390
1391   if (ppshpage == NULL)
1392   {
1393     return FALSE;
1394   }
1395
1396   if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1397     {
1398       pTemplate = (DLGTEMPLATE*)ppshpage->u.pResource;
1399       resSize = GetTemplateSize(pTemplate);
1400     }
1401   else if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1402   {
1403     HRSRC hResource;
1404     HANDLE hTemplate;
1405
1406     hResource = FindResourceW(ppshpage->hInstance,
1407                                     ppshpage->u.pszTemplate,
1408                                     (LPWSTR)RT_DIALOG);
1409     if(!hResource)
1410         return FALSE;
1411
1412     resSize = SizeofResource(ppshpage->hInstance, hResource);
1413
1414     hTemplate = LoadResource(ppshpage->hInstance, hResource);
1415     if(!hTemplate)
1416         return FALSE;
1417
1418     pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
1419     /*
1420      * Make a copy of the dialog template to make it writable
1421      */
1422   }
1423   else
1424   {
1425     HRSRC hResource;
1426     HANDLE hTemplate;
1427
1428     hResource = FindResourceA(ppshpage->hInstance,
1429                                     (LPCSTR)ppshpage->u.pszTemplate,
1430                                     (LPSTR)RT_DIALOG);
1431     if(!hResource)
1432         return FALSE;
1433
1434     resSize = SizeofResource(ppshpage->hInstance, hResource);
1435
1436     hTemplate = LoadResource(ppshpage->hInstance, hResource);
1437     if(!hTemplate)
1438         return FALSE;
1439
1440     pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
1441     /*
1442      * Make a copy of the dialog template to make it writable
1443      */
1444   }
1445   temp = Alloc(resSize);
1446   if (!temp)
1447     return FALSE;
1448   
1449   TRACE("copying pTemplate %p into temp %p (%d)\n", pTemplate, temp, resSize);
1450   memcpy(temp, pTemplate, resSize);
1451   pTemplate = temp;
1452
1453   if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
1454   {
1455     ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1456     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME;
1457     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION;
1458     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU;
1459     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP;
1460     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_DISABLED;
1461     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_VISIBLE;
1462     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_THICKFRAME;
1463
1464     ((MyDLGTEMPLATEEX*)pTemplate)->exStyle |= WS_EX_CONTROLPARENT;
1465   }
1466   else
1467   {
1468     pTemplate->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1469     pTemplate->style &= ~DS_MODALFRAME;
1470     pTemplate->style &= ~WS_CAPTION;
1471     pTemplate->style &= ~WS_SYSMENU;
1472     pTemplate->style &= ~WS_POPUP;
1473     pTemplate->style &= ~WS_DISABLED;
1474     pTemplate->style &= ~WS_VISIBLE;
1475     pTemplate->style &= ~WS_THICKFRAME;
1476
1477     pTemplate->dwExtendedStyle |= WS_EX_CONTROLPARENT;
1478   }
1479
1480   if (psInfo->proppage[index].useCallback)
1481     (*(ppshpage->pfnCallback))(0, PSPCB_CREATE,
1482                                (LPPROPSHEETPAGEW)ppshpage);
1483
1484   if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1485      hwndPage = CreateDialogIndirectParamW(ppshpage->hInstance,
1486                                         pTemplate,
1487                                         hwndParent,
1488                                         ppshpage->pfnDlgProc,
1489                                         (LPARAM)ppshpage);
1490   else
1491      hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
1492                                         pTemplate,
1493                                         hwndParent,
1494                                         ppshpage->pfnDlgProc,
1495                                         (LPARAM)ppshpage);
1496   /* Free a no more needed copy */
1497   Free(temp);
1498
1499   psInfo->proppage[index].hwndPage = hwndPage;
1500
1501   /* Subclass exterior wizard pages */
1502   if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
1503      (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
1504      (ppshpage->dwFlags & PSP_HIDEHEADER))
1505   {
1506       SetWindowSubclass(hwndPage, PROPSHEET_WizardSubclassProc, 1,
1507                         (DWORD_PTR)ppshpage);
1508   }
1509   if (!(psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD))
1510       EnableThemeDialogTexture (hwndPage, ETDT_ENABLETAB);
1511
1512   return TRUE;
1513 }
1514
1515 /******************************************************************************
1516  *            PROPSHEET_LoadWizardBitmaps
1517  *
1518  * Loads the watermark and header bitmaps for a wizard.
1519  */
1520 static VOID PROPSHEET_LoadWizardBitmaps(PropSheetInfo *psInfo)
1521 {
1522   if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD))
1523   {
1524     /* if PSH_USEHBMWATERMARK is not set, load the resource from pszbmWatermark 
1525        and put the HBITMAP in hbmWatermark. Thus all the rest of the code always 
1526        considers hbmWatermark as valid. */
1527     if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
1528         !(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK))
1529     {
1530       ((PropSheetInfo *)psInfo)->ppshheader.u4.hbmWatermark = 
1531         CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u4.pszbmWatermark, 0, NULL, 0);
1532     }
1533
1534     /* Same behavior as for watermarks */
1535     if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
1536         !(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER))
1537     {
1538       ((PropSheetInfo *)psInfo)->ppshheader.u5.hbmHeader = 
1539         CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u5.pszbmHeader, 0, NULL, 0);
1540     }
1541   }
1542 }
1543
1544
1545 /******************************************************************************
1546  *            PROPSHEET_ShowPage
1547  *
1548  * Displays or creates the specified page.
1549  */
1550 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
1551 {
1552   HWND hwndTabCtrl;
1553   HWND hwndLineHeader;
1554   LPCPROPSHEETPAGEW ppshpage;
1555
1556   TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1557   if (index == psInfo->active_page)
1558   {
1559       if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
1560           SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1561       return TRUE;
1562   }
1563
1564   ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1565   if (psInfo->proppage[index].hwndPage == 0)
1566   {
1567      PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1568   }
1569
1570   if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1571   {
1572      PROPSHEET_SetTitleW(hwndDlg, psInfo->ppshheader.dwFlags,
1573                          psInfo->proppage[index].pszText);
1574   }
1575
1576   if (psInfo->active_page != -1)
1577      ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1578
1579   ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1580
1581   /* Synchronize current selection with tab control
1582    * It seems to be needed even in case of PSH_WIZARD (no tab controls there) */
1583   hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1584   SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1585
1586   psInfo->active_page = index;
1587   psInfo->activeValid = TRUE;
1588
1589   if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW) )
1590   {
1591       hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
1592       ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1593       
1594       if ((ppshpage->dwFlags & PSP_HIDEHEADER) || (!(psInfo->ppshheader.dwFlags & PSH_HEADER)) )
1595           ShowWindow(hwndLineHeader, SW_HIDE);
1596       else
1597           ShowWindow(hwndLineHeader, SW_SHOW);
1598   }
1599
1600   return TRUE;
1601 }
1602
1603 /******************************************************************************
1604  *            PROPSHEET_Back
1605  */
1606 static BOOL PROPSHEET_Back(HWND hwndDlg)
1607 {
1608   PSHNOTIFY psn;
1609   HWND hwndPage;
1610   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1611                                                     PropSheetInfoStr);
1612   LRESULT result;
1613   int idx;
1614
1615   TRACE("active_page %d\n", psInfo->active_page);
1616   if (psInfo->active_page < 0)
1617      return FALSE;
1618
1619   psn.hdr.code     = PSN_WIZBACK;
1620   psn.hdr.hwndFrom = hwndDlg;
1621   psn.hdr.idFrom   = 0;
1622   psn.lParam       = 0;
1623
1624   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1625
1626   result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1627   if (result == -1)
1628     return FALSE;
1629   else if (result == 0)
1630      idx = psInfo->active_page - 1;
1631   else
1632      idx = PROPSHEET_FindPageByResId(psInfo, result);
1633
1634   if (idx >= 0 && idx < psInfo->nPages)
1635   {
1636      if (PROPSHEET_CanSetCurSel(hwndDlg))
1637         PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1638   }
1639   return TRUE;
1640 }
1641
1642 /******************************************************************************
1643  *            PROPSHEET_Next
1644  */
1645 static BOOL PROPSHEET_Next(HWND hwndDlg)
1646 {
1647   PSHNOTIFY psn;
1648   HWND hwndPage;
1649   LRESULT msgResult = 0;
1650   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1651                                                     PropSheetInfoStr);
1652   int idx;
1653
1654   TRACE("active_page %d\n", psInfo->active_page);
1655   if (psInfo->active_page < 0)
1656      return FALSE;
1657
1658   psn.hdr.code     = PSN_WIZNEXT;
1659   psn.hdr.hwndFrom = hwndDlg;
1660   psn.hdr.idFrom   = 0;
1661   psn.lParam       = 0;
1662
1663   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1664
1665   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1666   if (msgResult == -1)
1667     return FALSE;
1668   else if (msgResult == 0)
1669      idx = psInfo->active_page + 1;
1670   else
1671      idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1672
1673   if (idx < psInfo->nPages )
1674   {
1675      if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1676         PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1677   }
1678
1679   return TRUE;
1680 }
1681
1682 /******************************************************************************
1683  *            PROPSHEET_Finish
1684  */
1685 static BOOL PROPSHEET_Finish(HWND hwndDlg)
1686 {
1687   PSHNOTIFY psn;
1688   HWND hwndPage;
1689   LRESULT msgResult = 0;
1690   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1691                                                     PropSheetInfoStr);
1692
1693   TRACE("active_page %d\n", psInfo->active_page);
1694   if (psInfo->active_page < 0)
1695      return FALSE;
1696
1697   psn.hdr.code     = PSN_WIZFINISH;
1698   psn.hdr.hwndFrom = hwndDlg;
1699   psn.hdr.idFrom   = 0;
1700   psn.lParam       = 0;
1701
1702   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1703
1704   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1705
1706   TRACE("msg result %ld\n", msgResult);
1707
1708   if (msgResult != 0)
1709     return FALSE;
1710
1711   if (psInfo->result == 0)
1712       psInfo->result = IDOK;
1713   if (psInfo->isModeless)
1714     psInfo->activeValid = FALSE;
1715   else
1716     psInfo->ended = TRUE;
1717
1718   return TRUE;
1719 }
1720
1721 /******************************************************************************
1722  *            PROPSHEET_Apply
1723  */
1724 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1725 {
1726   int i;
1727   HWND hwndPage;
1728   PSHNOTIFY psn;
1729   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1730                                                     PropSheetInfoStr);
1731
1732   TRACE("active_page %d\n", psInfo->active_page);
1733   if (psInfo->active_page < 0)
1734      return FALSE;
1735
1736   psn.hdr.hwndFrom = hwndDlg;
1737   psn.hdr.idFrom   = 0;
1738   psn.lParam       = 0;
1739
1740
1741   /*
1742    * Send PSN_KILLACTIVE to the current page.
1743    */
1744   psn.hdr.code = PSN_KILLACTIVE;
1745
1746   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1747
1748   if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1749     return FALSE;
1750
1751   /*
1752    * Send PSN_APPLY to all pages.
1753    */
1754   psn.hdr.code = PSN_APPLY;
1755   psn.lParam   = lParam;
1756
1757   for (i = 0; i < psInfo->nPages; i++)
1758   {
1759     hwndPage = psInfo->proppage[i].hwndPage;
1760     if (hwndPage)
1761     {
1762        switch (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1763        {
1764        case PSNRET_INVALID:
1765            PROPSHEET_ShowPage(hwndDlg, i, psInfo);
1766            /* fall through */
1767        case PSNRET_INVALID_NOCHANGEPAGE:
1768            return FALSE;
1769        }
1770     }
1771   }
1772
1773   if(lParam)
1774   {
1775      psInfo->activeValid = FALSE;
1776   }
1777   else if(psInfo->active_page >= 0)
1778   {
1779      psn.hdr.code = PSN_SETACTIVE;
1780      psn.lParam   = 0;
1781      hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1782      SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1783   }
1784
1785   return TRUE;
1786 }
1787
1788 /******************************************************************************
1789  *            PROPSHEET_Cancel
1790  */
1791 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1792 {
1793   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1794                                                     PropSheetInfoStr);
1795   HWND hwndPage;
1796   PSHNOTIFY psn;
1797   int i;
1798
1799   TRACE("active_page %d\n", psInfo->active_page);
1800   if (psInfo->active_page < 0)
1801      return;
1802
1803   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1804   psn.hdr.code     = PSN_QUERYCANCEL;
1805   psn.hdr.hwndFrom = hwndDlg;
1806   psn.hdr.idFrom   = 0;
1807   psn.lParam       = 0;
1808
1809   if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1810     return;
1811
1812   psn.hdr.code = PSN_RESET;
1813   psn.lParam   = lParam;
1814
1815   for (i = 0; i < psInfo->nPages; i++)
1816   {
1817     hwndPage = psInfo->proppage[i].hwndPage;
1818
1819     if (hwndPage)
1820        SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1821   }
1822
1823   if (psInfo->isModeless)
1824   {
1825      /* makes PSM_GETCURRENTPAGEHWND return NULL */
1826      psInfo->activeValid = FALSE;
1827   }
1828   else
1829     psInfo->ended = TRUE;
1830 }
1831
1832 /******************************************************************************
1833  *            PROPSHEET_Help
1834  */
1835 static void PROPSHEET_Help(HWND hwndDlg)
1836 {
1837   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1838                                                     PropSheetInfoStr);
1839   HWND hwndPage;
1840   PSHNOTIFY psn;
1841
1842   TRACE("active_page %d\n", psInfo->active_page);
1843   if (psInfo->active_page < 0)
1844      return;
1845
1846   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1847   psn.hdr.code     = PSN_HELP;
1848   psn.hdr.hwndFrom = hwndDlg;
1849   psn.hdr.idFrom   = 0;
1850   psn.lParam       = 0;
1851
1852   SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1853 }
1854
1855 /******************************************************************************
1856  *            PROPSHEET_Changed
1857  */
1858 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1859 {
1860   int i;
1861   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1862                                                     PropSheetInfoStr);
1863
1864   TRACE("\n");
1865   if (!psInfo) return;
1866   /*
1867    * Set the dirty flag of this page.
1868    */
1869   for (i = 0; i < psInfo->nPages; i++)
1870   {
1871     if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1872       psInfo->proppage[i].isDirty = TRUE;
1873   }
1874
1875   /*
1876    * Enable the Apply button.
1877    */
1878   if (psInfo->hasApply)
1879   {
1880     HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1881
1882     EnableWindow(hwndApplyBtn, TRUE);
1883   }
1884 }
1885
1886 /******************************************************************************
1887  *            PROPSHEET_UnChanged
1888  */
1889 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1890 {
1891   int i;
1892   BOOL noPageDirty = TRUE;
1893   HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1894   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1895                                                     PropSheetInfoStr);
1896
1897   TRACE("\n");
1898   if ( !psInfo ) return;
1899   for (i = 0; i < psInfo->nPages; i++)
1900   {
1901     /* set the specified page as clean */
1902     if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1903       psInfo->proppage[i].isDirty = FALSE;
1904
1905     /* look to see if there's any dirty pages */
1906     if (psInfo->proppage[i].isDirty)
1907       noPageDirty = FALSE;
1908   }
1909
1910   /*
1911    * Disable Apply button.
1912    */
1913   if (noPageDirty)
1914     EnableWindow(hwndApplyBtn, FALSE);
1915 }
1916
1917 /******************************************************************************
1918  *            PROPSHEET_PressButton
1919  */
1920 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1921 {
1922   TRACE("buttonID %d\n", buttonID);
1923   switch (buttonID)
1924   {
1925     case PSBTN_APPLYNOW:
1926       PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1927       break;
1928     case PSBTN_BACK:
1929       PROPSHEET_Back(hwndDlg);
1930       break;
1931     case PSBTN_CANCEL:
1932       PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1933       break;
1934     case PSBTN_FINISH:
1935       PROPSHEET_Finish(hwndDlg);
1936       break;
1937     case PSBTN_HELP:
1938       PROPSHEET_DoCommand(hwndDlg, IDHELP);
1939       break;
1940     case PSBTN_NEXT:
1941       PROPSHEET_Next(hwndDlg);
1942       break;
1943     case PSBTN_OK:
1944       PROPSHEET_DoCommand(hwndDlg, IDOK);
1945       break;
1946     default:
1947       FIXME("Invalid button index %d\n", buttonID);
1948   }
1949 }
1950
1951
1952 /*************************************************************************
1953  * BOOL PROPSHEET_CanSetCurSel [Internal]
1954  *
1955  * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1956  *
1957  * PARAMS
1958  *     hwndDlg        [I] handle to a Dialog hWnd
1959  *
1960  * RETURNS
1961  *     TRUE if Current Selection can change
1962  *
1963  * NOTES
1964  */
1965 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1966 {
1967   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1968                                                     PropSheetInfoStr);
1969   HWND hwndPage;
1970   PSHNOTIFY psn;
1971   BOOL res = FALSE;
1972
1973   TRACE("active_page %d\n", psInfo->active_page);
1974   if (!psInfo)
1975   {
1976      res = FALSE;
1977      goto end;
1978   }
1979
1980   if (psInfo->active_page < 0)
1981   {
1982      res = TRUE;
1983      goto end;
1984   }
1985
1986   /*
1987    * Notify the current page.
1988    */
1989   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1990   psn.hdr.code     = PSN_KILLACTIVE;
1991   psn.hdr.hwndFrom = hwndDlg;
1992   psn.hdr.idFrom   = 0;
1993   psn.lParam       = 0;
1994
1995   res = !SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1996
1997 end:
1998   TRACE("<-- %d\n", res);
1999   return res;
2000 }
2001
2002 /******************************************************************************
2003  *            PROPSHEET_SetCurSel
2004  */
2005 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
2006                                 int index,
2007                                 int skipdir,
2008                                 HPROPSHEETPAGE hpage
2009                                 )
2010 {
2011   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2012   HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
2013   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2014
2015   TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
2016   /* hpage takes precedence over index */
2017   if (hpage != NULL)
2018     index = PROPSHEET_GetPageIndex(hpage, psInfo);
2019
2020   if (index < 0 || index >= psInfo->nPages)
2021   {
2022     TRACE("Could not find page to select!\n");
2023     return FALSE;
2024   }
2025
2026   /* unset active page while doing this transition. */
2027   if (psInfo->active_page != -1)
2028      ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
2029   psInfo->active_page = -1;
2030
2031   while (1) {
2032     int result;
2033     PSHNOTIFY psn;
2034     RECT rc;
2035     LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2036
2037     if (hwndTabControl)
2038         SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);
2039
2040     psn.hdr.code     = PSN_SETACTIVE;
2041     psn.hdr.hwndFrom = hwndDlg;
2042     psn.hdr.idFrom   = 0;
2043     psn.lParam       = 0;
2044
2045     if (!psInfo->proppage[index].hwndPage) {
2046       PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
2047     }
2048
2049     /* Resize the property sheet page to the fit in the Tab control
2050      * (for regular property sheets) or to fit in the client area (for
2051      * wizards).
2052      * NOTE: The resizing happens every time the page is selected and
2053      * not only when it's created (some applications depend on it). */
2054     PROPSHEET_GetPageRect(psInfo, hwndDlg, &rc, ppshpage);
2055     TRACE("setting page %p, rc (%d,%d)-(%d,%d) w=%d, h=%d\n",
2056           psInfo->proppage[index].hwndPage, rc.left, rc.top, rc.right, rc.bottom,
2057           rc.right - rc.left, rc.bottom - rc.top);
2058     SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP,
2059                  rc.left, rc.top,
2060                  rc.right - rc.left, rc.bottom - rc.top, 0);
2061
2062     result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2063     if (!result)
2064       break;
2065     if (result == -1) {
2066       index+=skipdir;
2067       if (index < 0) {
2068         index = 0;
2069         WARN("Tried to skip before first property sheet page!\n");
2070         break;
2071       }
2072       if (index >= psInfo->nPages) {
2073         WARN("Tried to skip after last property sheet page!\n");
2074         index = psInfo->nPages-1;
2075         break;
2076       }
2077     }
2078     else if (result != 0)
2079     {
2080       int old_index = index;
2081       index = PROPSHEET_FindPageByResId(psInfo, result);
2082       if(index >= psInfo->nPages) {
2083         index = old_index;
2084         WARN("Tried to skip to nonexistant page by res id\n");
2085         break;
2086       }
2087       continue;
2088     }
2089   }
2090
2091   /* Invalidate the header area */
2092   if ( (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
2093        (psInfo->ppshheader.dwFlags & PSH_HEADER) )
2094   {
2095     HWND hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
2096     RECT r;
2097
2098     GetClientRect(hwndLineHeader, &r);
2099     MapWindowPoints(hwndLineHeader, hwndDlg, (LPPOINT) &r, 2);
2100     SetRect(&r, 0, 0, r.right + 1, r.top - 1);
2101
2102     InvalidateRect(hwndDlg, &r, TRUE);
2103   }
2104
2105   /*
2106    * Display the new page.
2107    */
2108   PROPSHEET_ShowPage(hwndDlg, index, psInfo);
2109
2110   if (psInfo->proppage[index].hasHelp)
2111     EnableWindow(hwndHelp, TRUE);
2112   else
2113     EnableWindow(hwndHelp, FALSE);
2114
2115   return TRUE;
2116 }
2117
2118 /******************************************************************************
2119  *            PROPSHEET_SetCurSelId
2120  *
2121  * Selects the page, specified by resource id.
2122  */
2123 static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id)
2124 {
2125       int idx;
2126       PropSheetInfo* psInfo =
2127           (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2128
2129       idx = PROPSHEET_FindPageByResId(psInfo, id);
2130       if (idx < psInfo->nPages )
2131       {
2132           if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
2133               PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
2134       }
2135 }
2136
2137 /******************************************************************************
2138  *            PROPSHEET_SetTitleA
2139  */
2140 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
2141 {
2142   if(HIWORD(lpszText))
2143   {
2144      WCHAR szTitle[256];
2145      MultiByteToWideChar(CP_ACP, 0, lpszText, -1,
2146                          szTitle, sizeof(szTitle)/sizeof(WCHAR));
2147      PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
2148   }
2149   else
2150   {
2151      PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
2152   }
2153 }
2154
2155 /******************************************************************************
2156  *            PROPSHEET_SetTitleW
2157  */
2158 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
2159 {
2160   PropSheetInfo*        psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2161   WCHAR                 szTitle[256];
2162
2163   TRACE("%s (style %08x)\n", debugstr_w(lpszText), dwStyle);
2164   if (HIWORD(lpszText) == 0) {
2165     if (!LoadStringW(psInfo->ppshheader.hInstance,
2166                      LOWORD(lpszText), szTitle, sizeof(szTitle)-sizeof(WCHAR)))
2167       return;
2168     lpszText = szTitle;
2169   }
2170   if (dwStyle & PSH_PROPTITLE)
2171   {
2172     WCHAR* dest;
2173     int lentitle = strlenW(lpszText);
2174     int lenprop  = strlenW(psInfo->strPropertiesFor);
2175
2176     dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2177     wsprintfW(dest, psInfo->strPropertiesFor, lpszText);
2178
2179     SetWindowTextW(hwndDlg, dest);
2180     Free(dest);
2181   }
2182   else
2183     SetWindowTextW(hwndDlg, lpszText);
2184 }
2185
2186 /******************************************************************************
2187  *            PROPSHEET_SetFinishTextA
2188  */
2189 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
2190 {
2191   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2192   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2193
2194   TRACE("'%s'\n", lpszText);
2195   /* Set text, show and enable the Finish button */
2196   SetWindowTextA(hwndButton, lpszText);
2197   ShowWindow(hwndButton, SW_SHOW);
2198   EnableWindow(hwndButton, TRUE);
2199
2200   /* Make it default pushbutton */
2201   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2202
2203   /* Hide Back button */
2204   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2205   ShowWindow(hwndButton, SW_HIDE);
2206
2207   if (!psInfo->hasFinish)
2208   {
2209     /* Hide Next button */
2210     hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2211     ShowWindow(hwndButton, SW_HIDE);
2212   }
2213 }
2214
2215 /******************************************************************************
2216  *            PROPSHEET_SetFinishTextW
2217  */
2218 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
2219 {
2220   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2221   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2222
2223   TRACE("%s\n", debugstr_w(lpszText));
2224   /* Set text, show and enable the Finish button */
2225   SetWindowTextW(hwndButton, lpszText);
2226   ShowWindow(hwndButton, SW_SHOW);
2227   EnableWindow(hwndButton, TRUE);
2228
2229   /* Make it default pushbutton */
2230   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2231
2232   /* Hide Back button */
2233   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2234   ShowWindow(hwndButton, SW_HIDE);
2235
2236   if (!psInfo->hasFinish)
2237   {
2238     /* Hide Next button */
2239     hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2240     ShowWindow(hwndButton, SW_HIDE);
2241   }
2242 }
2243
2244 /******************************************************************************
2245  *            PROPSHEET_QuerySiblings
2246  */
2247 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
2248                                        WPARAM wParam, LPARAM lParam)
2249 {
2250   int i = 0;
2251   HWND hwndPage;
2252   LRESULT msgResult = 0;
2253   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2254
2255   while ((i < psInfo->nPages) && (msgResult == 0))
2256   {
2257     hwndPage = psInfo->proppage[i].hwndPage;
2258     msgResult = SendMessageW(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2259     i++;
2260   }
2261
2262   return msgResult;
2263 }
2264
2265
2266 /******************************************************************************
2267  *            PROPSHEET_AddPage
2268  */
2269 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
2270                               HPROPSHEETPAGE hpage)
2271 {
2272   PropPageInfo * ppi;
2273   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2274                                                      PropSheetInfoStr);
2275   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2276   TCITEMW item;
2277   LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
2278
2279   TRACE("hpage %p\n", hpage);
2280   /*
2281    * Allocate and fill in a new PropPageInfo entry.
2282    */
2283   ppi = (PropPageInfo*) ReAlloc(psInfo->proppage,
2284                                 sizeof(PropPageInfo) *
2285                                 (psInfo->nPages + 1));
2286   if (!ppi)
2287       return FALSE;
2288
2289   psInfo->proppage = ppi;
2290   if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages, FALSE))
2291       return FALSE;
2292
2293   psInfo->proppage[psInfo->nPages].hpage = hpage;
2294
2295   if (ppsp->dwFlags & PSP_PREMATURE)
2296   {
2297      /* Create the page but don't show it */
2298      PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp);
2299   }
2300
2301   /*
2302    * Add a new tab to the tab control.
2303    */
2304   item.mask = TCIF_TEXT;
2305   item.pszText = (LPWSTR) psInfo->proppage[psInfo->nPages].pszText;
2306   item.cchTextMax = MAX_TABTEXT_LENGTH;
2307
2308   if (psInfo->hImageList)
2309   {
2310     SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
2311   }
2312
2313   if ( psInfo->proppage[psInfo->nPages].hasIcon )
2314   {
2315     item.mask |= TCIF_IMAGE;
2316     item.iImage = psInfo->nPages;
2317   }
2318
2319   SendMessageW(hwndTabControl, TCM_INSERTITEMW, psInfo->nPages + 1,
2320                (LPARAM)&item);
2321
2322   psInfo->nPages++;
2323
2324   /* If it is the only page - show it */
2325   if(psInfo->nPages == 1)
2326      PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2327   return TRUE;
2328 }
2329
2330 /******************************************************************************
2331  *            PROPSHEET_RemovePage
2332  */
2333 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
2334                                  int index,
2335                                  HPROPSHEETPAGE hpage)
2336 {
2337   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2338                                                      PropSheetInfoStr);
2339   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2340   PropPageInfo* oldPages;
2341
2342   TRACE("index %d, hpage %p\n", index, hpage);
2343   if (!psInfo) {
2344     return FALSE;
2345   }
2346   /*
2347    * hpage takes precedence over index.
2348    */
2349   if (hpage != 0)
2350   {
2351     index = PROPSHEET_GetPageIndex(hpage, psInfo);
2352   }
2353
2354   /* Make sure that index is within range */
2355   if (index < 0 || index >= psInfo->nPages)
2356   {
2357       TRACE("Could not find page to remove!\n");
2358       return FALSE;
2359   }
2360
2361   TRACE("total pages %d removing page %d active page %d\n",
2362         psInfo->nPages, index, psInfo->active_page);
2363   /*
2364    * Check if we're removing the active page.
2365    */
2366   if (index == psInfo->active_page)
2367   {
2368     if (psInfo->nPages > 1)
2369     {
2370       if (index > 0)
2371       {
2372         /* activate previous page  */
2373         PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2374       }
2375       else
2376       {
2377         /* activate the next page */
2378         PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2379         psInfo->active_page = index;
2380       }
2381     }
2382     else
2383     {
2384       psInfo->active_page = -1;
2385       if (!psInfo->isModeless)
2386       {
2387          psInfo->ended = TRUE;
2388          return TRUE;
2389       }
2390     }
2391   }
2392   else if (index < psInfo->active_page)
2393     psInfo->active_page--;
2394
2395   /* Unsubclass the page dialog window */
2396   if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD) &&
2397      (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2398      ((PROPSHEETPAGEW*)psInfo->proppage[index].hpage)->dwFlags & PSP_HIDEHEADER))
2399   {
2400      RemoveWindowSubclass(psInfo->proppage[index].hwndPage,
2401                           PROPSHEET_WizardSubclassProc, 1);
2402   }
2403
2404   /* Destroy page dialog window */
2405   DestroyWindow(psInfo->proppage[index].hwndPage);
2406
2407   /* Free page resources */
2408   if(psInfo->proppage[index].hpage)
2409   {
2410      PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)psInfo->proppage[index].hpage;
2411
2412      if (psp->dwFlags & PSP_USETITLE)
2413         Free ((LPVOID)psInfo->proppage[index].pszText);
2414
2415      DestroyPropertySheetPage(psInfo->proppage[index].hpage);
2416   }
2417
2418   /* Remove the tab */
2419   SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2420
2421   oldPages = psInfo->proppage;
2422   psInfo->nPages--;
2423   psInfo->proppage = Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2424
2425   if (index > 0)
2426     memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
2427
2428   if (index < psInfo->nPages)
2429     memcpy(&psInfo->proppage[index], &oldPages[index + 1],
2430            (psInfo->nPages - index) * sizeof(PropPageInfo));
2431
2432   Free(oldPages);
2433
2434   return FALSE;
2435 }
2436
2437 /******************************************************************************
2438  *            PROPSHEET_SetWizButtons
2439  *
2440  * This code will work if (and assumes that) the Next button is on top of the
2441  * Finish button. ie. Finish comes after Next in the Z order.
2442  * This means make sure the dialog template reflects this.
2443  *
2444  */
2445 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
2446 {
2447   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2448                                                     PropSheetInfoStr);
2449   HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2450   HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2451   HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2452
2453   TRACE("%d\n", dwFlags);
2454
2455   EnableWindow(hwndBack, FALSE);
2456   EnableWindow(hwndNext, FALSE);
2457   EnableWindow(hwndFinish, FALSE);
2458
2459   /* set the default pushbutton to an enabled button */
2460   if (((dwFlags & PSWIZB_FINISH) || psInfo->hasFinish) && !(dwFlags & PSWIZB_DISABLEDFINISH))
2461     SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2462   else if (dwFlags & PSWIZB_NEXT)
2463     SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
2464   else if (dwFlags & PSWIZB_BACK)
2465     SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
2466   else
2467     SendMessageW(hwndDlg, DM_SETDEFID, IDCANCEL, 0);
2468
2469
2470   if (dwFlags & PSWIZB_BACK)
2471     EnableWindow(hwndBack, TRUE);
2472
2473   if (dwFlags & PSWIZB_NEXT)
2474     EnableWindow(hwndNext, TRUE);
2475
2476   if (!psInfo->hasFinish)
2477   {
2478     if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
2479     {
2480       /* Hide the Next button */
2481       ShowWindow(hwndNext, SW_HIDE);
2482       
2483       /* Show the Finish button */
2484       ShowWindow(hwndFinish, SW_SHOW);
2485
2486       if (!(dwFlags & PSWIZB_DISABLEDFINISH))
2487         EnableWindow(hwndFinish, TRUE);
2488     }
2489     else
2490     {
2491       /* Hide the Finish button */
2492       ShowWindow(hwndFinish, SW_HIDE);
2493       /* Show the Next button */
2494       ShowWindow(hwndNext, SW_SHOW);
2495     }
2496   }
2497   else if (!(dwFlags & PSWIZB_DISABLEDFINISH))
2498     EnableWindow(hwndFinish, TRUE);
2499 }
2500
2501 /******************************************************************************
2502  *            PROPSHEET_InsertPage
2503  */
2504 static BOOL PROPSHEET_InsertPage(HWND hwndDlg, HPROPSHEETPAGE hpageInsertAfter, HPROPSHEETPAGE hpage)
2505 {
2506     if (!HIWORD(hpageInsertAfter))
2507         FIXME("(%p, %d, %p): stub\n", hwndDlg, LOWORD(hpageInsertAfter), hpage);
2508     else
2509         FIXME("(%p, %p, %p): stub\n", hwndDlg, hpageInsertAfter, hpage);
2510     return FALSE;
2511 }
2512
2513 /******************************************************************************
2514  *            PROPSHEET_SetHeaderTitleW
2515  */
2516 static void PROPSHEET_SetHeaderTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderTitle)
2517 {
2518     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderTitle));
2519 }
2520
2521 /******************************************************************************
2522  *            PROPSHEET_SetHeaderTitleA
2523  */
2524 static void PROPSHEET_SetHeaderTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderTitle)
2525 {
2526     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderTitle));
2527 }
2528
2529 /******************************************************************************
2530  *            PROPSHEET_SetHeaderSubTitleW
2531  */
2532 static void PROPSHEET_SetHeaderSubTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderSubTitle)
2533 {
2534     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderSubTitle));
2535 }
2536
2537 /******************************************************************************
2538  *            PROPSHEET_SetHeaderSubTitleA
2539  */
2540 static void PROPSHEET_SetHeaderSubTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderSubTitle)
2541 {
2542     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderSubTitle));
2543 }
2544
2545 /******************************************************************************
2546  *            PROPSHEET_HwndToIndex
2547  */
2548 static LRESULT PROPSHEET_HwndToIndex(HWND hwndDlg, HWND hPageDlg)
2549 {
2550     int index;
2551     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2552                                                        PropSheetInfoStr);
2553
2554     TRACE("(%p, %p)\n", hwndDlg, hPageDlg);
2555
2556     for (index = 0; index < psInfo->nPages; index++)
2557         if (psInfo->proppage[index].hwndPage == hPageDlg)
2558             return index;
2559     WARN("%p not found\n", hPageDlg);
2560     return -1;
2561 }
2562
2563 /******************************************************************************
2564  *            PROPSHEET_IndexToHwnd
2565  */
2566 static LRESULT PROPSHEET_IndexToHwnd(HWND hwndDlg, int iPageIndex)
2567 {
2568     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2569                                                        PropSheetInfoStr);
2570     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2571     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2572         WARN("%d out of range.\n", iPageIndex);
2573         return 0;
2574     }
2575     return (LRESULT)psInfo->proppage[iPageIndex].hwndPage;
2576 }
2577
2578 /******************************************************************************
2579  *            PROPSHEET_PageToIndex
2580  */
2581 static LRESULT PROPSHEET_PageToIndex(HWND hwndDlg, HPROPSHEETPAGE hPage)
2582 {
2583     int index;
2584     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2585                                                        PropSheetInfoStr);
2586
2587     TRACE("(%p, %p)\n", hwndDlg, hPage);
2588
2589     for (index = 0; index < psInfo->nPages; index++)
2590         if (psInfo->proppage[index].hpage == hPage)
2591             return index;
2592     WARN("%p not found\n", hPage);
2593     return -1;
2594 }
2595
2596 /******************************************************************************
2597  *            PROPSHEET_IndexToPage
2598  */
2599 static LRESULT PROPSHEET_IndexToPage(HWND hwndDlg, int iPageIndex)
2600 {
2601     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2602                                                        PropSheetInfoStr);
2603     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2604     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2605         WARN("%d out of range.\n", iPageIndex);
2606         return 0;
2607     }
2608     return (LRESULT)psInfo->proppage[iPageIndex].hpage;
2609 }
2610
2611 /******************************************************************************
2612  *            PROPSHEET_IdToIndex
2613  */
2614 static LRESULT PROPSHEET_IdToIndex(HWND hwndDlg, int iPageId)
2615 {
2616     int index;
2617     LPCPROPSHEETPAGEW psp;
2618     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2619                                                        PropSheetInfoStr);
2620     TRACE("(%p, %d)\n", hwndDlg, iPageId);
2621     for (index = 0; index < psInfo->nPages; index++) {
2622         psp = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2623         if (psp->u.pszTemplate == MAKEINTRESOURCEW(iPageId))
2624             return index;
2625     }
2626
2627     return -1;
2628 }
2629
2630 /******************************************************************************
2631  *            PROPSHEET_IndexToId
2632  */
2633 static LRESULT PROPSHEET_IndexToId(HWND hwndDlg, int iPageIndex)
2634 {
2635     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2636                                                        PropSheetInfoStr);
2637     LPCPROPSHEETPAGEW psp;
2638     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2639     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2640         WARN("%d out of range.\n", iPageIndex);
2641         return 0;
2642     }
2643     psp = (LPCPROPSHEETPAGEW)psInfo->proppage[iPageIndex].hpage;
2644     if (psp->dwFlags & PSP_DLGINDIRECT || HIWORD(psp->u.pszTemplate)) {
2645         return 0;
2646     }
2647     return (LRESULT)psp->u.pszTemplate;
2648 }
2649
2650 /******************************************************************************
2651  *            PROPSHEET_GetResult
2652  */
2653 static LRESULT PROPSHEET_GetResult(HWND hwndDlg)
2654 {
2655     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2656                                                        PropSheetInfoStr);
2657     return psInfo->result;
2658 }
2659
2660 /******************************************************************************
2661  *            PROPSHEET_RecalcPageSizes
2662  */
2663 static BOOL PROPSHEET_RecalcPageSizes(HWND hwndDlg)
2664 {
2665     FIXME("(%p): stub\n", hwndDlg);
2666     return FALSE;
2667 }
2668
2669 /******************************************************************************
2670  *            PROPSHEET_GetPageIndex
2671  *
2672  * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
2673  * the array of PropPageInfo.
2674  */
2675 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, const PropSheetInfo* psInfo)
2676 {
2677   BOOL found = FALSE;
2678   int index = 0;
2679
2680   TRACE("hpage %p\n", hpage);
2681   while ((index < psInfo->nPages) && (found == FALSE))
2682   {
2683     if (psInfo->proppage[index].hpage == hpage)
2684       found = TRUE;
2685     else
2686       index++;
2687   }
2688
2689   if (found == FALSE)
2690     index = -1;
2691
2692   return index;
2693 }
2694
2695 /******************************************************************************
2696  *            PROPSHEET_CleanUp
2697  */
2698 static void PROPSHEET_CleanUp(HWND hwndDlg)
2699 {
2700   int i;
2701   PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropW(hwndDlg,
2702                                                        PropSheetInfoStr);
2703
2704   TRACE("\n");
2705   if (!psInfo) return;
2706   if (HIWORD(psInfo->ppshheader.pszCaption))
2707       Free ((LPVOID)psInfo->ppshheader.pszCaption);
2708
2709   for (i = 0; i < psInfo->nPages; i++)
2710   {
2711      PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
2712
2713      /* Unsubclass the page dialog window */
2714      if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
2715         (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2716         (psp->dwFlags & PSP_HIDEHEADER))
2717      {
2718         RemoveWindowSubclass(psInfo->proppage[i].hwndPage,
2719                              PROPSHEET_WizardSubclassProc, 1);
2720      }
2721
2722      if(psInfo->proppage[i].hwndPage)
2723         DestroyWindow(psInfo->proppage[i].hwndPage);
2724
2725      if(psp)
2726      {
2727         if (psp->dwFlags & PSP_USETITLE)
2728            Free ((LPVOID)psInfo->proppage[i].pszText);
2729
2730         DestroyPropertySheetPage(psInfo->proppage[i].hpage);
2731      }
2732   }
2733
2734   DeleteObject(psInfo->hFont);
2735   DeleteObject(psInfo->hFontBold);
2736   /* If we created the bitmaps, destroy them */
2737   if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2738       (!(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK)) )
2739       DeleteObject(psInfo->ppshheader.u4.hbmWatermark);
2740   if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
2741       (!(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER)) )
2742       DeleteObject(psInfo->ppshheader.u5.hbmHeader);
2743
2744   Free(psInfo->proppage);
2745   Free(psInfo->strPropertiesFor);
2746   ImageList_Destroy(psInfo->hImageList);
2747
2748   GlobalFree((HGLOBAL)psInfo);
2749 }
2750
2751 static INT do_loop(const PropSheetInfo *psInfo)
2752 {
2753     MSG msg;
2754     INT ret = -1;
2755     HWND hwnd = psInfo->hwnd;
2756
2757     while(IsWindow(hwnd) && !psInfo->ended && (ret = GetMessageW(&msg, NULL, 0, 0)))
2758     {
2759         if(ret == -1)
2760             break;
2761
2762         if(!IsDialogMessageW(hwnd, &msg))
2763         {
2764             TranslateMessage(&msg);
2765             DispatchMessageW(&msg);
2766         }
2767     }
2768
2769     if(ret == 0)
2770     {
2771         PostQuitMessage(msg.wParam);
2772         ret = -1;
2773     }
2774
2775     if(ret != -1)
2776         ret = psInfo->result;
2777
2778     DestroyWindow(hwnd);
2779     return ret;
2780 }
2781
2782 /******************************************************************************
2783  *            PROPSHEET_PropertySheet
2784  *
2785  * Common code between PropertySheetA/W
2786  */
2787 static INT_PTR PROPSHEET_PropertySheet(PropSheetInfo* psInfo, BOOL unicode)
2788 {
2789   INT_PTR bRet = 0;
2790   if (psInfo->active_page >= psInfo->nPages) psInfo->active_page = 0;
2791   TRACE("startpage: %d of %d pages\n", psInfo->active_page, psInfo->nPages);
2792
2793   psInfo->unicode = unicode;
2794   psInfo->ended = FALSE;
2795
2796   bRet = PROPSHEET_CreateDialog(psInfo);
2797   if(!psInfo->isModeless)
2798   {
2799       HWND parent = GetParent(psInfo->hwnd);
2800       if (parent) EnableWindow(parent, FALSE);
2801       bRet = do_loop(psInfo);
2802       if (parent) EnableWindow(parent, TRUE);
2803   }
2804   return bRet;
2805 }
2806
2807 /******************************************************************************
2808  *            PropertySheet    (COMCTL32.@)
2809  *            PropertySheetA   (COMCTL32.@)
2810  *
2811  * Creates a property sheet in the specified property sheet header.
2812  *
2813  * RETURNS
2814  *     Modal property sheets: Positive if successful or -1 otherwise.
2815  *     Modeless property sheets: Property sheet handle.
2816  *     Or:
2817  *| ID_PSREBOOTSYSTEM - The user must reboot the computer for the changes to take effect.
2818  *| ID_PSRESTARTWINDOWS - The user must restart Windows for the changes to take effect.
2819  */
2820 INT_PTR WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2821 {
2822   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2823                                                        sizeof(PropSheetInfo));
2824   UINT i, n;
2825   const BYTE* pByte;
2826
2827   TRACE("(%p)\n", lppsh);
2828
2829   PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2830
2831   psInfo->proppage = (PropPageInfo*) Alloc(sizeof(PropPageInfo) *
2832                                                     lppsh->nPages);
2833   pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2834
2835   for (n = i = 0; i < lppsh->nPages; i++, n++)
2836   {
2837     if (!psInfo->usePropPage)
2838       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2839     else
2840     {
2841        psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
2842        pByte += ((LPCPROPSHEETPAGEA)pByte)->dwSize;
2843     }
2844
2845     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2846                                psInfo, n, TRUE))
2847     {
2848         if (psInfo->usePropPage)
2849             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2850         n--;
2851         psInfo->nPages--;
2852     }
2853   }
2854
2855   return PROPSHEET_PropertySheet(psInfo, FALSE);
2856 }
2857
2858 /******************************************************************************
2859  *            PropertySheetW   (COMCTL32.@)
2860  *
2861  * See PropertySheetA.
2862  */
2863 INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2864 {
2865   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2866                                                        sizeof(PropSheetInfo));
2867   UINT i, n;
2868   const BYTE* pByte;
2869
2870   TRACE("(%p)\n", lppsh);
2871
2872   PROPSHEET_CollectSheetInfoW(lppsh, psInfo);
2873
2874   psInfo->proppage = (PropPageInfo*) Alloc(sizeof(PropPageInfo) *
2875                                                     lppsh->nPages);
2876   pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2877
2878   for (n = i = 0; i < lppsh->nPages; i++, n++)
2879   {
2880     if (!psInfo->usePropPage)
2881       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2882     else
2883     {
2884        psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2885        pByte += ((LPCPROPSHEETPAGEW)pByte)->dwSize;
2886     }
2887
2888     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2889                                psInfo, n, TRUE))
2890     {
2891         if (psInfo->usePropPage)
2892             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2893         n--;
2894         psInfo->nPages--;
2895     }
2896   }
2897
2898   return PROPSHEET_PropertySheet(psInfo, TRUE);
2899 }
2900
2901 static LPWSTR load_string( HINSTANCE instance, LPCWSTR str )
2902 {
2903     LPWSTR ret;
2904     UINT len;
2905
2906     if (IS_INTRESOURCE(str))
2907     {
2908         HRSRC hrsrc;
2909         HGLOBAL hmem;
2910         WCHAR *ptr;
2911         WORD i, id = LOWORD(str);
2912
2913         if (!(hrsrc = FindResourceW( instance, MAKEINTRESOURCEW((id >> 4) + 1), (LPWSTR)RT_STRING )))
2914             return NULL;
2915         if (!(hmem = LoadResource( instance, hrsrc ))) return NULL;
2916         if (!(ptr = LockResource( hmem ))) return NULL;
2917         for (i = id & 0x0f; i > 0; i--) ptr += *ptr + 1;
2918         len = *ptr;
2919         if (!len) return NULL;
2920         ret = Alloc( (len + 1) * sizeof(WCHAR) );
2921         if (ret)
2922         {
2923             memcpy( ret, ptr + 1, len * sizeof(WCHAR) );
2924             ret[len] = 0;
2925         }
2926     }
2927     else
2928     {
2929         int len = (strlenW(str) + 1) * sizeof(WCHAR);
2930         ret = Alloc( len );
2931         if (ret) memcpy( ret, str, len );
2932     }
2933     return ret;
2934 }
2935
2936
2937 /******************************************************************************
2938  *            CreatePropertySheetPage    (COMCTL32.@)
2939  *            CreatePropertySheetPageA   (COMCTL32.@)
2940  *
2941  * Creates a new property sheet page.
2942  *
2943  * RETURNS
2944  *     Success: Handle to new property sheet page.
2945  *     Failure: NULL.
2946  *
2947  * NOTES
2948  *     An application must use the PSM_ADDPAGE message to add the new page to
2949  *     an existing property sheet.
2950  */
2951 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
2952                           LPCPROPSHEETPAGEA lpPropSheetPage)
2953 {
2954   PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
2955
2956   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEA)));
2957
2958   ppsp->dwFlags &= ~ PSP_INTERNAL_UNICODE;
2959
2960     if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
2961     {
2962         if (HIWORD( ppsp->u.pszTemplate ))
2963         {
2964             int len = strlen(lpPropSheetPage->u.pszTemplate) + 1;
2965             char *template = Alloc( len );
2966
2967             ppsp->u.pszTemplate = (LPWSTR)strcpy( template, lpPropSheetPage->u.pszTemplate );
2968         }
2969     }
2970
2971     if (ppsp->dwFlags & PSP_USEICONID)
2972     {
2973         if (HIWORD( ppsp->u2.pszIcon ))
2974             PROPSHEET_AtoW(&ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon);
2975     }
2976
2977     if (ppsp->dwFlags & PSP_USETITLE)
2978     {
2979         if (HIWORD( ppsp->pszTitle ))
2980             PROPSHEET_AtoW( &ppsp->pszTitle, lpPropSheetPage->pszTitle );
2981         else
2982             ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
2983     }
2984     else
2985         ppsp->pszTitle = NULL;
2986
2987     if (ppsp->dwFlags & PSP_HIDEHEADER)
2988         ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);
2989
2990     if (ppsp->dwFlags & PSP_USEHEADERTITLE)
2991     {
2992         if (HIWORD( ppsp->pszHeaderTitle ))
2993             PROPSHEET_AtoW(&ppsp->pszHeaderTitle, lpPropSheetPage->pszHeaderTitle);
2994         else
2995             ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
2996     }
2997     else
2998         ppsp->pszHeaderTitle = NULL;
2999
3000     if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3001     {
3002         if (HIWORD( ppsp->pszHeaderSubTitle ))
3003             PROPSHEET_AtoW(&ppsp->pszHeaderSubTitle, lpPropSheetPage->pszHeaderSubTitle);
3004         else
3005             ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3006     }
3007     else
3008         ppsp->pszHeaderSubTitle = NULL;
3009
3010     return (HPROPSHEETPAGE)ppsp;
3011 }
3012
3013 /******************************************************************************
3014  *            CreatePropertySheetPageW   (COMCTL32.@)
3015  *
3016  * See CreatePropertySheetA.
3017  */
3018 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
3019 {
3020   PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
3021
3022   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEW)));
3023
3024   ppsp->dwFlags |= PSP_INTERNAL_UNICODE;
3025
3026     if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
3027     {
3028         if (HIWORD( ppsp->u.pszTemplate ))
3029         {
3030             int len = strlenW(lpPropSheetPage->u.pszTemplate) + 1;
3031             WCHAR *template = Alloc( len * sizeof (WCHAR) );
3032
3033             ppsp->u.pszTemplate = strcpyW( template, lpPropSheetPage->u.pszTemplate );
3034         }
3035     }
3036
3037     if ( ppsp->dwFlags & PSP_USEICONID )
3038     {
3039         if (HIWORD( ppsp->u2.pszIcon ))
3040         {
3041             int len = strlenW(lpPropSheetPage->u2.pszIcon) + 1;
3042             WCHAR *icon = Alloc( len * sizeof (WCHAR) );
3043
3044             ppsp->u2.pszIcon = strcpyW( icon, lpPropSheetPage->u2.pszIcon );
3045         }
3046     }
3047
3048     if (ppsp->dwFlags & PSP_USETITLE)
3049         ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
3050     else
3051         ppsp->pszTitle = NULL;
3052
3053     if (ppsp->dwFlags & PSP_HIDEHEADER)
3054         ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);
3055
3056     if (ppsp->dwFlags & PSP_USEHEADERTITLE)
3057         ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3058     else
3059         ppsp->pszHeaderTitle = NULL;
3060
3061     if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3062         ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3063     else
3064         ppsp->pszHeaderSubTitle = NULL;
3065
3066     return (HPROPSHEETPAGE)ppsp;
3067 }
3068
3069 /******************************************************************************
3070  *            DestroyPropertySheetPage   (COMCTL32.@)
3071  *
3072  * Destroys a property sheet page previously created with
3073  * CreatePropertySheetA() or CreatePropertySheetW() and frees the associated
3074  * memory.
3075  *
3076  * RETURNS
3077  *     Success: TRUE
3078  *     Failure: FALSE
3079  */
3080 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
3081 {
3082   PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
3083
3084   if (!psp)
3085      return FALSE;
3086
3087   if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u.pszTemplate ) )
3088      Free ((LPVOID)psp->u.pszTemplate);
3089
3090   if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) )
3091      Free ((LPVOID)psp->u2.pszIcon);
3092
3093   if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle ))
3094      Free ((LPVOID)psp->pszTitle);
3095
3096   Free(hPropPage);
3097
3098   return TRUE;
3099 }
3100
3101 /******************************************************************************
3102  *            PROPSHEET_IsDialogMessage
3103  */
3104 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
3105 {
3106    PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
3107
3108    TRACE("\n");
3109    if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
3110       return FALSE;
3111
3112    if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
3113    {
3114       int new_page = 0;
3115       INT dlgCode = SendMessageW(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
3116
3117       if (!(dlgCode & DLGC_WANTMESSAGE))
3118       {
3119          switch (lpMsg->wParam)
3120          {
3121             case VK_TAB:
3122                if (GetKeyState(VK_SHIFT) & 0x8000)
3123                    new_page = -1;
3124                 else
3125                    new_page = 1;
3126                break;
3127
3128             case VK_NEXT:   new_page = 1;  break;
3129             case VK_PRIOR:  new_page = -1; break;
3130          }
3131       }
3132
3133       if (new_page)
3134       {
3135          if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
3136          {
3137             new_page += psInfo->active_page;
3138
3139             if (new_page < 0)
3140                new_page = psInfo->nPages - 1;
3141             else if (new_page >= psInfo->nPages)
3142                new_page = 0;
3143
3144             PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
3145          }
3146
3147          return TRUE;
3148       }
3149    }
3150
3151    return IsDialogMessageW(hwnd, lpMsg);
3152 }
3153
3154 /******************************************************************************
3155  *            PROPSHEET_DoCommand
3156  */
3157 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID)
3158 {
3159
3160     switch (wID) {
3161
3162     case IDOK:
3163     case IDC_APPLY_BUTTON:
3164         {
3165             HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
3166
3167             if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
3168                 break;
3169
3170             if (wID == IDOK)
3171                 {
3172                     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3173                                                                       PropSheetInfoStr);
3174
3175                     /* don't overwrite ID_PSRESTARTWINDOWS or ID_PSREBOOTSYSTEM */
3176                     if (psInfo->result == 0)
3177                         psInfo->result = IDOK;
3178
3179                     if (psInfo->isModeless)
3180                         psInfo->activeValid = FALSE;
3181                     else
3182                         psInfo->ended = TRUE;
3183                 }
3184             else
3185                 EnableWindow(hwndApplyBtn, FALSE);
3186
3187             break;
3188         }
3189
3190     case IDC_BACK_BUTTON:
3191         PROPSHEET_Back(hwnd);
3192         break;
3193
3194     case IDC_NEXT_BUTTON:
3195         PROPSHEET_Next(hwnd);
3196         break;
3197
3198     case IDC_FINISH_BUTTON:
3199         PROPSHEET_Finish(hwnd);
3200         break;
3201
3202     case IDCANCEL:
3203         PROPSHEET_Cancel(hwnd, 0);
3204         break;
3205
3206     case IDHELP:
3207         PROPSHEET_Help(hwnd);
3208         break;
3209
3210     default:
3211         return FALSE;
3212     }
3213
3214     return TRUE;
3215 }
3216
3217 /******************************************************************************
3218  *            PROPSHEET_Paint
3219  */
3220 static LRESULT PROPSHEET_Paint(HWND hwnd, HDC hdcParam)
3221 {
3222     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
3223     PAINTSTRUCT ps;
3224     HDC hdc, hdcSrc;
3225     BITMAP bm;
3226     HBITMAP hbmp;
3227     HPALETTE hOldPal = 0;
3228     int offsety = 0;
3229     HBRUSH hbr;
3230     RECT r, rzone;
3231     LPCPROPSHEETPAGEW ppshpage;
3232     WCHAR szBuffer[256];
3233     int nLength;
3234
3235     if (psInfo->active_page < 0) return 1;
3236     hdc = hdcParam ? hdcParam : BeginPaint(hwnd, &ps);
3237     if (!hdc) return 1;
3238
3239     hdcSrc = CreateCompatibleDC(0);
3240     ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[psInfo->active_page].hpage;
3241
3242     if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK) 
3243         hOldPal = SelectPalette(hdc, psInfo->ppshheader.hplWatermark, FALSE);
3244
3245     if ( (!(ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3246          (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
3247          (psInfo->ppshheader.dwFlags & PSH_HEADER) ) 
3248     {
3249         HWND hwndLineHeader = GetDlgItem(hwnd, IDC_SUNKEN_LINEHEADER);
3250         HFONT hOldFont;
3251         COLORREF clrOld = 0;
3252         int oldBkMode = 0;
3253
3254         hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u5.hbmHeader);
3255         hOldFont = SelectObject(hdc, psInfo->hFontBold);
3256
3257         GetClientRect(hwndLineHeader, &r);
3258         MapWindowPoints(hwndLineHeader, hwnd, (LPPOINT) &r, 2);
3259         SetRect(&rzone, 0, 0, r.right + 1, r.top - 1);
3260
3261         GetObjectW(psInfo->ppshheader.u5.hbmHeader, sizeof(BITMAP), (LPVOID)&bm);               
3262
3263         if (psInfo->ppshheader.dwFlags & PSH_WIZARD97_OLD)
3264         {
3265             /* Fill the unoccupied part of the header with color of the
3266              * left-top pixel, but do it only when needed.
3267              */
3268             if (bm.bmWidth < r.right || bm.bmHeight < r.bottom)
3269             {
3270                 hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
3271                 CopyRect(&r, &rzone);
3272                 if (bm.bmWidth < r.right)
3273                 {
3274                     r.left = bm.bmWidth;
3275                     FillRect(hdc, &r, hbr);
3276                 }
3277                 if (bm.bmHeight < r.bottom)
3278                 {
3279                     r.left = 0;
3280                     r.top = bm.bmHeight;
3281                     FillRect(hdc, &r, hbr);
3282                 }
3283                 DeleteObject(hbr);
3284             }
3285
3286             /* Draw the header itself. */
3287             BitBlt(hdc, 0, 0,
3288                    bm.bmWidth, min(bm.bmHeight, rzone.bottom),
3289                    hdcSrc, 0, 0, SRCCOPY);
3290         }
3291         else
3292         {
3293             int margin;
3294             hbr = GetSysColorBrush(COLOR_WINDOW);
3295             FillRect(hdc, &rzone, hbr);
3296
3297             /* Draw the header bitmap. It's always centered like a
3298              * common 49 x 49 bitmap. */
3299             margin = (rzone.bottom - 49) / 2;
3300             BitBlt(hdc, rzone.right - 49 - margin, margin,
3301                    min(bm.bmWidth, 49), min(bm.bmHeight, 49),
3302                    hdcSrc, 0, 0, SRCCOPY);
3303
3304             /* NOTE: Native COMCTL32 draws a white stripe over the bitmap
3305              * if its height is smaller than 49 pixels. Because the reason
3306              * for this bug is unknown the current code doesn't try to
3307              * replicate it. */
3308         }
3309
3310         clrOld = SetTextColor (hdc, 0x00000000);
3311         oldBkMode = SetBkMode (hdc, TRANSPARENT); 
3312
3313         if (ppshpage->dwFlags & PSP_USEHEADERTITLE) {
3314             SetRect(&r, 20, 10, 0, 0);
3315             if (HIWORD(ppshpage->pszHeaderTitle))
3316                 DrawTextW(hdc, ppshpage->pszHeaderTitle, -1, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3317             else
3318             {
3319                 nLength = LoadStringW(ppshpage->hInstance, (UINT_PTR)ppshpage->pszHeaderTitle,
3320                                       szBuffer, 256);
3321                 if (nLength != 0)
3322                 {
3323                     DrawTextW(hdc, szBuffer, nLength, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3324                 }
3325             }
3326         }
3327
3328         if (ppshpage->dwFlags & PSP_USEHEADERSUBTITLE) {
3329             SelectObject(hdc, psInfo->hFont);
3330             SetRect(&r, 40, 25, rzone.right - 69, rzone.bottom);
3331             if (HIWORD(ppshpage->pszHeaderTitle))
3332                 DrawTextW(hdc, ppshpage->pszHeaderSubTitle, -1, &r, DT_LEFT | DT_WORDBREAK);
3333             else
3334             {
3335                 nLength = LoadStringW(ppshpage->hInstance, (UINT_PTR)ppshpage->pszHeaderSubTitle,
3336                                       szBuffer, 256);
3337                 if (nLength != 0)
3338                 {
3339                     DrawTextW(hdc, szBuffer, nLength, &r, DT_LEFT | DT_WORDBREAK);
3340                 }
3341             }
3342         }
3343
3344         offsety = rzone.bottom + 2;
3345
3346         SetTextColor(hdc, clrOld);
3347         SetBkMode(hdc, oldBkMode);
3348         SelectObject(hdc, hOldFont);
3349         SelectObject(hdcSrc, hbmp);
3350     }
3351
3352     if ( (ppshpage->dwFlags & PSP_HIDEHEADER) &&
3353          (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
3354          (psInfo->ppshheader.dwFlags & PSH_WATERMARK) ) 
3355     {
3356         HWND hwndLine = GetDlgItem(hwnd, IDC_SUNKEN_LINE);          
3357
3358         GetClientRect(hwndLine, &r);
3359         MapWindowPoints(hwndLine, hwnd, (LPPOINT) &r, 2);
3360
3361         rzone.left = 0;
3362         rzone.top = 0;
3363         rzone.right = r.right;
3364         rzone.bottom = r.top - 1;
3365
3366         hbr = GetSysColorBrush(COLOR_WINDOW);
3367         FillRect(hdc, &rzone, hbr);
3368
3369         GetObjectW(psInfo->ppshheader.u4.hbmWatermark, sizeof(BITMAP), (LPVOID)&bm);
3370         hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u4.hbmWatermark);
3371
3372         /* The watermark is truncated to a width of 164 pixels */
3373         r.right = min(r.right, 164);
3374         BitBlt(hdc, 0, offsety, min(bm.bmWidth, r.right),
3375                min(bm.bmHeight, r.bottom), hdcSrc, 0, 0, SRCCOPY);
3376
3377         /* If the bitmap is not big enough, fill the remaining area
3378            with the color of pixel (0,0) of bitmap - see MSDN */
3379         if (r.top > bm.bmHeight) {
3380             r.bottom = r.top - 1;
3381             r.top = bm.bmHeight;
3382             r.left = 0;
3383             r.right = bm.bmWidth;
3384             hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
3385             FillRect(hdc, &r, hbr);
3386             DeleteObject(hbr);
3387         }
3388
3389         SelectObject(hdcSrc, hbmp);         
3390     }
3391
3392     if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK) 
3393         SelectPalette(hdc, hOldPal, FALSE);
3394
3395     DeleteDC(hdcSrc);
3396
3397     if (!hdcParam) EndPaint(hwnd, &ps);
3398
3399     return 0;
3400 }
3401
3402 /******************************************************************************
3403  *            PROPSHEET_DialogProc
3404  */
3405 static INT_PTR CALLBACK
3406 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3407 {
3408   TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n",
3409         hwnd, uMsg, wParam, lParam);
3410
3411   switch (uMsg)
3412   {
3413     case WM_INITDIALOG:
3414     {
3415       PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
3416       WCHAR* strCaption = (WCHAR*)Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
3417       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
3418       LPCPROPSHEETPAGEW ppshpage;
3419       int idx;
3420       LOGFONTW logFont;
3421
3422       /* Using PropSheetInfoStr to store extra data doesn't match the native
3423        * common control: native uses TCM_[GS]ETITEM
3424        */
3425       SetPropW(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
3426
3427       /*
3428        * psInfo->hwnd is not being used by WINE code - it exists
3429        * for compatibility with "real" Windoze. The same about
3430        * SetWindowLongPtr - WINE is only using the PropSheetInfoStr
3431        * property.
3432        */
3433       psInfo->hwnd = hwnd;
3434       SetWindowLongPtrW(hwnd, DWLP_USER, (DWORD_PTR)psInfo);
3435
3436       /* set up the Next and Back buttons by default */
3437       PROPSHEET_SetWizButtons(hwnd, PSWIZB_BACK|PSWIZB_NEXT);
3438
3439       /* Set up fonts */
3440       SystemParametersInfoW (SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
3441       psInfo->hFont = CreateFontIndirectW (&logFont);
3442       logFont.lfWeight = FW_BOLD;
3443       psInfo->hFontBold = CreateFontIndirectW (&logFont);
3444       
3445       /*
3446        * Small icon in the title bar.
3447        */
3448       if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
3449           (psInfo->ppshheader.dwFlags & PSH_USEHICON))
3450       {
3451         HICON hIcon;
3452         int icon_cx = GetSystemMetrics(SM_CXSMICON);
3453         int icon_cy = GetSystemMetrics(SM_CYSMICON);
3454
3455         if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
3456           hIcon = LoadImageW(psInfo->ppshheader.hInstance,
3457                              psInfo->ppshheader.u.pszIcon,
3458                              IMAGE_ICON,
3459                              icon_cx, icon_cy,
3460                              LR_DEFAULTCOLOR);
3461         else
3462           hIcon = psInfo->ppshheader.u.hIcon;
3463
3464         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)hIcon);
3465       }
3466
3467       if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
3468         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)psInfo->ppshheader.u.hIcon);
3469
3470       psInfo->strPropertiesFor = strCaption;
3471
3472       GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
3473
3474       PROPSHEET_CreateTabControl(hwnd, psInfo);
3475
3476       PROPSHEET_LoadWizardBitmaps(psInfo);
3477
3478       if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3479       {
3480         ShowWindow(hwndTabCtrl, SW_HIDE);
3481         PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
3482         PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
3483       }
3484       else
3485       {
3486         if (PROPSHEET_SizeMismatch(hwnd, psInfo))
3487         {
3488           PROPSHEET_AdjustSize(hwnd, psInfo);
3489           PROPSHEET_AdjustButtons(hwnd, psInfo);
3490         }
3491       }
3492
3493       if (!HIWORD(psInfo->ppshheader.pszCaption) &&
3494               psInfo->ppshheader.hInstance)
3495       {
3496          WCHAR szText[256];
3497
3498          if (LoadStringW(psInfo->ppshheader.hInstance,
3499                          (UINT_PTR)psInfo->ppshheader.pszCaption, szText, 255))
3500             PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
3501       }
3502       else
3503       {
3504          PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
3505                          psInfo->ppshheader.pszCaption);
3506       }
3507
3508
3509       if (psInfo->useCallback)
3510              (*(psInfo->ppshheader.pfnCallback))(hwnd,
3511                                               PSCB_INITIALIZED, (LPARAM)0);
3512
3513       idx = psInfo->active_page;
3514       ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[idx].hpage;
3515       psInfo->active_page = -1;
3516
3517       PROPSHEET_SetCurSel(hwnd, idx, 1, psInfo->proppage[idx].hpage);
3518
3519       /* doing TCM_SETCURSEL seems to be needed even in case of PSH_WIZARD,
3520        * as some programs call TCM_GETCURSEL to get the current selection
3521        * from which to switch to the next page */
3522       SendMessageW(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
3523
3524       return TRUE;
3525     }
3526
3527     case WM_PRINTCLIENT:
3528     case WM_PAINT:
3529       PROPSHEET_Paint(hwnd, (HDC)wParam);
3530       return TRUE;
3531
3532     case WM_DESTROY:
3533       PROPSHEET_CleanUp(hwnd);
3534       return TRUE;
3535
3536     case WM_CLOSE:
3537       PROPSHEET_Cancel(hwnd, 1);
3538       return FALSE; /* let DefDlgProc post us WM_COMMAND/IDCANCEL */
3539
3540     case WM_COMMAND:
3541       if (!PROPSHEET_DoCommand(hwnd, LOWORD(wParam)))
3542       {
3543           PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
3544
3545           if (!psInfo)
3546               return FALSE;
3547
3548           /* No default handler, forward notification to active page */
3549           if (psInfo->activeValid && psInfo->active_page != -1)
3550           {
3551              HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
3552              SendMessageW(hwndPage, WM_COMMAND, wParam, lParam);
3553           }
3554       }
3555       return TRUE;
3556
3557     case WM_NOTIFY:
3558     {
3559       NMHDR* pnmh = (LPNMHDR) lParam;
3560
3561       if (pnmh->code == TCN_SELCHANGE)
3562       {
3563         int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
3564         PROPSHEET_SetCurSel(hwnd, index, 1, 0);
3565       }
3566
3567       if(pnmh->code == TCN_SELCHANGING)
3568       {
3569         BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
3570         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, !bRet);
3571         return TRUE;
3572       }
3573
3574       return FALSE;
3575     }
3576   
3577     case WM_SYSCOLORCHANGE:
3578       COMCTL32_RefreshSysColors();
3579       return FALSE;
3580
3581     case PSM_GETCURRENTPAGEHWND:
3582     {
3583       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3584                                                         PropSheetInfoStr);
3585       HWND hwndPage = 0;
3586
3587       if (!psInfo)
3588         return FALSE;
3589
3590       if (psInfo->activeValid && psInfo->active_page != -1)
3591         hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
3592
3593       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndPage);
3594
3595       return TRUE;
3596     }
3597
3598     case PSM_CHANGED:
3599       PROPSHEET_Changed(hwnd, (HWND)wParam);
3600       return TRUE;
3601
3602     case PSM_UNCHANGED:
3603       PROPSHEET_UnChanged(hwnd, (HWND)wParam);
3604       return TRUE;
3605
3606     case PSM_GETTABCONTROL:
3607     {
3608       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
3609
3610       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndTabCtrl);
3611
3612       return TRUE;
3613     }
3614
3615     case PSM_SETCURSEL:
3616     {
3617       BOOL msgResult;
3618
3619       msgResult = PROPSHEET_CanSetCurSel(hwnd);
3620       if(msgResult != FALSE)
3621       {
3622         msgResult = PROPSHEET_SetCurSel(hwnd,
3623                                        (int)wParam,
3624                                        1,
3625                                        (HPROPSHEETPAGE)lParam);
3626       }
3627
3628       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3629
3630       return TRUE;
3631     }
3632
3633     case PSM_CANCELTOCLOSE:
3634     {
3635       WCHAR buf[MAX_BUTTONTEXT_LENGTH];
3636       HWND hwndOK = GetDlgItem(hwnd, IDOK);
3637       HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
3638
3639       EnableWindow(hwndCancel, FALSE);
3640       if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)))
3641          SetWindowTextW(hwndOK, buf);
3642
3643       return FALSE;
3644     }
3645
3646     case PSM_RESTARTWINDOWS:
3647     {
3648       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3649                                                         PropSheetInfoStr);
3650
3651       if (!psInfo)
3652         return FALSE;
3653
3654       /* reboot system takes precedence over restart windows */
3655       if (psInfo->result != ID_PSREBOOTSYSTEM)
3656           psInfo->result = ID_PSRESTARTWINDOWS;
3657
3658       return TRUE;
3659     }
3660
3661     case PSM_REBOOTSYSTEM:
3662     {
3663       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3664                                                         PropSheetInfoStr);
3665
3666       if (!psInfo)
3667         return FALSE;
3668
3669       psInfo->result = ID_PSREBOOTSYSTEM;
3670
3671       return TRUE;
3672     }
3673
3674     case PSM_SETTITLEA:
3675       PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
3676       return TRUE;
3677
3678     case PSM_SETTITLEW:
3679       PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
3680       return TRUE;
3681
3682     case PSM_APPLY:
3683     {
3684       BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
3685
3686       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3687
3688       return TRUE;
3689     }
3690
3691     case PSM_QUERYSIBLINGS:
3692     {
3693       LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
3694
3695       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3696
3697       return TRUE;
3698     }
3699
3700     case PSM_ADDPAGE:
3701     {
3702       /*
3703        * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have
3704        *       a return value. This is not true. PSM_ADDPAGE returns TRUE
3705        *       on success or FALSE otherwise, as specified on MSDN Online.
3706        *       Also see the MFC code for
3707        *       CPropertySheet::AddPage(CPropertyPage* pPage).
3708        */
3709
3710       BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
3711
3712       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3713
3714       return TRUE;
3715     }
3716
3717     case PSM_REMOVEPAGE:
3718       PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
3719       return TRUE;
3720
3721     case PSM_ISDIALOGMESSAGE:
3722     {
3723        BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
3724        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3725        return TRUE;
3726     }
3727
3728     case PSM_PRESSBUTTON:
3729       PROPSHEET_PressButton(hwnd, (int)wParam);
3730       return TRUE;
3731
3732     case PSM_SETFINISHTEXTA:
3733       PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
3734       return TRUE;
3735
3736     case PSM_SETWIZBUTTONS:
3737       PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
3738       return TRUE;
3739
3740     case PSM_SETCURSELID:
3741         PROPSHEET_SetCurSelId(hwnd, (int)lParam);
3742         return TRUE;
3743
3744     case PSM_SETFINISHTEXTW:
3745         PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
3746         return FALSE;
3747
3748     case PSM_INSERTPAGE:
3749     {
3750         BOOL msgResult = PROPSHEET_InsertPage(hwnd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam);
3751         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3752         return TRUE;
3753     }
3754
3755     case PSM_SETHEADERTITLEW:
3756         PROPSHEET_SetHeaderTitleW(hwnd, (int)wParam, (LPCWSTR)lParam);
3757         return TRUE;
3758
3759     case PSM_SETHEADERTITLEA:
3760         PROPSHEET_SetHeaderTitleA(hwnd, (int)wParam, (LPCSTR)lParam);
3761         return TRUE;
3762
3763     case PSM_SETHEADERSUBTITLEW:
3764         PROPSHEET_SetHeaderSubTitleW(hwnd, (int)wParam, (LPCWSTR)lParam);
3765         return TRUE;
3766
3767     case PSM_SETHEADERSUBTITLEA:
3768         PROPSHEET_SetHeaderSubTitleA(hwnd, (int)wParam, (LPCSTR)lParam);
3769         return TRUE;
3770
3771     case PSM_HWNDTOINDEX:
3772     {
3773         LRESULT msgResult = PROPSHEET_HwndToIndex(hwnd, (HWND)wParam);
3774         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3775         return TRUE;
3776     }
3777
3778     case PSM_INDEXTOHWND:
3779     {
3780         LRESULT msgResult = PROPSHEET_IndexToHwnd(hwnd, (int)wParam);
3781         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3782         return TRUE;
3783     }
3784
3785     case PSM_PAGETOINDEX:
3786     {
3787         LRESULT msgResult = PROPSHEET_PageToIndex(hwnd, (HPROPSHEETPAGE)wParam);
3788         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3789         return TRUE;
3790     }
3791
3792     case PSM_INDEXTOPAGE:
3793     {
3794         LRESULT msgResult = PROPSHEET_IndexToPage(hwnd, (int)wParam);
3795         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3796         return TRUE;
3797     }
3798
3799     case PSM_IDTOINDEX:
3800     {
3801         LRESULT msgResult = PROPSHEET_IdToIndex(hwnd, (int)lParam);
3802         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3803         return TRUE;
3804     }
3805
3806     case PSM_INDEXTOID:
3807     {
3808         LRESULT msgResult = PROPSHEET_IndexToId(hwnd, (int)wParam);
3809         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3810         return TRUE;
3811     }
3812
3813     case PSM_GETRESULT:
3814     {
3815         LRESULT msgResult = PROPSHEET_GetResult(hwnd);
3816         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3817         return TRUE;
3818     }
3819
3820     case PSM_RECALCPAGESIZES:
3821     {
3822         LRESULT msgResult = PROPSHEET_RecalcPageSizes(hwnd);
3823         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3824         return TRUE;
3825     }
3826
3827     default:
3828       return FALSE;
3829   }
3830 }