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