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