Moved the audio driver configuration to HKCU\Software\Wine\Drivers and
[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   InvalidateRgn(hwndDlg, NULL, TRUE);
1627   UpdateWindow(hwndDlg);
1628
1629   return TRUE;
1630 }
1631
1632 /******************************************************************************
1633  *            PROPSHEET_Back
1634  */
1635 static BOOL PROPSHEET_Back(HWND hwndDlg)
1636 {
1637   PSHNOTIFY psn;
1638   HWND hwndPage;
1639   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1640                                                     PropSheetInfoStr);
1641   LRESULT result;
1642   int idx;
1643
1644   TRACE("active_page %d\n", psInfo->active_page);
1645   if (psInfo->active_page < 0)
1646      return FALSE;
1647
1648   psn.hdr.code     = PSN_WIZBACK;
1649   psn.hdr.hwndFrom = hwndDlg;
1650   psn.hdr.idFrom   = 0;
1651   psn.lParam       = 0;
1652
1653   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1654
1655   result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1656   if (result == -1)
1657     return FALSE;
1658   else if (result == 0)
1659      idx = psInfo->active_page - 1;
1660   else
1661      idx = PROPSHEET_FindPageByResId(psInfo, result);
1662
1663   if (idx >= 0 && idx < psInfo->nPages)
1664   {
1665      if (PROPSHEET_CanSetCurSel(hwndDlg))
1666         PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1667   }
1668   return TRUE;
1669 }
1670
1671 /******************************************************************************
1672  *            PROPSHEET_Next
1673  */
1674 static BOOL PROPSHEET_Next(HWND hwndDlg)
1675 {
1676   PSHNOTIFY psn;
1677   HWND hwndPage;
1678   LRESULT msgResult = 0;
1679   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1680                                                     PropSheetInfoStr);
1681   int idx;
1682
1683   TRACE("active_page %d\n", psInfo->active_page);
1684   if (psInfo->active_page < 0)
1685      return FALSE;
1686
1687   psn.hdr.code     = PSN_WIZNEXT;
1688   psn.hdr.hwndFrom = hwndDlg;
1689   psn.hdr.idFrom   = 0;
1690   psn.lParam       = 0;
1691
1692   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1693
1694   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1695   if (msgResult == -1)
1696     return FALSE;
1697   else if (msgResult == 0)
1698      idx = psInfo->active_page + 1;
1699   else
1700      idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1701
1702   if (idx < psInfo->nPages )
1703   {
1704      if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1705         PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1706   }
1707
1708   return TRUE;
1709 }
1710
1711 /******************************************************************************
1712  *            PROPSHEET_Finish
1713  */
1714 static BOOL PROPSHEET_Finish(HWND hwndDlg)
1715 {
1716   PSHNOTIFY psn;
1717   HWND hwndPage;
1718   LRESULT msgResult = 0;
1719   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1720                                                     PropSheetInfoStr);
1721
1722   TRACE("active_page %d\n", psInfo->active_page);
1723   if (psInfo->active_page < 0)
1724      return FALSE;
1725
1726   psn.hdr.code     = PSN_WIZFINISH;
1727   psn.hdr.hwndFrom = hwndDlg;
1728   psn.hdr.idFrom   = 0;
1729   psn.lParam       = 0;
1730
1731   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1732
1733   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1734
1735   TRACE("msg result %ld\n", msgResult);
1736
1737   if (msgResult != 0)
1738     return FALSE;
1739
1740   if (psInfo->isModeless)
1741     psInfo->activeValid = FALSE;
1742   else
1743     psInfo->ended = TRUE;
1744
1745   return TRUE;
1746 }
1747
1748 /******************************************************************************
1749  *            PROPSHEET_Apply
1750  */
1751 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1752 {
1753   int i;
1754   HWND hwndPage;
1755   PSHNOTIFY psn;
1756   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1757                                                     PropSheetInfoStr);
1758
1759   TRACE("active_page %d\n", psInfo->active_page);
1760   if (psInfo->active_page < 0)
1761      return FALSE;
1762
1763   psn.hdr.hwndFrom = hwndDlg;
1764   psn.hdr.idFrom   = 0;
1765   psn.lParam       = 0;
1766
1767
1768   /*
1769    * Send PSN_KILLACTIVE to the current page.
1770    */
1771   psn.hdr.code = PSN_KILLACTIVE;
1772
1773   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1774
1775   if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1776     return FALSE;
1777
1778   /*
1779    * Send PSN_APPLY to all pages.
1780    */
1781   psn.hdr.code = PSN_APPLY;
1782   psn.lParam   = lParam;
1783
1784   for (i = 0; i < psInfo->nPages; i++)
1785   {
1786     hwndPage = psInfo->proppage[i].hwndPage;
1787     if (hwndPage)
1788     {
1789        switch (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1790        {
1791        case PSNRET_INVALID:
1792            PROPSHEET_ShowPage(hwndDlg, i, psInfo);
1793            /* fall through */
1794        case PSNRET_INVALID_NOCHANGEPAGE:
1795            return FALSE;
1796        }
1797     }
1798   }
1799
1800   if(lParam)
1801   {
1802      psInfo->activeValid = FALSE;
1803   }
1804   else if(psInfo->active_page >= 0)
1805   {
1806      psn.hdr.code = PSN_SETACTIVE;
1807      psn.lParam   = 0;
1808      hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1809      SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1810   }
1811
1812   return TRUE;
1813 }
1814
1815 /******************************************************************************
1816  *            PROPSHEET_Cancel
1817  */
1818 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1819 {
1820   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1821                                                     PropSheetInfoStr);
1822   HWND hwndPage;
1823   PSHNOTIFY psn;
1824   int i;
1825
1826   TRACE("active_page %d\n", psInfo->active_page);
1827   if (psInfo->active_page < 0)
1828      return;
1829
1830   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1831   psn.hdr.code     = PSN_QUERYCANCEL;
1832   psn.hdr.hwndFrom = hwndDlg;
1833   psn.hdr.idFrom   = 0;
1834   psn.lParam       = 0;
1835
1836   if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1837     return;
1838
1839   psn.hdr.code = PSN_RESET;
1840   psn.lParam   = lParam;
1841
1842   for (i = 0; i < psInfo->nPages; i++)
1843   {
1844     hwndPage = psInfo->proppage[i].hwndPage;
1845
1846     if (hwndPage)
1847        SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1848   }
1849
1850   if (psInfo->isModeless)
1851   {
1852      /* makes PSM_GETCURRENTPAGEHWND return NULL */
1853      psInfo->activeValid = FALSE;
1854   }
1855   else
1856     psInfo->ended = TRUE;
1857 }
1858
1859 /******************************************************************************
1860  *            PROPSHEET_Help
1861  */
1862 static void PROPSHEET_Help(HWND hwndDlg)
1863 {
1864   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1865                                                     PropSheetInfoStr);
1866   HWND hwndPage;
1867   PSHNOTIFY psn;
1868
1869   TRACE("active_page %d\n", psInfo->active_page);
1870   if (psInfo->active_page < 0)
1871      return;
1872
1873   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1874   psn.hdr.code     = PSN_HELP;
1875   psn.hdr.hwndFrom = hwndDlg;
1876   psn.hdr.idFrom   = 0;
1877   psn.lParam       = 0;
1878
1879   SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1880 }
1881
1882 /******************************************************************************
1883  *            PROPSHEET_Changed
1884  */
1885 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1886 {
1887   int i;
1888   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1889                                                     PropSheetInfoStr);
1890
1891   TRACE("\n");
1892   if (!psInfo) return;
1893   /*
1894    * Set the dirty flag of this page.
1895    */
1896   for (i = 0; i < psInfo->nPages; i++)
1897   {
1898     if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1899       psInfo->proppage[i].isDirty = TRUE;
1900   }
1901
1902   /*
1903    * Enable the Apply button.
1904    */
1905   if (psInfo->hasApply)
1906   {
1907     HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1908
1909     EnableWindow(hwndApplyBtn, TRUE);
1910   }
1911 }
1912
1913 /******************************************************************************
1914  *            PROPSHEET_UnChanged
1915  */
1916 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1917 {
1918   int i;
1919   BOOL noPageDirty = TRUE;
1920   HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1921   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1922                                                     PropSheetInfoStr);
1923
1924   TRACE("\n");
1925   if ( !psInfo ) return;
1926   for (i = 0; i < psInfo->nPages; i++)
1927   {
1928     /* set the specified page as clean */
1929     if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1930       psInfo->proppage[i].isDirty = FALSE;
1931
1932     /* look to see if there's any dirty pages */
1933     if (psInfo->proppage[i].isDirty)
1934       noPageDirty = FALSE;
1935   }
1936
1937   /*
1938    * Disable Apply button.
1939    */
1940   if (noPageDirty)
1941     EnableWindow(hwndApplyBtn, FALSE);
1942 }
1943
1944 /******************************************************************************
1945  *            PROPSHEET_PressButton
1946  */
1947 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1948 {
1949   TRACE("buttonID %d\n", buttonID);
1950   switch (buttonID)
1951   {
1952     case PSBTN_APPLYNOW:
1953       PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1954       break;
1955     case PSBTN_BACK:
1956       PROPSHEET_Back(hwndDlg);
1957       break;
1958     case PSBTN_CANCEL:
1959       PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1960       break;
1961     case PSBTN_FINISH:
1962       PROPSHEET_Finish(hwndDlg);
1963       break;
1964     case PSBTN_HELP:
1965       PROPSHEET_DoCommand(hwndDlg, IDHELP);
1966       break;
1967     case PSBTN_NEXT:
1968       PROPSHEET_Next(hwndDlg);
1969       break;
1970     case PSBTN_OK:
1971       PROPSHEET_DoCommand(hwndDlg, IDOK);
1972       break;
1973     default:
1974       FIXME("Invalid button index %d\n", buttonID);
1975   }
1976 }
1977
1978
1979 /*************************************************************************
1980  * BOOL PROPSHEET_CanSetCurSel [Internal]
1981  *
1982  * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1983  *
1984  * PARAMS
1985  *     hwndDlg        [I] handle to a Dialog hWnd
1986  *
1987  * RETURNS
1988  *     TRUE if Current Selection can change
1989  *
1990  * NOTES
1991  */
1992 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1993 {
1994   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1995                                                     PropSheetInfoStr);
1996   HWND hwndPage;
1997   PSHNOTIFY psn;
1998   BOOL res = FALSE;
1999
2000   TRACE("active_page %d\n", psInfo->active_page);
2001   if (!psInfo)
2002   {
2003      res = FALSE;
2004      goto end;
2005   }
2006
2007   if (psInfo->active_page < 0)
2008   {
2009      res = TRUE;
2010      goto end;
2011   }
2012
2013   /*
2014    * Notify the current page.
2015    */
2016   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
2017   psn.hdr.code     = PSN_KILLACTIVE;
2018   psn.hdr.hwndFrom = hwndDlg;
2019   psn.hdr.idFrom   = 0;
2020   psn.lParam       = 0;
2021
2022   res = !SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2023
2024 end:
2025   TRACE("<-- %d\n", res);
2026   return res;
2027 }
2028
2029 /******************************************************************************
2030  *            PROPSHEET_SetCurSel
2031  */
2032 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
2033                                 int index,
2034                                 int skipdir,
2035                                 HPROPSHEETPAGE hpage
2036                                 )
2037 {
2038   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2039   HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
2040   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2041
2042   TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
2043   /* hpage takes precedence over index */
2044   if (hpage != NULL)
2045     index = PROPSHEET_GetPageIndex(hpage, psInfo);
2046
2047   if (index < 0 || index >= psInfo->nPages)
2048   {
2049     TRACE("Could not find page to select!\n");
2050     return FALSE;
2051   }
2052
2053   while (1) {
2054     int result;
2055     PSHNOTIFY psn;
2056     RECT rc;
2057     LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2058
2059     if (hwndTabControl)
2060         SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);
2061
2062     psn.hdr.code     = PSN_SETACTIVE;
2063     psn.hdr.hwndFrom = hwndDlg;
2064     psn.hdr.idFrom   = 0;
2065     psn.lParam       = 0;
2066
2067     if (!psInfo->proppage[index].hwndPage) {
2068       PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
2069     }
2070
2071     /* Resize the property sheet page to the fit in the Tab control
2072      * (for regular property sheets) or to fit in the client area (for
2073      * wizards).
2074      * NOTE: The resizing happens every time the page is selected and
2075      * not only when it's created (some applications depend on it). */
2076     PROPSHEET_GetPageRect(psInfo, hwndDlg, &rc, ppshpage);
2077     TRACE("setting page %p, rc (%ld,%ld)-(%ld,%ld) w=%ld, h=%ld\n",
2078           psInfo->proppage[index].hwndPage, rc.left, rc.top, rc.right, rc.bottom,
2079           rc.right - rc.left, rc.bottom - rc.top);
2080     SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP,
2081                  rc.left, rc.top,
2082                  rc.right - rc.left, rc.bottom - rc.top, 0);
2083
2084     result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2085     if (!result)
2086       break;
2087     if (result == -1) {
2088       index+=skipdir;
2089       if (index < 0) {
2090         index = 0;
2091         WARN("Tried to skip before first property sheet page!\n");
2092         break;
2093       }
2094       if (index >= psInfo->nPages) {
2095         WARN("Tried to skip after last property sheet page!\n");
2096         index = psInfo->nPages-1;
2097         break;
2098       }
2099     }
2100     else if (result != 0)
2101     {
2102       int old_index = index;
2103       index = PROPSHEET_FindPageByResId(psInfo, result);
2104       if(index >= psInfo->nPages) {
2105         index = old_index;
2106         WARN("Tried to skip to nonexistant page by res id\n");
2107         break;
2108       }
2109       continue;
2110     }
2111   }
2112   /*
2113    * Display the new page.
2114    */
2115   PROPSHEET_ShowPage(hwndDlg, index, psInfo);
2116
2117   if (psInfo->proppage[index].hasHelp)
2118     EnableWindow(hwndHelp, TRUE);
2119   else
2120     EnableWindow(hwndHelp, FALSE);
2121
2122   return TRUE;
2123 }
2124
2125 /******************************************************************************
2126  *            PROPSHEET_SetCurSelId
2127  *
2128  * Selects the page, specified by resource id.
2129  */
2130 static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id)
2131 {
2132       int idx;
2133       PropSheetInfo* psInfo =
2134           (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2135
2136       idx = PROPSHEET_FindPageByResId(psInfo, id);
2137       if (idx < psInfo->nPages )
2138       {
2139           if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
2140               PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
2141       }
2142 }
2143
2144 /******************************************************************************
2145  *            PROPSHEET_SetTitleA
2146  */
2147 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
2148 {
2149   if(HIWORD(lpszText))
2150   {
2151      WCHAR szTitle[256];
2152      MultiByteToWideChar(CP_ACP, 0, lpszText, -1,
2153                              szTitle, sizeof(szTitle));
2154      PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
2155   }
2156   else
2157   {
2158      PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
2159   }
2160 }
2161
2162 /******************************************************************************
2163  *            PROPSHEET_SetTitleW
2164  */
2165 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
2166 {
2167   PropSheetInfo*        psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2168   WCHAR                 szTitle[256];
2169
2170   TRACE("'%s' (style %08lx)\n", debugstr_w(lpszText), dwStyle);
2171   if (HIWORD(lpszText) == 0) {
2172     if (!LoadStringW(psInfo->ppshheader.hInstance,
2173                      LOWORD(lpszText), szTitle, sizeof(szTitle)-sizeof(WCHAR)))
2174       return;
2175     lpszText = szTitle;
2176   }
2177   if (dwStyle & PSH_PROPTITLE)
2178   {
2179     WCHAR* dest;
2180     int lentitle = strlenW(lpszText);
2181     int lenprop  = strlenW(psInfo->strPropertiesFor);
2182
2183     dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2184     strcpyW(dest, psInfo->strPropertiesFor);
2185     strcatW(dest, lpszText);
2186
2187     SetWindowTextW(hwndDlg, dest);
2188     Free(dest);
2189   }
2190   else
2191     SetWindowTextW(hwndDlg, lpszText);
2192 }
2193
2194 /******************************************************************************
2195  *            PROPSHEET_SetFinishTextA
2196  */
2197 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
2198 {
2199   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2200
2201   TRACE("'%s'\n", lpszText);
2202   /* Set text, show and enable the Finish button */
2203   SetWindowTextA(hwndButton, lpszText);
2204   ShowWindow(hwndButton, SW_SHOW);
2205   EnableWindow(hwndButton, TRUE);
2206
2207   /* Make it default pushbutton */
2208   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2209
2210   /* Hide Back button */
2211   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2212   ShowWindow(hwndButton, SW_HIDE);
2213
2214   /* Hide Next button */
2215   hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2216   ShowWindow(hwndButton, SW_HIDE);
2217 }
2218
2219 /******************************************************************************
2220  *            PROPSHEET_SetFinishTextW
2221  */
2222 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
2223 {
2224   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2225
2226   TRACE("'%s'\n", debugstr_w(lpszText));
2227   /* Set text, show and enable the Finish button */
2228   SetWindowTextW(hwndButton, lpszText);
2229   ShowWindow(hwndButton, SW_SHOW);
2230   EnableWindow(hwndButton, TRUE);
2231
2232   /* Make it default pushbutton */
2233   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2234
2235   /* Hide Back button */
2236   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2237   ShowWindow(hwndButton, SW_HIDE);
2238
2239   /* Hide Next button */
2240   hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2241   ShowWindow(hwndButton, SW_HIDE);
2242 }
2243
2244 /******************************************************************************
2245  *            PROPSHEET_QuerySiblings
2246  */
2247 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
2248                                        WPARAM wParam, LPARAM lParam)
2249 {
2250   int i = 0;
2251   HWND hwndPage;
2252   LRESULT msgResult = 0;
2253   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
2254
2255   while ((i < psInfo->nPages) && (msgResult == 0))
2256   {
2257     hwndPage = psInfo->proppage[i].hwndPage;
2258     msgResult = SendMessageW(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2259     i++;
2260   }
2261
2262   return msgResult;
2263 }
2264
2265
2266 /******************************************************************************
2267  *            PROPSHEET_AddPage
2268  */
2269 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
2270                               HPROPSHEETPAGE hpage)
2271 {
2272   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2273                                                      PropSheetInfoStr);
2274   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2275   TCITEMW item;
2276   LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
2277
2278   TRACE("hpage %p\n", hpage);
2279   /*
2280    * Allocate and fill in a new PropPageInfo entry.
2281    */
2282   psInfo->proppage = (PropPageInfo*) ReAlloc(psInfo->proppage,
2283                                                       sizeof(PropPageInfo) *
2284                                                       (psInfo->nPages + 1));
2285   if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages))
2286       return FALSE;
2287
2288   psInfo->proppage[psInfo->nPages].hpage = hpage;
2289
2290   if (ppsp->dwFlags & PSP_PREMATURE)
2291   {
2292      /* Create the page but don't show it */
2293      PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp);
2294   }
2295
2296   /*
2297    * Add a new tab to the tab control.
2298    */
2299   item.mask = TCIF_TEXT;
2300   item.pszText = (LPWSTR) psInfo->proppage[psInfo->nPages].pszText;
2301   item.cchTextMax = MAX_TABTEXT_LENGTH;
2302
2303   if (psInfo->hImageList)
2304   {
2305     SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
2306   }
2307
2308   if ( psInfo->proppage[psInfo->nPages].hasIcon )
2309   {
2310     item.mask |= TCIF_IMAGE;
2311     item.iImage = psInfo->nPages;
2312   }
2313
2314   SendMessageW(hwndTabControl, TCM_INSERTITEMW, psInfo->nPages + 1,
2315                (LPARAM)&item);
2316
2317   psInfo->nPages++;
2318
2319   /* If it is the only page - show it */
2320   if(psInfo->nPages == 1)
2321      PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2322   return TRUE;
2323 }
2324
2325 /******************************************************************************
2326  *            PROPSHEET_RemovePage
2327  */
2328 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
2329                                  int index,
2330                                  HPROPSHEETPAGE hpage)
2331 {
2332   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2333                                                      PropSheetInfoStr);
2334   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2335   PropPageInfo* oldPages;
2336
2337   TRACE("index %d, hpage %p\n", index, hpage);
2338   if (!psInfo) {
2339     return FALSE;
2340   }
2341   /*
2342    * hpage takes precedence over index.
2343    */
2344   if (hpage != 0)
2345   {
2346     index = PROPSHEET_GetPageIndex(hpage, psInfo);
2347   }
2348
2349   /* Make sure that index is within range */
2350   if (index < 0 || index >= psInfo->nPages)
2351   {
2352       TRACE("Could not find page to remove!\n");
2353       return FALSE;
2354   }
2355
2356   TRACE("total pages %d removing page %d active page %d\n",
2357         psInfo->nPages, index, psInfo->active_page);
2358   /*
2359    * Check if we're removing the active page.
2360    */
2361   if (index == psInfo->active_page)
2362   {
2363     if (psInfo->nPages > 1)
2364     {
2365       if (index > 0)
2366       {
2367         /* activate previous page  */
2368         PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2369       }
2370       else
2371       {
2372         /* activate the next page */
2373         PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2374         psInfo->active_page = index;
2375       }
2376     }
2377     else
2378     {
2379       psInfo->active_page = -1;
2380       if (!psInfo->isModeless)
2381       {
2382          psInfo->ended = TRUE;
2383          return TRUE;
2384       }
2385     }
2386   }
2387   else if (index < psInfo->active_page)
2388     psInfo->active_page--;
2389
2390   /* Unsubclass the page dialog window */
2391   if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD) &&
2392      (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2393      ((PROPSHEETPAGEW*)psInfo->proppage[index].hpage)->dwFlags & PSP_HIDEHEADER))
2394   {
2395      RemoveWindowSubclass(psInfo->proppage[index].hwndPage,
2396                           PROPSHEET_WizardSubclassProc, 1);
2397   }
2398
2399   /* Destroy page dialog window */
2400   DestroyWindow(psInfo->proppage[index].hwndPage);
2401
2402   /* Free page resources */
2403   if(psInfo->proppage[index].hpage)
2404   {
2405      PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)psInfo->proppage[index].hpage;
2406
2407      if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[index].pszText)
2408         Free ((LPVOID)psInfo->proppage[index].pszText);
2409
2410      DestroyPropertySheetPage(psInfo->proppage[index].hpage);
2411   }
2412
2413   /* Remove the tab */
2414   SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2415
2416   oldPages = psInfo->proppage;
2417   psInfo->nPages--;
2418   psInfo->proppage = Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2419
2420   if (index > 0)
2421     memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
2422
2423   if (index < psInfo->nPages)
2424     memcpy(&psInfo->proppage[index], &oldPages[index + 1],
2425            (psInfo->nPages - index) * sizeof(PropPageInfo));
2426
2427   Free(oldPages);
2428
2429   return FALSE;
2430 }
2431
2432 /******************************************************************************
2433  *            PROPSHEET_SetWizButtons
2434  *
2435  * This code will work if (and assumes that) the Next button is on top of the
2436  * Finish button. ie. Finish comes after Next in the Z order.
2437  * This means make sure the dialog template reflects this.
2438  *
2439  */
2440 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
2441 {
2442   HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2443   HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2444   HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2445
2446   TRACE("%ld\n", dwFlags);
2447
2448   EnableWindow(hwndBack, FALSE);
2449   EnableWindow(hwndNext, FALSE);
2450   EnableWindow(hwndFinish, FALSE);
2451
2452   if (dwFlags & PSWIZB_BACK)
2453     EnableWindow(hwndBack, TRUE);
2454
2455   if (dwFlags & PSWIZB_NEXT)
2456   {
2457     /* Hide the Finish button */
2458     ShowWindow(hwndFinish, SW_HIDE);
2459
2460     /* Show and enable the Next button */
2461     ShowWindow(hwndNext, SW_SHOW);
2462     EnableWindow(hwndNext, TRUE);
2463
2464     /* Set the Next button as the default pushbutton  */
2465     SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
2466   }
2467
2468   if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
2469   {
2470     /* Hide the Next button */
2471     ShowWindow(hwndNext, SW_HIDE);
2472
2473     /* Show the Finish button */
2474     ShowWindow(hwndFinish, SW_SHOW);
2475
2476     if (dwFlags & PSWIZB_FINISH)
2477       EnableWindow(hwndFinish, TRUE);
2478
2479     /* Set the Finish button as the default pushbutton  */
2480     SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2481   }
2482 }
2483
2484 /******************************************************************************
2485  *            PROPSHEET_InsertPage
2486  */
2487 static BOOL PROPSHEET_InsertPage(HWND hwndDlg, HPROPSHEETPAGE hpageInsertAfter, HPROPSHEETPAGE hpage)
2488 {
2489     if (!HIWORD(hpageInsertAfter))
2490         FIXME("(%p, %d, %p): stub\n", hwndDlg, LOWORD(hpageInsertAfter), hpage);
2491     else
2492         FIXME("(%p, %p, %p): stub\n", hwndDlg, hpageInsertAfter, hpage);
2493     return FALSE;
2494 }
2495
2496 /******************************************************************************
2497  *            PROPSHEET_SetHeaderTitleW
2498  */
2499 static void PROPSHEET_SetHeaderTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderTitle)
2500 {
2501     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderTitle));
2502 }
2503
2504 /******************************************************************************
2505  *            PROPSHEET_SetHeaderTitleA
2506  */
2507 static void PROPSHEET_SetHeaderTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderTitle)
2508 {
2509     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderTitle));
2510 }
2511
2512 /******************************************************************************
2513  *            PROPSHEET_SetHeaderSubTitleW
2514  */
2515 static void PROPSHEET_SetHeaderSubTitleW(HWND hwndDlg, int iPageIndex, LPCWSTR pszHeaderSubTitle)
2516 {
2517     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_w(pszHeaderSubTitle));
2518 }
2519
2520 /******************************************************************************
2521  *            PROPSHEET_SetHeaderSubTitleA
2522  */
2523 static void PROPSHEET_SetHeaderSubTitleA(HWND hwndDlg, int iPageIndex, LPCSTR pszHeaderSubTitle)
2524 {
2525     FIXME("(%p, %d, %s): stub\n", hwndDlg, iPageIndex, debugstr_a(pszHeaderSubTitle));
2526 }
2527
2528 /******************************************************************************
2529  *            PROPSHEET_HwndToIndex
2530  */
2531 static LRESULT PROPSHEET_HwndToIndex(HWND hwndDlg, HWND hPageDlg)
2532 {
2533     int index;
2534     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2535                                                        PropSheetInfoStr);
2536
2537     TRACE("(%p, %p)\n", hwndDlg, hPageDlg);
2538
2539     for (index = 0; index < psInfo->nPages; index++)
2540         if (psInfo->proppage[index].hwndPage == hPageDlg)
2541             return index;
2542     WARN("%p not found\n", hPageDlg);
2543     return -1;
2544 }
2545
2546 /******************************************************************************
2547  *            PROPSHEET_IndexToHwnd
2548  */
2549 static LRESULT PROPSHEET_IndexToHwnd(HWND hwndDlg, int iPageIndex)
2550 {
2551     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2552                                                        PropSheetInfoStr);
2553     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2554     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2555         WARN("%d out of range.\n", iPageIndex);
2556         return 0;
2557     }
2558     return (LRESULT)psInfo->proppage[iPageIndex].hwndPage;
2559 }
2560
2561 /******************************************************************************
2562  *            PROPSHEET_PageToIndex
2563  */
2564 static LRESULT PROPSHEET_PageToIndex(HWND hwndDlg, HPROPSHEETPAGE hPage)
2565 {
2566     int index;
2567     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2568                                                        PropSheetInfoStr);
2569
2570     TRACE("(%p, %p)\n", hwndDlg, hPage);
2571
2572     for (index = 0; index < psInfo->nPages; index++)
2573         if (psInfo->proppage[index].hpage == hPage)
2574             return index;
2575     WARN("%p not found\n", hPage);
2576     return -1;
2577 }
2578
2579 /******************************************************************************
2580  *            PROPSHEET_IndexToPage
2581  */
2582 static LRESULT PROPSHEET_IndexToPage(HWND hwndDlg, int iPageIndex)
2583 {
2584     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2585                                                        PropSheetInfoStr);
2586     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2587     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2588         WARN("%d out of range.\n", iPageIndex);
2589         return 0;
2590     }
2591     return (LRESULT)psInfo->proppage[iPageIndex].hpage;
2592 }
2593
2594 /******************************************************************************
2595  *            PROPSHEET_IdToIndex
2596  */
2597 static LRESULT PROPSHEET_IdToIndex(HWND hwndDlg, int iPageId)
2598 {
2599     FIXME("(%p, %d): stub\n", hwndDlg, iPageId);
2600     return -1;
2601 }
2602
2603 /******************************************************************************
2604  *            PROPSHEET_IndexToId
2605  */
2606 static LRESULT PROPSHEET_IndexToId(HWND hwndDlg, int iPageIndex)
2607 {
2608     PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
2609                                                        PropSheetInfoStr);
2610     LPCPROPSHEETPAGEW psp;
2611     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2612     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2613         WARN("%d out of range.\n", iPageIndex);
2614         return 0;
2615     }
2616     psp = (LPCPROPSHEETPAGEW)psInfo->proppage[iPageIndex].hpage;
2617     if (psp->dwFlags & PSP_DLGINDIRECT || HIWORD(psp->u.pszTemplate)) {
2618         return 0;
2619     }
2620     return (LRESULT)psp->u.pszTemplate;
2621 }
2622
2623 /******************************************************************************
2624  *            PROPSHEET_GetResult
2625  */
2626 static LRESULT PROPSHEET_GetResult(HWND hwndDlg)
2627 {
2628     FIXME("(%p): stub\n", hwndDlg);
2629     return -1;
2630 }
2631
2632 /******************************************************************************
2633  *            PROPSHEET_RecalcPageSizes
2634  */
2635 static BOOL PROPSHEET_RecalcPageSizes(HWND hwndDlg)
2636 {
2637     FIXME("(%p): stub\n", hwndDlg);
2638     return FALSE;
2639 }
2640
2641 /******************************************************************************
2642  *            PROPSHEET_GetPageIndex
2643  *
2644  * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
2645  * the array of PropPageInfo.
2646  */
2647 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
2648 {
2649   BOOL found = FALSE;
2650   int index = 0;
2651
2652   TRACE("hpage %p\n", hpage);
2653   while ((index < psInfo->nPages) && (found == FALSE))
2654   {
2655     if (psInfo->proppage[index].hpage == hpage)
2656       found = TRUE;
2657     else
2658       index++;
2659   }
2660
2661   if (found == FALSE)
2662     index = -1;
2663
2664   return index;
2665 }
2666
2667 /******************************************************************************
2668  *            PROPSHEET_CleanUp
2669  */
2670 static void PROPSHEET_CleanUp(HWND hwndDlg)
2671 {
2672   int i;
2673   PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropW(hwndDlg,
2674                                                        PropSheetInfoStr);
2675
2676   TRACE("\n");
2677   if (!psInfo) return;
2678   if (HIWORD(psInfo->ppshheader.pszCaption))
2679       Free ((LPVOID)psInfo->ppshheader.pszCaption);
2680
2681   for (i = 0; i < psInfo->nPages; i++)
2682   {
2683      PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
2684
2685      /* Unsubclass the page dialog window */
2686      if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
2687         (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2688         (psp->dwFlags & PSP_HIDEHEADER))
2689      {
2690         RemoveWindowSubclass(psInfo->proppage[i].hwndPage,
2691                              PROPSHEET_WizardSubclassProc, 1);
2692      }
2693
2694      if(psInfo->proppage[i].hwndPage)
2695         DestroyWindow(psInfo->proppage[i].hwndPage);
2696
2697      if(psp)
2698      {
2699         if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[i].pszText)
2700            Free ((LPVOID)psInfo->proppage[i].pszText);
2701
2702         DestroyPropertySheetPage(psInfo->proppage[i].hpage);
2703      }
2704   }
2705
2706   DeleteObject(psInfo->hFont);
2707   DeleteObject(psInfo->hFontBold);
2708   /* If we created the bitmaps, destroy them */
2709   if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2710       (!(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK)) )
2711       DeleteObject(psInfo->ppshheader.u4.hbmWatermark);
2712   if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
2713       (!(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER)) )
2714       DeleteObject(psInfo->ppshheader.u5.hbmHeader);
2715
2716   Free(psInfo->proppage);
2717   Free(psInfo->strPropertiesFor);
2718   ImageList_Destroy(psInfo->hImageList);
2719
2720   GlobalFree((HGLOBAL)psInfo);
2721 }
2722
2723 static INT do_loop(PropSheetInfo *psInfo)
2724 {
2725     MSG msg;
2726     INT ret = -1;
2727     HWND hwnd = psInfo->hwnd;
2728
2729     while(IsWindow(hwnd) && !psInfo->ended && (ret = GetMessageW(&msg, NULL, 0, 0)))
2730     {
2731         if(ret == -1)
2732             break;
2733
2734         if(!IsDialogMessageW(hwnd, &msg))
2735         {
2736             TranslateMessage(&msg);
2737             DispatchMessageW(&msg);
2738         }
2739     }
2740
2741     if(ret == 0)
2742     {
2743         PostQuitMessage(msg.wParam);
2744         ret = -1;
2745     }
2746
2747     DestroyWindow(hwnd);
2748     return ret;
2749 }
2750
2751 /******************************************************************************
2752  *            PropertySheet    (COMCTL32.@)
2753  *            PropertySheetA   (COMCTL32.@)
2754  *
2755  * Creates a property sheet in the specified property sheet header.
2756  *
2757  * RETURNS
2758  *     Modal property sheets: Positive if successful or -1 otherwise.
2759  *     Modeless property sheets: Property sheet handle.
2760  *     Or:
2761  *| ID_PSREBOOTSYSTEM - The user must reboot the computer for the changes to take effect.
2762  *| ID_PSRESTARTWINDOWS - The user must restart Windows for the changes to take effect.
2763  */
2764 INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2765 {
2766   int bRet = 0;
2767   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2768                                                        sizeof(PropSheetInfo));
2769   UINT i, n;
2770   BYTE* pByte;
2771
2772   TRACE("(%p)\n", lppsh);
2773
2774   PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2775
2776   psInfo->proppage = (PropPageInfo*) Alloc(sizeof(PropPageInfo) *
2777                                                     lppsh->nPages);
2778   pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2779
2780   for (n = i = 0; i < lppsh->nPages; i++, n++)
2781   {
2782     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
2783       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2784     else
2785     {
2786        psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
2787        pByte += ((LPPROPSHEETPAGEA)pByte)->dwSize;
2788     }
2789
2790     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2791                                psInfo, n))
2792     {
2793         if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
2794             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2795         n--;
2796         psInfo->nPages--;
2797     }
2798   }
2799
2800   psInfo->unicode = FALSE;
2801   psInfo->ended = FALSE;
2802
2803   bRet = PROPSHEET_CreateDialog(psInfo);
2804   if(!psInfo->isModeless)
2805       bRet = do_loop(psInfo);
2806
2807   return bRet;
2808 }
2809
2810 /******************************************************************************
2811  *            PropertySheetW   (COMCTL32.@)
2812  *
2813  * See PropertySheetA.
2814  */
2815 INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2816 {
2817   int bRet = 0;
2818   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2819                                                        sizeof(PropSheetInfo));
2820   UINT i, n;
2821   BYTE* pByte;
2822
2823   TRACE("(%p)\n", lppsh);
2824
2825   PROPSHEET_CollectSheetInfoW(lppsh, psInfo);
2826
2827   psInfo->proppage = (PropPageInfo*) Alloc(sizeof(PropPageInfo) *
2828                                                     lppsh->nPages);
2829   pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2830
2831   for (n = i = 0; i < lppsh->nPages; i++, n++)
2832   {
2833     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
2834       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2835     else
2836     {
2837        psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2838        pByte += ((LPPROPSHEETPAGEW)pByte)->dwSize;
2839     }
2840
2841     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2842                                psInfo, n))
2843     {
2844         if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
2845             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2846         n--;
2847         psInfo->nPages--;
2848     }
2849   }
2850
2851   psInfo->unicode = TRUE;
2852   psInfo->ended = FALSE;
2853
2854   bRet = PROPSHEET_CreateDialog(psInfo);
2855   if(!psInfo->isModeless)
2856       bRet = do_loop(psInfo);
2857
2858   return bRet;
2859 }
2860
2861 /******************************************************************************
2862  *            CreatePropertySheetPage    (COMCTL32.@)
2863  *            CreatePropertySheetPageA   (COMCTL32.@)
2864  *
2865  * Creates a new property sheet page.
2866  *
2867  * RETURNS
2868  *     Success: Handle to new property sheet page.
2869  *     Failure: NULL.
2870  *
2871  * NOTES
2872  *     An application must use the PSM_ADDPAGE message to add the new page to
2873  *     an existing property sheet.
2874  */
2875 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
2876                           LPCPROPSHEETPAGEA lpPropSheetPage)
2877 {
2878   PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
2879
2880   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEA)));
2881
2882   ppsp->dwFlags &= ~ PSP_INTERNAL_UNICODE;
2883   if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
2884   {
2885      int len = strlen(lpPropSheetPage->u.pszTemplate);
2886
2887      ppsp->u.pszTemplate = Alloc( len+1 );
2888      strcpy( (LPSTR)ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate );
2889   }
2890   if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2891   {
2892       PROPSHEET_AtoW(&ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon);
2893   }
2894
2895   if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2896   {
2897       PROPSHEET_AtoW(&ppsp->pszTitle, lpPropSheetPage->pszTitle);
2898   }
2899   else if ( !(ppsp->dwFlags & PSP_USETITLE) )
2900       ppsp->pszTitle = NULL;
2901
2902   return (HPROPSHEETPAGE)ppsp;
2903 }
2904
2905 /******************************************************************************
2906  *            CreatePropertySheetPageW   (COMCTL32.@)
2907  *
2908  * See CreatePropertySheetA.
2909  */
2910 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
2911 {
2912   PROPSHEETPAGEW* ppsp = Alloc(sizeof(PROPSHEETPAGEW));
2913
2914   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEW)));
2915
2916   ppsp->dwFlags |= PSP_INTERNAL_UNICODE;
2917
2918   if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
2919   {
2920     int len = strlenW(lpPropSheetPage->u.pszTemplate);
2921
2922     ppsp->u.pszTemplate = Alloc( (len+1)*sizeof (WCHAR) );
2923     strcpyW( (WCHAR *)ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate );
2924   }
2925   if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2926   {
2927       int len = strlenW(lpPropSheetPage->u2.pszIcon);
2928       ppsp->u2.pszIcon = Alloc( (len+1)*sizeof (WCHAR) );
2929       strcpyW( (WCHAR *)ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon );
2930   }
2931
2932   if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2933   {
2934       int len = strlenW(lpPropSheetPage->pszTitle);
2935       ppsp->pszTitle = Alloc( (len+1)*sizeof (WCHAR) );
2936       strcpyW( (WCHAR *)ppsp->pszTitle, lpPropSheetPage->pszTitle );
2937   }
2938   else if ( !(ppsp->dwFlags & PSP_USETITLE) )
2939       ppsp->pszTitle = NULL;
2940
2941   return (HPROPSHEETPAGE)ppsp;
2942 }
2943
2944 /******************************************************************************
2945  *            DestroyPropertySheetPage   (COMCTL32.@)
2946  *
2947  * Destroys a property sheet page previously created with
2948  * CreatePropertySheetA() or CreatePropertySheetW() and frees the associated
2949  * memory.
2950  *
2951  * RETURNS
2952  *     Success: TRUE
2953  *     Failure: FALSE
2954  */
2955 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
2956 {
2957   PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
2958
2959   if (!psp)
2960      return FALSE;
2961
2962   if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u.pszTemplate ) )
2963      Free ((LPVOID)psp->u.pszTemplate);
2964
2965   if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) )
2966      Free ((LPVOID)psp->u2.pszIcon);
2967
2968   if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle ))
2969      Free ((LPVOID)psp->pszTitle);
2970
2971   Free(hPropPage);
2972
2973   return TRUE;
2974 }
2975
2976 /******************************************************************************
2977  *            PROPSHEET_IsDialogMessage
2978  */
2979 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
2980 {
2981    PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
2982
2983    TRACE("\n");
2984    if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
2985       return FALSE;
2986
2987    if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
2988    {
2989       int new_page = 0;
2990       INT dlgCode = SendMessageW(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
2991
2992       if (!(dlgCode & DLGC_WANTMESSAGE))
2993       {
2994          switch (lpMsg->wParam)
2995          {
2996             case VK_TAB:
2997                if (GetKeyState(VK_SHIFT) & 0x8000)
2998                    new_page = -1;
2999                 else
3000                    new_page = 1;
3001                break;
3002
3003             case VK_NEXT:   new_page = 1;  break;
3004             case VK_PRIOR:  new_page = -1; break;
3005          }
3006       }
3007
3008       if (new_page)
3009       {
3010          if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
3011          {
3012             new_page += psInfo->active_page;
3013
3014             if (new_page < 0)
3015                new_page = psInfo->nPages - 1;
3016             else if (new_page >= psInfo->nPages)
3017                new_page = 0;
3018
3019             PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
3020          }
3021
3022          return TRUE;
3023       }
3024    }
3025
3026    return IsDialogMessageW(hwnd, lpMsg);
3027 }
3028
3029 /******************************************************************************
3030  *            PROPSHEET_DoCommand
3031  */
3032 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID)
3033 {
3034
3035     switch (wID) {
3036
3037     case IDOK:
3038     case IDC_APPLY_BUTTON:
3039         {
3040             HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
3041
3042             if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
3043                 break;
3044
3045             if (wID == IDOK)
3046                 {
3047                     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3048                                                                       PropSheetInfoStr);
3049                     int result = TRUE;
3050
3051                     if (psInfo->restartWindows)
3052                         result = ID_PSRESTARTWINDOWS;
3053
3054                     /* reboot system takes precedence over restart windows */
3055                     if (psInfo->rebootSystem)
3056                         result = ID_PSREBOOTSYSTEM;
3057
3058                     if (psInfo->isModeless)
3059                         psInfo->activeValid = FALSE;
3060                     else
3061                         psInfo->ended = TRUE;
3062                 }
3063             else
3064                 EnableWindow(hwndApplyBtn, FALSE);
3065
3066             break;
3067         }
3068
3069     case IDC_BACK_BUTTON:
3070         PROPSHEET_Back(hwnd);
3071         break;
3072
3073     case IDC_NEXT_BUTTON:
3074         PROPSHEET_Next(hwnd);
3075         break;
3076
3077     case IDC_FINISH_BUTTON:
3078         PROPSHEET_Finish(hwnd);
3079         break;
3080
3081     case IDCANCEL:
3082         PROPSHEET_Cancel(hwnd, 0);
3083         break;
3084
3085     case IDHELP:
3086         PROPSHEET_Help(hwnd);
3087         break;
3088
3089     default:
3090         return FALSE;
3091     }
3092
3093     return TRUE;
3094 }
3095
3096 /******************************************************************************
3097  *            PROPSHEET_Paint
3098  */
3099 static LRESULT PROPSHEET_Paint(HWND hwnd, HDC hdcParam)
3100 {
3101     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
3102     PAINTSTRUCT ps;
3103     HDC hdc, hdcSrc;
3104     BITMAP bm;
3105     HBITMAP hbmp;
3106     HPALETTE hOldPal = 0;
3107     int offsety = 0;
3108     HBRUSH hbr;
3109     RECT r, rzone;
3110     LPCPROPSHEETPAGEW ppshpage;
3111     WCHAR szBuffer[256];
3112     int nLength;
3113
3114     hdc = hdcParam ? hdcParam : BeginPaint(hwnd, &ps);
3115     if (!hdc) return 1;
3116
3117     hdcSrc = CreateCompatibleDC(0);
3118     ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[psInfo->active_page].hpage;
3119
3120     if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK) 
3121         hOldPal = SelectPalette(hdc, psInfo->ppshheader.hplWatermark, FALSE);
3122
3123     if ( (!(ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3124          (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
3125          (psInfo->ppshheader.dwFlags & PSH_HEADER) ) 
3126     {
3127         HWND hwndLineHeader = GetDlgItem(hwnd, IDC_SUNKEN_LINEHEADER);
3128         HFONT hOldFont;
3129         COLORREF clrOld = 0;
3130         int oldBkMode = 0;
3131
3132         hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u5.hbmHeader);
3133         hOldFont = SelectObject(hdc, psInfo->hFontBold);
3134
3135         GetClientRect(hwndLineHeader, &r);
3136         MapWindowPoints(hwndLineHeader, hwnd, (LPPOINT) &r, 2);
3137         SetRect(&rzone, 0, 0, r.right + 1, r.top - 1);
3138
3139         GetObjectW(psInfo->ppshheader.u5.hbmHeader, sizeof(BITMAP), (LPVOID)&bm);               
3140
3141         if (psInfo->ppshheader.dwFlags & PSH_WIZARD97_OLD)
3142         {
3143             /* Fill the unoccupied part of the header with color of the
3144              * left-top pixel, but do it only when needed.
3145              */
3146             if (bm.bmWidth < r.right || bm.bmHeight < r.bottom)
3147             {
3148                 hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
3149                 CopyRect(&r, &rzone);
3150                 if (bm.bmWidth < r.right)
3151                 {
3152                     r.left = bm.bmWidth;
3153                     FillRect(hdc, &r, hbr);
3154                 }
3155                 if (bm.bmHeight < r.bottom)
3156                 {
3157                     r.left = 0;
3158                     r.top = bm.bmHeight;
3159                     FillRect(hdc, &r, hbr);
3160                 }
3161                 DeleteObject(hbr);
3162             }
3163
3164             /* Draw the header itself. */
3165             BitBlt(hdc, 0, 0,
3166                    bm.bmWidth, min(bm.bmHeight, rzone.bottom),
3167                    hdcSrc, 0, 0, SRCCOPY);
3168         }
3169         else
3170         {
3171             hbr = GetSysColorBrush(COLOR_WINDOW);
3172             FillRect(hdc, &rzone, hbr);
3173
3174             /* Draw the header bitmap. It's always centered like a
3175              * common 49 x 49 bitmap. */
3176             BitBlt(hdc, rzone.right - 49 - ((rzone.bottom - 49) / 2),
3177                    (rzone.bottom - 49) / 2,
3178                    bm.bmWidth, bm.bmHeight,
3179                    hdcSrc, 0, 0, SRCCOPY);
3180
3181             /* NOTE: Native COMCTL32 draws a white stripe over the bitmap
3182              * if its height is smaller than 49 pixels. Because the reason
3183              * for this bug is unknown the current code doesn't try to
3184              * replicate it. */
3185         }
3186
3187         clrOld = SetTextColor (hdc, 0x00000000);
3188         oldBkMode = SetBkMode (hdc, TRANSPARENT); 
3189
3190         if (ppshpage->dwFlags & PSP_USEHEADERTITLE) {
3191             SetRect(&r, 20, 10, 0, 0);
3192             if (HIWORD(ppshpage->pszHeaderTitle))
3193             {
3194                 if (psInfo->unicode)
3195                     DrawTextW(hdc, (LPWSTR)ppshpage->pszHeaderTitle,
3196                               -1, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3197                 else
3198                     DrawTextA(hdc, (LPCSTR)ppshpage->pszHeaderTitle,
3199                               -1, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3200             }
3201             else
3202             {
3203                 nLength = LoadStringW(ppshpage->hInstance, (UINT)ppshpage->pszHeaderTitle,
3204                                       szBuffer, 256);
3205                 if (nLength != 0)
3206                 {
3207                     DrawTextW(hdc, szBuffer, nLength,
3208                               &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3209                 }
3210             }
3211         }
3212
3213         if (ppshpage->dwFlags & PSP_USEHEADERSUBTITLE) {
3214             SelectObject(hdc, psInfo->hFont);
3215             SetRect(&r, 40, 25, rzone.right - 69, rzone.bottom);
3216             if (HIWORD(ppshpage->pszHeaderTitle))
3217             {
3218                 if (psInfo->unicode)
3219                     DrawTextW(hdc, (LPWSTR)ppshpage->pszHeaderSubTitle,
3220                               -1, &r, DT_LEFT | DT_SINGLELINE);
3221                 else
3222                     DrawTextA(hdc, (LPCSTR)ppshpage->pszHeaderSubTitle,
3223                               -1, &r, DT_LEFT | DT_SINGLELINE);
3224             }
3225             else
3226             {
3227                 nLength = LoadStringW(ppshpage->hInstance, (UINT)ppshpage->pszHeaderSubTitle,
3228                                       szBuffer, 256);
3229                 if (nLength != 0)
3230                 {
3231                     DrawTextW(hdc, szBuffer, nLength,
3232                               &r, DT_LEFT | DT_SINGLELINE);
3233                 }
3234             }
3235         }
3236
3237         offsety = rzone.bottom + 2;
3238
3239         SetTextColor(hdc, clrOld);
3240         SetBkMode(hdc, oldBkMode);
3241         SelectObject(hdc, hOldFont);
3242         SelectObject(hdcSrc, hbmp);
3243     }
3244
3245     if ( (ppshpage->dwFlags & PSP_HIDEHEADER) &&
3246          (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
3247          (psInfo->ppshheader.dwFlags & PSH_WATERMARK) ) 
3248     {
3249         HWND hwndLine = GetDlgItem(hwnd, IDC_SUNKEN_LINE);          
3250
3251         GetClientRect(hwndLine, &r);
3252         MapWindowPoints(hwndLine, hwnd, (LPPOINT) &r, 2);
3253
3254         rzone.left = 0;
3255         rzone.top = 0;
3256         rzone.right = r.right;
3257         rzone.bottom = r.top - 1;
3258
3259         hbr = GetSysColorBrush(COLOR_WINDOW);
3260         FillRect(hdc, &rzone, hbr);
3261
3262         GetObjectW(psInfo->ppshheader.u4.hbmWatermark, sizeof(BITMAP), (LPVOID)&bm);
3263         hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u4.hbmWatermark);
3264
3265         BitBlt(hdc, 0, offsety, min(bm.bmWidth, r.right),
3266                min(bm.bmHeight, r.bottom), hdcSrc, 0, 0, SRCCOPY);
3267
3268         /* If the bitmap is not big enough, fill the remaining area
3269            with the color of pixel (0,0) of bitmap - see MSDN */
3270         if (r.top > bm.bmHeight) {
3271             r.bottom = r.top - 1;
3272             r.top = bm.bmHeight;
3273             r.left = 0;
3274             r.right = bm.bmWidth;
3275             hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
3276             FillRect(hdc, &r, hbr);
3277             DeleteObject(hbr);
3278         }
3279
3280         SelectObject(hdcSrc, hbmp);         
3281     }
3282
3283     if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK) 
3284         SelectPalette(hdc, hOldPal, FALSE);
3285
3286     DeleteDC(hdcSrc);
3287
3288     if (!hdcParam) EndPaint(hwnd, &ps);
3289
3290     return 0;
3291 }
3292
3293 /******************************************************************************
3294  *            PROPSHEET_DialogProc
3295  */
3296 INT_PTR CALLBACK
3297 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3298 {
3299   TRACE("hwnd=%p msg=0x%04x wparam=%x lparam=%lx\n",
3300         hwnd, uMsg, wParam, lParam);
3301
3302   switch (uMsg)
3303   {
3304     case WM_INITDIALOG:
3305     {
3306       PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
3307       WCHAR* strCaption = (WCHAR*)Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
3308       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
3309       LPCPROPSHEETPAGEW ppshpage;
3310       int idx;
3311       LOGFONTW logFont;
3312
3313       /* Using PropSheetInfoStr to store extra data doesn't match the native
3314        * common control: native uses TCM_[GS]ETITEM
3315        */
3316       SetPropW(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
3317
3318       /*
3319        * psInfo->hwnd is not being used by WINE code - it exists
3320        * for compatibility with "real" Windoze. The same about
3321        * SetWindowLongPtr - WINE is only using the PropSheetInfoStr
3322        * property.
3323        */
3324       psInfo->hwnd = hwnd;
3325       SetWindowLongPtrW(hwnd, DWLP_USER, (DWORD_PTR)psInfo);
3326
3327       /* set up the Next and Back buttons by default */
3328       PROPSHEET_SetWizButtons(hwnd, PSWIZB_BACK|PSWIZB_NEXT);
3329
3330       /* Set up fonts */
3331       SystemParametersInfoW (SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
3332       psInfo->hFont = CreateFontIndirectW (&logFont);
3333       logFont.lfWeight = FW_BOLD;
3334       psInfo->hFontBold = CreateFontIndirectW (&logFont);
3335       
3336       /*
3337        * Small icon in the title bar.
3338        */
3339       if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
3340           (psInfo->ppshheader.dwFlags & PSH_USEHICON))
3341       {
3342         HICON hIcon;
3343         int icon_cx = GetSystemMetrics(SM_CXSMICON);
3344         int icon_cy = GetSystemMetrics(SM_CYSMICON);
3345
3346         if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
3347           hIcon = LoadImageW(psInfo->ppshheader.hInstance,
3348                              psInfo->ppshheader.u.pszIcon,
3349                              IMAGE_ICON,
3350                              icon_cx, icon_cy,
3351                              LR_DEFAULTCOLOR);
3352         else
3353           hIcon = psInfo->ppshheader.u.hIcon;
3354
3355         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)hIcon);
3356       }
3357
3358       if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
3359         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)psInfo->ppshheader.u.hIcon);
3360
3361       psInfo->strPropertiesFor = strCaption;
3362
3363       GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
3364
3365       PROPSHEET_CreateTabControl(hwnd, psInfo);
3366
3367       PROPSHEET_LoadWizardBitmaps(psInfo);
3368
3369       if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3370       {
3371         ShowWindow(hwndTabCtrl, SW_HIDE);
3372         PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
3373         PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
3374       }
3375       else
3376       {
3377         if (PROPSHEET_SizeMismatch(hwnd, psInfo))
3378         {
3379           PROPSHEET_AdjustSize(hwnd, psInfo);
3380           PROPSHEET_AdjustButtons(hwnd, psInfo);
3381         }
3382       }
3383
3384       if (psInfo->useCallback)
3385              (*(psInfo->ppshheader.pfnCallback))(hwnd,
3386                                               PSCB_INITIALIZED, (LPARAM)0);
3387
3388       idx = psInfo->active_page;
3389       ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[idx].hpage;
3390       psInfo->active_page = -1;
3391
3392       PROPSHEET_SetCurSel(hwnd, idx, 1, psInfo->proppage[idx].hpage);
3393
3394       /* doing TCM_SETCURSEL seems to be needed even in case of PSH_WIZARD,
3395        * as some programs call TCM_GETCURSEL to get the current selection
3396        * from which to switch to the next page */
3397       SendMessageW(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
3398
3399       if (!HIWORD(psInfo->ppshheader.pszCaption) &&
3400               psInfo->ppshheader.hInstance)
3401       {
3402          WCHAR szText[256];
3403
3404          if (LoadStringW(psInfo->ppshheader.hInstance,
3405                  (UINT)psInfo->ppshheader.pszCaption, szText, 255))
3406             PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
3407       }
3408       else
3409       {
3410          PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
3411                          psInfo->ppshheader.pszCaption);
3412       }
3413
3414       return TRUE;
3415     }
3416
3417     case WM_PAINT:
3418       PROPSHEET_Paint(hwnd, (HDC)wParam);
3419       return TRUE;
3420
3421     case WM_DESTROY:
3422       PROPSHEET_CleanUp(hwnd);
3423       return TRUE;
3424
3425     case WM_CLOSE:
3426       PROPSHEET_Cancel(hwnd, 1);
3427       return TRUE;
3428
3429     case WM_COMMAND:
3430       if (!PROPSHEET_DoCommand(hwnd, LOWORD(wParam)))
3431       {
3432           PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
3433
3434           if (!psInfo)
3435               return FALSE;
3436
3437           /* No default handler, forward notification to active page */
3438           if (psInfo->activeValid && psInfo->active_page != -1)
3439           {
3440              HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
3441              SendMessageW(hwndPage, WM_COMMAND, wParam, lParam);
3442           }
3443       }
3444       return TRUE;
3445
3446     case WM_SYSCOMMAND:
3447       switch(wParam & 0xfff0)
3448       {
3449         case SC_CLOSE:
3450           PROPSHEET_Cancel(hwnd, 1);
3451           return TRUE;
3452
3453         default:
3454           return FALSE;
3455       }
3456
3457     case WM_NOTIFY:
3458     {
3459       NMHDR* pnmh = (LPNMHDR) lParam;
3460
3461       if (pnmh->code == TCN_SELCHANGE)
3462       {
3463         int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
3464         PROPSHEET_SetCurSel(hwnd, index, 1, 0);
3465       }
3466
3467       if(pnmh->code == TCN_SELCHANGING)
3468       {
3469         BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
3470         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, !bRet);
3471         return TRUE;
3472       }
3473
3474       return FALSE;
3475     }
3476
3477     case PSM_GETCURRENTPAGEHWND:
3478     {
3479       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3480                                                         PropSheetInfoStr);
3481       HWND hwndPage = 0;
3482
3483       if (!psInfo)
3484         return FALSE;
3485
3486       if (psInfo->activeValid && psInfo->active_page != -1)
3487         hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
3488
3489       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndPage);
3490
3491       return TRUE;
3492     }
3493
3494     case PSM_CHANGED:
3495       PROPSHEET_Changed(hwnd, (HWND)wParam);
3496       return TRUE;
3497
3498     case PSM_UNCHANGED:
3499       PROPSHEET_UnChanged(hwnd, (HWND)wParam);
3500       return TRUE;
3501
3502     case PSM_GETTABCONTROL:
3503     {
3504       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
3505
3506       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndTabCtrl);
3507
3508       return TRUE;
3509     }
3510
3511     case PSM_SETCURSEL:
3512     {
3513       BOOL msgResult;
3514
3515       msgResult = PROPSHEET_CanSetCurSel(hwnd);
3516       if(msgResult != FALSE)
3517       {
3518         msgResult = PROPSHEET_SetCurSel(hwnd,
3519                                        (int)wParam,
3520                                        1,
3521                                        (HPROPSHEETPAGE)lParam);
3522       }
3523
3524       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3525
3526       return TRUE;
3527     }
3528
3529     case PSM_CANCELTOCLOSE:
3530     {
3531       WCHAR buf[MAX_BUTTONTEXT_LENGTH];
3532       HWND hwndOK = GetDlgItem(hwnd, IDOK);
3533       HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
3534
3535       EnableWindow(hwndCancel, FALSE);
3536       if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)))
3537          SetWindowTextW(hwndOK, buf);
3538
3539       return FALSE;
3540     }
3541
3542     case PSM_RESTARTWINDOWS:
3543     {
3544       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3545                                                         PropSheetInfoStr);
3546
3547       if (!psInfo)
3548         return FALSE;
3549
3550       psInfo->restartWindows = TRUE;
3551       return TRUE;
3552     }
3553
3554     case PSM_REBOOTSYSTEM:
3555     {
3556       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
3557                                                         PropSheetInfoStr);
3558
3559       if (!psInfo)
3560         return FALSE;
3561
3562       psInfo->rebootSystem = TRUE;
3563       return TRUE;
3564     }
3565
3566     case PSM_SETTITLEA:
3567       PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
3568       return TRUE;
3569
3570     case PSM_SETTITLEW:
3571       PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
3572       return TRUE;
3573
3574     case PSM_APPLY:
3575     {
3576       BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
3577
3578       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3579
3580       return TRUE;
3581     }
3582
3583     case PSM_QUERYSIBLINGS:
3584     {
3585       LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
3586
3587       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3588
3589       return TRUE;
3590     }
3591
3592     case PSM_ADDPAGE:
3593     {
3594       /*
3595        * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have
3596        *       a return value. This is not true. PSM_ADDPAGE returns TRUE
3597        *       on success or FALSE otherwise, as specified on MSDN Online.
3598        *       Also see the MFC code for
3599        *       CPropertySheet::AddPage(CPropertyPage* pPage).
3600        */
3601
3602       BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
3603
3604       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3605
3606       return TRUE;
3607     }
3608
3609     case PSM_REMOVEPAGE:
3610       PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
3611       return TRUE;
3612
3613     case PSM_ISDIALOGMESSAGE:
3614     {
3615        BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
3616        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3617        return TRUE;
3618     }
3619
3620     case PSM_PRESSBUTTON:
3621       PROPSHEET_PressButton(hwnd, (int)wParam);
3622       return TRUE;
3623
3624     case PSM_SETFINISHTEXTA:
3625       PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
3626       return TRUE;
3627
3628     case PSM_SETWIZBUTTONS:
3629       PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
3630       return TRUE;
3631
3632     case PSM_SETCURSELID:
3633         PROPSHEET_SetCurSelId(hwnd, (int)lParam);
3634         return TRUE;
3635
3636     case PSM_SETFINISHTEXTW:
3637         PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
3638         return FALSE;
3639
3640     case PSM_INSERTPAGE:
3641     {
3642         BOOL msgResult = PROPSHEET_InsertPage(hwnd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam);
3643         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3644         return TRUE;
3645     }
3646
3647     case PSM_SETHEADERTITLEW:
3648         PROPSHEET_SetHeaderTitleW(hwnd, (int)wParam, (LPCWSTR)lParam);
3649         return TRUE;
3650
3651     case PSM_SETHEADERTITLEA:
3652         PROPSHEET_SetHeaderTitleA(hwnd, (int)wParam, (LPCSTR)lParam);
3653         return TRUE;
3654
3655     case PSM_SETHEADERSUBTITLEW:
3656         PROPSHEET_SetHeaderSubTitleW(hwnd, (int)wParam, (LPCWSTR)lParam);
3657         return TRUE;
3658
3659     case PSM_SETHEADERSUBTITLEA:
3660         PROPSHEET_SetHeaderSubTitleA(hwnd, (int)wParam, (LPCSTR)lParam);
3661         return TRUE;
3662
3663     case PSM_HWNDTOINDEX:
3664     {
3665         LRESULT msgResult = PROPSHEET_HwndToIndex(hwnd, (HWND)wParam);
3666         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3667         return TRUE;
3668     }
3669
3670     case PSM_INDEXTOHWND:
3671     {
3672         LRESULT msgResult = PROPSHEET_IndexToHwnd(hwnd, (int)wParam);
3673         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3674         return TRUE;
3675     }
3676
3677     case PSM_PAGETOINDEX:
3678     {
3679         LRESULT msgResult = PROPSHEET_PageToIndex(hwnd, (HPROPSHEETPAGE)wParam);
3680         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3681         return TRUE;
3682     }
3683
3684     case PSM_INDEXTOPAGE:
3685     {
3686         LRESULT msgResult = PROPSHEET_IndexToPage(hwnd, (int)wParam);
3687         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3688         return TRUE;
3689     }
3690
3691     case PSM_IDTOINDEX:
3692     {
3693         LRESULT msgResult = PROPSHEET_IdToIndex(hwnd, (int)lParam);
3694         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3695         return TRUE;
3696     }
3697
3698     case PSM_INDEXTOID:
3699     {
3700         LRESULT msgResult = PROPSHEET_IndexToId(hwnd, (int)wParam);
3701         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3702         return TRUE;
3703     }
3704
3705     case PSM_GETRESULT:
3706     {
3707         LRESULT msgResult = PROPSHEET_GetResult(hwnd);
3708         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3709         return TRUE;
3710     }
3711
3712     case PSM_RECALCPAGESIZES:
3713     {
3714         LRESULT msgResult = PROPSHEET_RecalcPageSizes(hwnd);
3715         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3716         return TRUE;
3717     }
3718
3719     default:
3720       return FALSE;
3721   }
3722 }