xref: /reactos/dll/win32/comctl32/propsheet.c (revision 6a6b5ec2)
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 #ifdef __REACTOS__
1337 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage);
1338 #endif
1339 /******************************************************************************
1340  *            PROPSHEET_CreatePage
1341  *
1342  * Creates a page.
1343  */
1344 static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1345                                 int index,
1346                                 const PropSheetInfo * psInfo,
1347                                 LPCPROPSHEETPAGEW ppshpage)
1348 {
1349   const DLGTEMPLATE* pTemplate;
1350   HWND hwndPage;
1351   DWORD resSize;
1352   DLGTEMPLATE* pTemplateCopy = NULL;
1353 
1354   TRACE("index %d\n", index);
1355 
1356   if (ppshpage == NULL)
1357   {
1358     return FALSE;
1359   }
1360 
1361   if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1362     {
1363       pTemplate = ppshpage->u.pResource;
1364       resSize = GetTemplateSize(pTemplate);
1365     }
1366   else if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1367   {
1368     HRSRC hResource;
1369     HANDLE hTemplate;
1370 
1371     hResource = FindResourceW(ppshpage->hInstance,
1372                                     ppshpage->u.pszTemplate,
1373                                     (LPWSTR)RT_DIALOG);
1374     if(!hResource)
1375 	return FALSE;
1376 
1377     resSize = SizeofResource(ppshpage->hInstance, hResource);
1378 
1379     hTemplate = LoadResource(ppshpage->hInstance, hResource);
1380     if(!hTemplate)
1381 	return FALSE;
1382 
1383     pTemplate = LockResource(hTemplate);
1384     /*
1385      * Make a copy of the dialog template to make it writable
1386      */
1387   }
1388   else
1389   {
1390     HRSRC hResource;
1391     HANDLE hTemplate;
1392 
1393     hResource = FindResourceA(ppshpage->hInstance,
1394                                     (LPCSTR)ppshpage->u.pszTemplate,
1395                                     (LPSTR)RT_DIALOG);
1396     if(!hResource)
1397 	return FALSE;
1398 
1399     resSize = SizeofResource(ppshpage->hInstance, hResource);
1400 
1401     hTemplate = LoadResource(ppshpage->hInstance, hResource);
1402     if(!hTemplate)
1403 	return FALSE;
1404 
1405     pTemplate = LockResource(hTemplate);
1406     /*
1407      * Make a copy of the dialog template to make it writable
1408      */
1409   }
1410   pTemplateCopy = Alloc(resSize);
1411   if (!pTemplateCopy)
1412     return FALSE;
1413 
1414   TRACE("copying pTemplate %p into pTemplateCopy %p (%d)\n", pTemplate, pTemplateCopy, resSize);
1415   memcpy(pTemplateCopy, pTemplate, resSize);
1416 
1417   if (((MyDLGTEMPLATEEX*)pTemplateCopy)->signature == 0xFFFF)
1418   {
1419     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1420     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~DS_MODALFRAME;
1421     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_CAPTION;
1422     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_SYSMENU;
1423     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_POPUP;
1424     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_DISABLED;
1425     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_VISIBLE;
1426     ((MyDLGTEMPLATEEX*)pTemplateCopy)->style &= ~WS_THICKFRAME;
1427 
1428     ((MyDLGTEMPLATEEX*)pTemplateCopy)->exStyle |= WS_EX_CONTROLPARENT;
1429   }
1430   else
1431   {
1432     pTemplateCopy->style |= WS_CHILD | WS_TABSTOP | DS_CONTROL;
1433     pTemplateCopy->style &= ~DS_MODALFRAME;
1434     pTemplateCopy->style &= ~WS_CAPTION;
1435     pTemplateCopy->style &= ~WS_SYSMENU;
1436     pTemplateCopy->style &= ~WS_POPUP;
1437     pTemplateCopy->style &= ~WS_DISABLED;
1438     pTemplateCopy->style &= ~WS_VISIBLE;
1439     pTemplateCopy->style &= ~WS_THICKFRAME;
1440 
1441     pTemplateCopy->dwExtendedStyle |= WS_EX_CONTROLPARENT;
1442   }
1443 
1444   if (psInfo->proppage[index].useCallback)
1445     (*(ppshpage->pfnCallback))(0, PSPCB_CREATE,
1446                                (LPPROPSHEETPAGEW)ppshpage);
1447 
1448   if(ppshpage->dwFlags & PSP_INTERNAL_UNICODE)
1449      hwndPage = CreateDialogIndirectParamW(ppshpage->hInstance,
1450 					pTemplateCopy,
1451 					hwndParent,
1452 					ppshpage->pfnDlgProc,
1453 					(LPARAM)ppshpage);
1454   else
1455      hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
1456 					pTemplateCopy,
1457 					hwndParent,
1458 					ppshpage->pfnDlgProc,
1459 					(LPARAM)ppshpage);
1460   /* Free a no more needed copy */
1461   Free(pTemplateCopy);
1462 
1463   if(!hwndPage)
1464       return FALSE;
1465 
1466   psInfo->proppage[index].hwndPage = hwndPage;
1467 
1468   /* Subclass exterior wizard pages */
1469   if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
1470      (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
1471      (ppshpage->dwFlags & PSP_HIDEHEADER))
1472   {
1473 #ifdef __REACTOS__
1474     if (psInfo->ppshheader.u4.hbmWatermark)
1475 #endif
1476       SetWindowSubclass(hwndPage, PROPSHEET_WizardSubclassProc, 1,
1477                         (DWORD_PTR)ppshpage);
1478   }
1479   if (!(psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD))
1480       EnableThemeDialogTexture (hwndPage, ETDT_ENABLETAB);
1481 
1482 #ifdef __REACTOS__
1483   PROPSHEET_UnChanged(hwndParent, hwndPage);
1484 #endif
1485   return TRUE;
1486 }
1487 
1488 /******************************************************************************
1489  *            PROPSHEET_LoadWizardBitmaps
1490  *
1491  * Loads the watermark and header bitmaps for a wizard.
1492  */
1493 static VOID PROPSHEET_LoadWizardBitmaps(PropSheetInfo *psInfo)
1494 {
1495   if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD))
1496   {
1497     /* if PSH_USEHBMWATERMARK is not set, load the resource from pszbmWatermark
1498        and put the HBITMAP in hbmWatermark. Thus all the rest of the code always
1499        considers hbmWatermark as valid. */
1500     if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
1501         !(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK))
1502     {
1503       psInfo->ppshheader.u4.hbmWatermark =
1504         CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u4.pszbmWatermark, 0, NULL, 0);
1505     }
1506 
1507     /* Same behavior as for watermarks */
1508     if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
1509         !(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER))
1510     {
1511       psInfo->ppshheader.u5.hbmHeader =
1512         CreateMappedBitmap(psInfo->ppshheader.hInstance, (INT_PTR)psInfo->ppshheader.u5.pszbmHeader, 0, NULL, 0);
1513     }
1514   }
1515 }
1516 
1517 
1518 /******************************************************************************
1519  *            PROPSHEET_ShowPage
1520  *
1521  * Displays or creates the specified page.
1522  */
1523 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
1524 {
1525   HWND hwndTabCtrl;
1526   HWND hwndLineHeader;
1527   HWND control;
1528   LPCPROPSHEETPAGEW ppshpage;
1529 
1530   TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1531   if (index == psInfo->active_page)
1532   {
1533       if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
1534           SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1535       return TRUE;
1536   }
1537 
1538   ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1539   if (psInfo->proppage[index].hwndPage == 0)
1540   {
1541      PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1542   }
1543 
1544   if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1545   {
1546      PROPSHEET_SetTitleW(hwndDlg, psInfo->ppshheader.dwFlags,
1547                          psInfo->proppage[index].pszText);
1548 
1549      control = GetNextDlgTabItem(psInfo->proppage[index].hwndPage, NULL, FALSE);
1550      if(control != NULL)
1551          SetFocus(control);
1552   }
1553 
1554   if (psInfo->active_page != -1)
1555      ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1556 
1557   ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1558 
1559   /* Synchronize current selection with tab control
1560    * It seems to be needed even in case of PSH_WIZARD (no tab controls there) */
1561   hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1562   SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1563 
1564   psInfo->active_page = index;
1565   psInfo->activeValid = TRUE;
1566 
1567   if (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW) )
1568   {
1569       hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
1570       ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1571 
1572       if ((ppshpage->dwFlags & PSP_HIDEHEADER) || (!(psInfo->ppshheader.dwFlags & PSH_HEADER)) )
1573 	  ShowWindow(hwndLineHeader, SW_HIDE);
1574       else
1575 	  ShowWindow(hwndLineHeader, SW_SHOW);
1576   }
1577 
1578   return TRUE;
1579 }
1580 
1581 /******************************************************************************
1582  *            PROPSHEET_Back
1583  */
1584 static BOOL PROPSHEET_Back(HWND hwndDlg)
1585 {
1586   PSHNOTIFY psn;
1587   HWND hwndPage;
1588   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1589   LRESULT result;
1590   int idx;
1591 
1592   TRACE("active_page %d\n", psInfo->active_page);
1593   if (psInfo->active_page < 0)
1594      return FALSE;
1595 
1596   psn.hdr.code     = PSN_WIZBACK;
1597   psn.hdr.hwndFrom = hwndDlg;
1598   psn.hdr.idFrom   = 0;
1599   psn.lParam       = 0;
1600 
1601   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1602 
1603   result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1604   if (result == -1)
1605     return FALSE;
1606   else if (result == 0)
1607      idx = psInfo->active_page - 1;
1608   else
1609      idx = PROPSHEET_FindPageByResId(psInfo, result);
1610 
1611   if (idx >= 0 && idx < psInfo->nPages)
1612   {
1613      if (PROPSHEET_CanSetCurSel(hwndDlg))
1614      {
1615         SetFocus(GetDlgItem(hwndDlg, IDC_BACK_BUTTON));
1616         SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
1617         PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1618      }
1619   }
1620   return TRUE;
1621 }
1622 
1623 /******************************************************************************
1624  *            PROPSHEET_Next
1625  */
1626 static BOOL PROPSHEET_Next(HWND hwndDlg)
1627 {
1628   PSHNOTIFY psn;
1629   HWND hwndPage;
1630   LRESULT msgResult = 0;
1631   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1632   int idx;
1633 
1634   TRACE("active_page %d\n", psInfo->active_page);
1635   if (psInfo->active_page < 0)
1636      return FALSE;
1637 
1638   psn.hdr.code     = PSN_WIZNEXT;
1639   psn.hdr.hwndFrom = hwndDlg;
1640   psn.hdr.idFrom   = 0;
1641   psn.lParam       = 0;
1642 
1643   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1644 
1645   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1646   if (msgResult == -1)
1647     return FALSE;
1648   else if (msgResult == 0)
1649      idx = psInfo->active_page + 1;
1650   else
1651      idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1652 
1653   if (idx < psInfo->nPages )
1654   {
1655      if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1656      {
1657         SetFocus(GetDlgItem(hwndDlg, IDC_NEXT_BUTTON));
1658         SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
1659         PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1660      }
1661   }
1662 
1663   return TRUE;
1664 }
1665 
1666 /******************************************************************************
1667  *            PROPSHEET_Finish
1668  */
1669 static BOOL PROPSHEET_Finish(HWND hwndDlg)
1670 {
1671   PSHNOTIFY psn;
1672   HWND hwndPage;
1673   LRESULT msgResult = 0;
1674   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1675 
1676   TRACE("active_page %d\n", psInfo->active_page);
1677   if (psInfo->active_page < 0)
1678      return FALSE;
1679 
1680   psn.hdr.code     = PSN_WIZFINISH;
1681   psn.hdr.hwndFrom = hwndDlg;
1682   psn.hdr.idFrom   = 0;
1683   psn.lParam       = 0;
1684 
1685   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1686 
1687   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1688 
1689   TRACE("msg result %ld\n", msgResult);
1690 
1691   if (msgResult != 0)
1692     return FALSE;
1693 
1694   if (psInfo->result == 0)
1695       psInfo->result = IDOK;
1696   if (psInfo->isModeless)
1697     psInfo->activeValid = FALSE;
1698   else
1699     psInfo->ended = TRUE;
1700 
1701   return TRUE;
1702 }
1703 
1704 /******************************************************************************
1705  *            PROPSHEET_Apply
1706  */
1707 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1708 {
1709   int i;
1710   HWND hwndPage;
1711   PSHNOTIFY psn;
1712   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1713 
1714   TRACE("active_page %d\n", psInfo->active_page);
1715   if (psInfo->active_page < 0)
1716      return FALSE;
1717 
1718   psn.hdr.hwndFrom = hwndDlg;
1719   psn.hdr.idFrom   = 0;
1720   psn.lParam       = 0;
1721 
1722 
1723   /*
1724    * Send PSN_KILLACTIVE to the current page.
1725    */
1726   psn.hdr.code = PSN_KILLACTIVE;
1727 
1728   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1729 
1730   if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1731     return FALSE;
1732 
1733   /*
1734    * Send PSN_APPLY to all pages.
1735    */
1736   psn.hdr.code = PSN_APPLY;
1737   psn.lParam   = lParam;
1738 
1739   for (i = 0; i < psInfo->nPages; i++)
1740   {
1741     hwndPage = psInfo->proppage[i].hwndPage;
1742     if (hwndPage)
1743     {
1744        switch (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1745        {
1746        case PSNRET_INVALID:
1747            PROPSHEET_ShowPage(hwndDlg, i, psInfo);
1748            /* fall through */
1749        case PSNRET_INVALID_NOCHANGEPAGE:
1750            return FALSE;
1751        }
1752     }
1753   }
1754 
1755   if(lParam)
1756   {
1757      psInfo->activeValid = FALSE;
1758   }
1759   else if(psInfo->active_page >= 0)
1760   {
1761      psn.hdr.code = PSN_SETACTIVE;
1762      psn.lParam   = 0;
1763      hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1764      SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1765   }
1766 
1767   return TRUE;
1768 }
1769 
1770 /******************************************************************************
1771  *            PROPSHEET_Cancel
1772  */
1773 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1774 {
1775   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1776   HWND hwndPage;
1777   PSHNOTIFY psn;
1778   int i;
1779 
1780   TRACE("active_page %d\n", psInfo->active_page);
1781   if (psInfo->active_page < 0)
1782      return;
1783 
1784   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1785   psn.hdr.code     = PSN_QUERYCANCEL;
1786   psn.hdr.hwndFrom = hwndDlg;
1787   psn.hdr.idFrom   = 0;
1788   psn.lParam       = 0;
1789 
1790   if (SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1791     return;
1792 
1793   psn.hdr.code = PSN_RESET;
1794   psn.lParam   = lParam;
1795 
1796   for (i = 0; i < psInfo->nPages; i++)
1797   {
1798     hwndPage = psInfo->proppage[i].hwndPage;
1799 
1800     if (hwndPage)
1801        SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1802   }
1803 
1804   if (psInfo->isModeless)
1805   {
1806      /* makes PSM_GETCURRENTPAGEHWND return NULL */
1807      psInfo->activeValid = FALSE;
1808   }
1809   else
1810     psInfo->ended = TRUE;
1811 }
1812 
1813 /******************************************************************************
1814  *            PROPSHEET_Help
1815  */
1816 static void PROPSHEET_Help(HWND hwndDlg)
1817 {
1818   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1819   HWND hwndPage;
1820   PSHNOTIFY psn;
1821 
1822   TRACE("active_page %d\n", psInfo->active_page);
1823   if (psInfo->active_page < 0)
1824      return;
1825 
1826   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1827   psn.hdr.code     = PSN_HELP;
1828   psn.hdr.hwndFrom = hwndDlg;
1829   psn.hdr.idFrom   = 0;
1830   psn.lParam       = 0;
1831 
1832   SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1833 }
1834 
1835 /******************************************************************************
1836  *            PROPSHEET_Changed
1837  */
1838 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1839 {
1840   int i;
1841   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1842 
1843   TRACE("\n");
1844   if (!psInfo) return;
1845   /*
1846    * Set the dirty flag of this page.
1847    */
1848   for (i = 0; i < psInfo->nPages; i++)
1849   {
1850     if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1851       psInfo->proppage[i].isDirty = TRUE;
1852   }
1853 
1854   /*
1855    * Enable the Apply button.
1856    */
1857   if (psInfo->hasApply)
1858   {
1859     HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1860 
1861     EnableWindow(hwndApplyBtn, TRUE);
1862   }
1863 }
1864 
1865 /******************************************************************************
1866  *            PROPSHEET_UnChanged
1867  */
1868 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1869 {
1870   int i;
1871   BOOL noPageDirty = TRUE;
1872   HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1873   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1874 
1875   TRACE("\n");
1876   if ( !psInfo ) return;
1877   for (i = 0; i < psInfo->nPages; i++)
1878   {
1879     /* set the specified page as clean */
1880     if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1881       psInfo->proppage[i].isDirty = FALSE;
1882 
1883     /* look to see if there are any dirty pages */
1884     if (psInfo->proppage[i].isDirty)
1885       noPageDirty = FALSE;
1886   }
1887 
1888   /*
1889    * Disable Apply button.
1890    */
1891   if (noPageDirty)
1892     EnableWindow(hwndApplyBtn, FALSE);
1893 }
1894 
1895 /******************************************************************************
1896  *            PROPSHEET_PressButton
1897  */
1898 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1899 {
1900   TRACE("buttonID %d\n", buttonID);
1901   switch (buttonID)
1902   {
1903     case PSBTN_APPLYNOW:
1904       PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1905       break;
1906     case PSBTN_BACK:
1907       PROPSHEET_Back(hwndDlg);
1908       break;
1909     case PSBTN_CANCEL:
1910       PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1911       break;
1912     case PSBTN_FINISH:
1913       PROPSHEET_Finish(hwndDlg);
1914       break;
1915     case PSBTN_HELP:
1916       PROPSHEET_DoCommand(hwndDlg, IDHELP);
1917       break;
1918     case PSBTN_NEXT:
1919       PROPSHEET_Next(hwndDlg);
1920       break;
1921     case PSBTN_OK:
1922       PROPSHEET_DoCommand(hwndDlg, IDOK);
1923       break;
1924     default:
1925       FIXME("Invalid button index %d\n", buttonID);
1926   }
1927 }
1928 
1929 
1930 /*************************************************************************
1931  * BOOL PROPSHEET_CanSetCurSel [Internal]
1932  *
1933  * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1934  *
1935  * PARAMS
1936  *     hwndDlg        [I] handle to a Dialog hWnd
1937  *
1938  * RETURNS
1939  *     TRUE if Current Selection can change
1940  *
1941  * NOTES
1942  */
1943 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1944 {
1945   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1946   HWND hwndPage;
1947   PSHNOTIFY psn;
1948   BOOL res = FALSE;
1949 
1950   if (!psInfo)
1951   {
1952      res = FALSE;
1953      goto end;
1954   }
1955 
1956   TRACE("active_page %d\n", psInfo->active_page);
1957   if (psInfo->active_page < 0)
1958   {
1959      res = TRUE;
1960      goto end;
1961   }
1962 
1963   /*
1964    * Notify the current page.
1965    */
1966   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1967   psn.hdr.code     = PSN_KILLACTIVE;
1968   psn.hdr.hwndFrom = hwndDlg;
1969   psn.hdr.idFrom   = 0;
1970   psn.lParam       = 0;
1971 
1972   res = !SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1973 
1974 end:
1975   TRACE("<-- %d\n", res);
1976   return res;
1977 }
1978 
1979 /******************************************************************************
1980  *            PROPSHEET_SetCurSel
1981  */
1982 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
1983                                 int index,
1984 				int skipdir,
1985                                 HPROPSHEETPAGE hpage
1986 				)
1987 {
1988   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
1989   HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
1990   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1991 
1992   TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
1993 
1994   index = PROPSHEET_GetPageIndex(hpage, psInfo, index);
1995 
1996   if (index < 0 || index >= psInfo->nPages)
1997   {
1998     TRACE("Could not find page to select!\n");
1999     return FALSE;
2000   }
2001 
2002   /* unset active page while doing this transition. */
2003   if (psInfo->active_page != -1)
2004      ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
2005   psInfo->active_page = -1;
2006 
2007   while (1) {
2008     int result;
2009     PSHNOTIFY psn;
2010     RECT rc;
2011     LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2012 
2013     if (hwndTabControl)
2014 	SendMessageW(hwndTabControl, TCM_SETCURSEL, index, 0);
2015 
2016     psn.hdr.code     = PSN_SETACTIVE;
2017     psn.hdr.hwndFrom = hwndDlg;
2018     psn.hdr.idFrom   = 0;
2019     psn.lParam       = 0;
2020 
2021     if (!psInfo->proppage[index].hwndPage) {
2022       if(!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage)) {
2023         PROPSHEET_RemovePage(hwndDlg, index, NULL);
2024         if(index >= psInfo->nPages)
2025           index--;
2026         if(index < 0)
2027             return FALSE;
2028         continue;
2029       }
2030     }
2031 
2032     /* Resize the property sheet page to the fit in the Tab control
2033      * (for regular property sheets) or to fit in the client area (for
2034      * wizards).
2035      * NOTE: The resizing happens every time the page is selected and
2036      * not only when it's created (some applications depend on it). */
2037     PROPSHEET_GetPageRect(psInfo, hwndDlg, &rc, ppshpage);
2038     TRACE("setting page %p, rc (%s) w=%d, h=%d\n",
2039           psInfo->proppage[index].hwndPage, wine_dbgstr_rect(&rc),
2040           rc.right - rc.left, rc.bottom - rc.top);
2041     SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP,
2042                  rc.left, rc.top,
2043                  rc.right - rc.left, rc.bottom - rc.top, 0);
2044 
2045     result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
2046     if (!result)
2047       break;
2048     if (result == -1) {
2049       index+=skipdir;
2050       if (index < 0) {
2051 	index = 0;
2052 	WARN("Tried to skip before first property sheet page!\n");
2053 	break;
2054       }
2055       if (index >= psInfo->nPages) {
2056 	WARN("Tried to skip after last property sheet page!\n");
2057 	index = psInfo->nPages-1;
2058 	break;
2059       }
2060     }
2061     else if (result != 0)
2062     {
2063       int old_index = index;
2064       index = PROPSHEET_FindPageByResId(psInfo, result);
2065       if(index >= psInfo->nPages) {
2066         index = old_index;
2067         WARN("Tried to skip to nonexistent page by res id\n");
2068         break;
2069       }
2070       continue;
2071     }
2072   }
2073 
2074   /* Invalidate the header area */
2075   if ( (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
2076        (psInfo->ppshheader.dwFlags & PSH_HEADER) )
2077   {
2078     HWND hwndLineHeader = GetDlgItem(hwndDlg, IDC_SUNKEN_LINEHEADER);
2079     RECT r;
2080 
2081     GetClientRect(hwndLineHeader, &r);
2082     MapWindowPoints(hwndLineHeader, hwndDlg, (LPPOINT) &r, 2);
2083     SetRect(&r, 0, 0, r.right + 1, r.top - 1);
2084 
2085     InvalidateRect(hwndDlg, &r, TRUE);
2086   }
2087 
2088   /*
2089    * Display the new page.
2090    */
2091   PROPSHEET_ShowPage(hwndDlg, index, psInfo);
2092 
2093   if (psInfo->proppage[index].hasHelp)
2094     EnableWindow(hwndHelp, TRUE);
2095   else
2096     EnableWindow(hwndHelp, FALSE);
2097 
2098   return TRUE;
2099 }
2100 
2101 /******************************************************************************
2102  *            PROPSHEET_SetCurSelId
2103  *
2104  * Selects the page, specified by resource id.
2105  */
2106 static void PROPSHEET_SetCurSelId(HWND hwndDlg, int id)
2107 {
2108       int idx;
2109       PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2110 
2111       idx = PROPSHEET_FindPageByResId(psInfo, id);
2112       if (idx < psInfo->nPages )
2113       {
2114           if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
2115               PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
2116       }
2117 }
2118 
2119 /******************************************************************************
2120  *            PROPSHEET_SetTitleA
2121  */
2122 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
2123 {
2124   if(!IS_INTRESOURCE(lpszText))
2125   {
2126      WCHAR szTitle[256];
2127      MultiByteToWideChar(CP_ACP, 0, lpszText, -1, szTitle, ARRAY_SIZE(szTitle));
2128      PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
2129   }
2130   else
2131   {
2132      PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
2133   }
2134 }
2135 
2136 /******************************************************************************
2137  *            PROPSHEET_SetTitleW
2138  */
2139 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
2140 {
2141   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2142   WCHAR szTitle[256];
2143 
2144   TRACE("%s (style %08x)\n", debugstr_w(lpszText), dwStyle);
2145   if (IS_INTRESOURCE(lpszText)) {
2146     if (!LoadStringW(psInfo->ppshheader.hInstance, LOWORD(lpszText), szTitle, ARRAY_SIZE(szTitle)))
2147       return;
2148     lpszText = szTitle;
2149   }
2150   if (dwStyle & PSH_PROPTITLE)
2151   {
2152     WCHAR* dest;
2153     int lentitle = strlenW(lpszText);
2154     int lenprop  = strlenW(psInfo->strPropertiesFor);
2155 
2156     dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
2157     wsprintfW(dest, psInfo->strPropertiesFor, lpszText);
2158 
2159     SetWindowTextW(hwndDlg, dest);
2160     Free(dest);
2161   }
2162   else
2163     SetWindowTextW(hwndDlg, lpszText);
2164 }
2165 
2166 /******************************************************************************
2167  *            PROPSHEET_SetFinishTextA
2168  */
2169 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
2170 {
2171   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2172   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2173 
2174   TRACE("'%s'\n", lpszText);
2175   /* Set text, show and enable the Finish button */
2176   SetWindowTextA(hwndButton, lpszText);
2177   ShowWindow(hwndButton, SW_SHOW);
2178   EnableWindow(hwndButton, TRUE);
2179 
2180   /* Make it default pushbutton */
2181   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2182 
2183   /* Hide Back button */
2184   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2185   ShowWindow(hwndButton, SW_HIDE);
2186 
2187   if (!psInfo->hasFinish)
2188   {
2189     /* Hide Next button */
2190     hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2191     ShowWindow(hwndButton, SW_HIDE);
2192   }
2193 }
2194 
2195 /******************************************************************************
2196  *            PROPSHEET_SetFinishTextW
2197  */
2198 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
2199 {
2200   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2201   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2202 
2203   TRACE("%s\n", debugstr_w(lpszText));
2204   /* Set text, show and enable the Finish button */
2205   SetWindowTextW(hwndButton, lpszText);
2206   ShowWindow(hwndButton, SW_SHOW);
2207   EnableWindow(hwndButton, TRUE);
2208 
2209   /* Make it default pushbutton */
2210   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2211 
2212   /* Hide Back button */
2213   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2214   ShowWindow(hwndButton, SW_HIDE);
2215 
2216   if (!psInfo->hasFinish)
2217   {
2218     /* Hide Next button */
2219     hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2220     ShowWindow(hwndButton, SW_HIDE);
2221   }
2222 }
2223 
2224 /******************************************************************************
2225  *            PROPSHEET_QuerySiblings
2226  */
2227 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
2228                                        WPARAM wParam, LPARAM lParam)
2229 {
2230   int i = 0;
2231   HWND hwndPage;
2232   LRESULT msgResult = 0;
2233   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2234 
2235   while ((i < psInfo->nPages) && (msgResult == 0))
2236   {
2237     hwndPage = psInfo->proppage[i].hwndPage;
2238     msgResult = SendMessageW(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
2239     i++;
2240   }
2241 
2242   return msgResult;
2243 }
2244 
2245 /******************************************************************************
2246  *            PROPSHEET_InsertPage
2247  */
2248 static BOOL PROPSHEET_InsertPage(HWND hwndDlg, HPROPSHEETPAGE hpageInsertAfter, HPROPSHEETPAGE hpage)
2249 {
2250   PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2251   PropPageInfo *ppi, *prev_ppi = psInfo->proppage;
2252   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2253   LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
2254   TCITEMW item;
2255   int index;
2256 
2257   TRACE("hwndDlg %p, hpageInsertAfter %p, hpage %p\n", hwndDlg, hpageInsertAfter, hpage);
2258 
2259   if (IS_INTRESOURCE(hpageInsertAfter))
2260     index = LOWORD(hpageInsertAfter);
2261   else
2262   {
2263     index = PROPSHEET_GetPageIndex(hpageInsertAfter, psInfo, -1);
2264     if (index < 0)
2265     {
2266       TRACE("Could not find page to insert after!\n");
2267       return FALSE;
2268     }
2269     index++;
2270   }
2271 
2272   if (index > psInfo->nPages)
2273     index = psInfo->nPages;
2274 
2275   ppi = Alloc(sizeof(PropPageInfo) * (psInfo->nPages + 1));
2276   if (!ppi)
2277       return FALSE;
2278 
2279   /*
2280    * Fill in a new PropPageInfo entry.
2281    */
2282   if (index > 0)
2283     memcpy(ppi, prev_ppi, index * sizeof(PropPageInfo));
2284   memset(&ppi[index], 0, sizeof(PropPageInfo));
2285   if (index < psInfo->nPages)
2286     memcpy(&ppi[index + 1], &prev_ppi[index], (psInfo->nPages - index) * sizeof(PropPageInfo));
2287   psInfo->proppage = ppi;
2288 
2289   if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, index, FALSE))
2290   {
2291      psInfo->proppage = prev_ppi;
2292      Free(ppi);
2293      return FALSE;
2294   }
2295 
2296   psInfo->proppage[index].hpage = hpage;
2297 
2298   if (ppsp->dwFlags & PSP_PREMATURE)
2299   {
2300      /* Create the page but don't show it */
2301      if (!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppsp))
2302      {
2303         psInfo->proppage = prev_ppi;
2304         Free(ppi);
2305         return FALSE;
2306      }
2307   }
2308 
2309   Free(prev_ppi);
2310   psInfo->nPages++;
2311   if (index <= psInfo->active_page)
2312     psInfo->active_page++;
2313 
2314   /*
2315    * Add a new tab to the tab control.
2316    */
2317   item.mask = TCIF_TEXT;
2318   item.pszText = (LPWSTR) psInfo->proppage[index].pszText;
2319   item.cchTextMax = MAX_TABTEXT_LENGTH;
2320 
2321   if (psInfo->hImageList)
2322     SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
2323 
2324   if (psInfo->proppage[index].hasIcon)
2325   {
2326     item.mask |= TCIF_IMAGE;
2327     item.iImage = index;
2328   }
2329 
2330   SendMessageW(hwndTabControl, TCM_INSERTITEMW, index, (LPARAM)&item);
2331 
2332   /* If it is the only page - show it */
2333   if (psInfo->nPages == 1)
2334      PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
2335 
2336   return TRUE;
2337 }
2338 
2339 /******************************************************************************
2340  *            PROPSHEET_AddPage
2341  */
2342 static BOOL PROPSHEET_AddPage(HWND hwndDlg, HPROPSHEETPAGE hpage)
2343 {
2344   PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2345   TRACE("hwndDlg %p, hpage %p\n", hwndDlg, hpage);
2346   return PROPSHEET_InsertPage(hwndDlg, UlongToPtr(psInfo->nPages), hpage);
2347 }
2348 
2349 /******************************************************************************
2350  *            PROPSHEET_RemovePage
2351  */
2352 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
2353                                  int index,
2354                                  HPROPSHEETPAGE hpage)
2355 {
2356   PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2357   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
2358   PropPageInfo* oldPages;
2359 
2360   TRACE("index %d, hpage %p\n", index, hpage);
2361   if (!psInfo) {
2362     return FALSE;
2363   }
2364 
2365   index = PROPSHEET_GetPageIndex(hpage, psInfo, index);
2366 
2367   /* Make sure that index is within range */
2368   if (index < 0 || index >= psInfo->nPages)
2369   {
2370       TRACE("Could not find page to remove!\n");
2371       return FALSE;
2372   }
2373 
2374   TRACE("total pages %d removing page %d active page %d\n",
2375         psInfo->nPages, index, psInfo->active_page);
2376   /*
2377    * Check if we're removing the active page.
2378    */
2379   if (index == psInfo->active_page)
2380   {
2381     if (psInfo->nPages > 1)
2382     {
2383       if (index > 0)
2384       {
2385         /* activate previous page  */
2386         PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2387       }
2388       else
2389       {
2390         /* activate the next page */
2391         PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2392         psInfo->active_page = index;
2393       }
2394     }
2395     else
2396     {
2397       psInfo->active_page = -1;
2398       if (!psInfo->isModeless)
2399       {
2400          psInfo->ended = TRUE;
2401          return TRUE;
2402       }
2403     }
2404   }
2405   else if (index < psInfo->active_page)
2406     psInfo->active_page--;
2407 
2408   /* Unsubclass the page dialog window */
2409   if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD) &&
2410      (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2411      ((PROPSHEETPAGEW*)psInfo->proppage[index].hpage)->dwFlags & PSP_HIDEHEADER))
2412   {
2413      RemoveWindowSubclass(psInfo->proppage[index].hwndPage,
2414                           PROPSHEET_WizardSubclassProc, 1);
2415   }
2416 
2417   /* Destroy page dialog window */
2418   DestroyWindow(psInfo->proppage[index].hwndPage);
2419 
2420   /* Free page resources */
2421   if(psInfo->proppage[index].hpage)
2422   {
2423      PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)psInfo->proppage[index].hpage;
2424 
2425      if (psp->dwFlags & PSP_USETITLE)
2426         Free ((LPVOID)psInfo->proppage[index].pszText);
2427 
2428      DestroyPropertySheetPage(psInfo->proppage[index].hpage);
2429   }
2430 
2431   /* Remove the tab */
2432   SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2433 
2434   oldPages = psInfo->proppage;
2435   psInfo->nPages--;
2436   psInfo->proppage = Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2437 
2438   if (index > 0)
2439     memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
2440 
2441   if (index < psInfo->nPages)
2442     memcpy(&psInfo->proppage[index], &oldPages[index + 1],
2443            (psInfo->nPages - index) * sizeof(PropPageInfo));
2444 
2445   Free(oldPages);
2446 
2447   return FALSE;
2448 }
2449 
2450 /******************************************************************************
2451  *            PROPSHEET_SetWizButtons
2452  *
2453  * This code will work if (and assumes that) the Next button is on top of the
2454  * Finish button. ie. Finish comes after Next in the Z order.
2455  * This means make sure the dialog template reflects this.
2456  *
2457  */
2458 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
2459 {
2460   PropSheetInfo* psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2461   HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2462   HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2463   HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2464   BOOL enable_finish = ((dwFlags & PSWIZB_FINISH) || psInfo->hasFinish) && !(dwFlags & PSWIZB_DISABLEDFINISH);
2465 
2466 #ifdef __REACTOS__
2467   HWND hwndCancel = GetDlgItem(hwndDlg, IDCANCEL);
2468   INT iDefItem = 0;
2469   HWND hwndFocus;
2470 #endif
2471 
2472   TRACE("%d\n", dwFlags);
2473 
2474   EnableWindow(hwndBack, dwFlags & PSWIZB_BACK);
2475   EnableWindow(hwndNext, dwFlags & PSWIZB_NEXT);
2476   EnableWindow(hwndFinish, enable_finish);
2477 
2478 #ifndef __REACTOS__
2479   /* set the default pushbutton to an enabled button */
2480   if (enable_finish)
2481     SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2482   else if (dwFlags & PSWIZB_NEXT)
2483     SendMessageW(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
2484   else if (dwFlags & PSWIZB_BACK)
2485     SendMessageW(hwndDlg, DM_SETDEFID, IDC_BACK_BUTTON, 0);
2486   else
2487     SendMessageW(hwndDlg, DM_SETDEFID, IDCANCEL, 0);
2488 #endif
2489 
2490   if (!psInfo->hasFinish)
2491   {
2492     if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
2493     {
2494       /* Hide the Next button */
2495       ShowWindow(hwndNext, SW_HIDE);
2496 
2497       /* Show the Finish button */
2498       ShowWindow(hwndFinish, SW_SHOW);
2499     }
2500     else
2501     {
2502       /* Hide the Finish button */
2503       ShowWindow(hwndFinish, SW_HIDE);
2504       /* Show the Next button */
2505       ShowWindow(hwndNext, SW_SHOW);
2506     }
2507   }
2508 
2509 #ifdef __REACTOS__
2510   /* set the default pushbutton to an enabled button */
2511   if (((dwFlags & PSWIZB_FINISH) || psInfo->hasFinish) && !(dwFlags & PSWIZB_DISABLEDFINISH))
2512     iDefItem = IDC_FINISH_BUTTON;
2513   else if (dwFlags & PSWIZB_NEXT)
2514     iDefItem = IDC_NEXT_BUTTON;
2515   else if (dwFlags & PSWIZB_BACK)
2516     iDefItem = IDC_BACK_BUTTON;
2517   else
2518     iDefItem = IDCANCEL;
2519   SendMessageW(hwndDlg, DM_SETDEFID, iDefItem, 0);
2520 
2521   /* Set focus if no control has it */
2522   hwndFocus = GetFocus();
2523   if (!hwndFocus || hwndFocus == hwndCancel)
2524     SetFocus(GetDlgItem(hwndDlg, iDefItem));
2525 #endif
2526 
2527 }
2528 
2529 /******************************************************************************
2530  *            PROPSHEET_SetHeaderTitleW
2531  */
2532 static void PROPSHEET_SetHeaderTitleW(HWND hwndDlg, UINT page_index, const WCHAR *title)
2533 {
2534     PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2535     PROPSHEETPAGEW *page;
2536 
2537     TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_w(title));
2538 
2539     if (page_index >= psInfo->nPages)
2540         return;
2541 
2542     page = (PROPSHEETPAGEW *)psInfo->proppage[page_index].hpage;
2543 
2544     if (!IS_INTRESOURCE(page->pszHeaderTitle))
2545         Free((void *)page->pszHeaderTitle);
2546 
2547     page->pszHeaderTitle = heap_strdupW(title);
2548     page->dwFlags |= PSP_USEHEADERTITLE;
2549 }
2550 
2551 /******************************************************************************
2552  *            PROPSHEET_SetHeaderTitleA
2553  */
2554 static void PROPSHEET_SetHeaderTitleA(HWND hwndDlg, UINT page_index, const char *title)
2555 {
2556     WCHAR *titleW;
2557 
2558     TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_a(title));
2559 
2560     titleW = heap_strdupAtoW(title);
2561     PROPSHEET_SetHeaderTitleW(hwndDlg, page_index, titleW);
2562     Free(titleW);
2563 }
2564 
2565 /******************************************************************************
2566  *            PROPSHEET_SetHeaderSubTitleW
2567  */
2568 static void PROPSHEET_SetHeaderSubTitleW(HWND hwndDlg, UINT page_index, const WCHAR *subtitle)
2569 {
2570     PropSheetInfo *psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2571     PROPSHEETPAGEW *page;
2572 
2573     TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_w(subtitle));
2574 
2575     if (page_index >= psInfo->nPages)
2576         return;
2577 
2578     page = (PROPSHEETPAGEW *)psInfo->proppage[page_index].hpage;
2579 
2580     if (!IS_INTRESOURCE(page->pszHeaderSubTitle))
2581         Free((void *)page->pszHeaderSubTitle);
2582 
2583     page->pszHeaderSubTitle = heap_strdupW(subtitle);
2584     page->dwFlags |= PSP_USEHEADERSUBTITLE;
2585 }
2586 
2587 /******************************************************************************
2588  *            PROPSHEET_SetHeaderSubTitleA
2589  */
2590 static void PROPSHEET_SetHeaderSubTitleA(HWND hwndDlg, UINT page_index, const char *subtitle)
2591 {
2592     WCHAR *subtitleW;
2593 
2594     TRACE("(%p, %u, %s)\n", hwndDlg, page_index, debugstr_a(subtitle));
2595 
2596     subtitleW = heap_strdupAtoW(subtitle);
2597     PROPSHEET_SetHeaderSubTitleW(hwndDlg, page_index, subtitleW);
2598     Free(subtitleW);
2599 }
2600 
2601 /******************************************************************************
2602  *            PROPSHEET_HwndToIndex
2603  */
2604 static LRESULT PROPSHEET_HwndToIndex(HWND hwndDlg, HWND hPageDlg)
2605 {
2606     int index;
2607     PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2608 
2609     TRACE("(%p, %p)\n", hwndDlg, hPageDlg);
2610 
2611     for (index = 0; index < psInfo->nPages; index++)
2612         if (psInfo->proppage[index].hwndPage == hPageDlg)
2613             return index;
2614     WARN("%p not found\n", hPageDlg);
2615     return -1;
2616 }
2617 
2618 /******************************************************************************
2619  *            PROPSHEET_IndexToHwnd
2620  */
2621 static LRESULT PROPSHEET_IndexToHwnd(HWND hwndDlg, int iPageIndex)
2622 {
2623     PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2624     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2625     if (!psInfo)
2626         return 0;
2627     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2628         WARN("%d out of range.\n", iPageIndex);
2629 	return 0;
2630     }
2631     return (LRESULT)psInfo->proppage[iPageIndex].hwndPage;
2632 }
2633 
2634 /******************************************************************************
2635  *            PROPSHEET_PageToIndex
2636  */
2637 static LRESULT PROPSHEET_PageToIndex(HWND hwndDlg, HPROPSHEETPAGE hPage)
2638 {
2639     PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2640 
2641     TRACE("(%p, %p)\n", hwndDlg, hPage);
2642 
2643     return PROPSHEET_GetPageIndex(hPage, psInfo, -1);
2644 }
2645 
2646 /******************************************************************************
2647  *            PROPSHEET_IndexToPage
2648  */
2649 static LRESULT PROPSHEET_IndexToPage(HWND hwndDlg, int iPageIndex)
2650 {
2651     PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2652     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2653     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2654         WARN("%d out of range.\n", iPageIndex);
2655 	return 0;
2656     }
2657     return (LRESULT)psInfo->proppage[iPageIndex].hpage;
2658 }
2659 
2660 /******************************************************************************
2661  *            PROPSHEET_IdToIndex
2662  */
2663 static LRESULT PROPSHEET_IdToIndex(HWND hwndDlg, int iPageId)
2664 {
2665     int index;
2666     LPCPROPSHEETPAGEW psp;
2667     PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2668     TRACE("(%p, %d)\n", hwndDlg, iPageId);
2669     for (index = 0; index < psInfo->nPages; index++) {
2670         psp = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
2671         if (psp->u.pszTemplate == MAKEINTRESOURCEW(iPageId))
2672             return index;
2673     }
2674 
2675     return -1;
2676 }
2677 
2678 /******************************************************************************
2679  *            PROPSHEET_IndexToId
2680  */
2681 static LRESULT PROPSHEET_IndexToId(HWND hwndDlg, int iPageIndex)
2682 {
2683     PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2684     LPCPROPSHEETPAGEW psp;
2685     TRACE("(%p, %d)\n", hwndDlg, iPageIndex);
2686     if (iPageIndex<0 || iPageIndex>=psInfo->nPages) {
2687         WARN("%d out of range.\n", iPageIndex);
2688 	return 0;
2689     }
2690     psp = (LPCPROPSHEETPAGEW)psInfo->proppage[iPageIndex].hpage;
2691     if (psp->dwFlags & PSP_DLGINDIRECT || !IS_INTRESOURCE(psp->u.pszTemplate)) {
2692         return 0;
2693     }
2694     return (LRESULT)psp->u.pszTemplate;
2695 }
2696 
2697 /******************************************************************************
2698  *            PROPSHEET_GetResult
2699  */
2700 static LRESULT PROPSHEET_GetResult(HWND hwndDlg)
2701 {
2702     PropSheetInfo * psInfo = GetPropW(hwndDlg, PropSheetInfoStr);
2703     return psInfo->result;
2704 }
2705 
2706 /******************************************************************************
2707  *            PROPSHEET_RecalcPageSizes
2708  */
2709 static BOOL PROPSHEET_RecalcPageSizes(HWND hwndDlg)
2710 {
2711     FIXME("(%p): stub\n", hwndDlg);
2712     return FALSE;
2713 }
2714 
2715 /******************************************************************************
2716  *            PROPSHEET_GetPageIndex
2717  *
2718  * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
2719  * the array of PropPageInfo. If page is not found original index is used
2720  * (page takes precedence over index).
2721  */
2722 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE page, const PropSheetInfo* psInfo, int original_index)
2723 {
2724     int index;
2725 
2726     TRACE("page %p index %d\n", page, original_index);
2727 
2728     for (index = 0; index < psInfo->nPages; index++)
2729         if (psInfo->proppage[index].hpage == page)
2730             return index;
2731 
2732     return original_index;
2733 }
2734 
2735 /******************************************************************************
2736  *            PROPSHEET_CleanUp
2737  */
2738 static void PROPSHEET_CleanUp(HWND hwndDlg)
2739 {
2740   int i;
2741   PropSheetInfo* psInfo = RemovePropW(hwndDlg, PropSheetInfoStr);
2742 
2743   TRACE("\n");
2744   if (!psInfo) return;
2745   if (!IS_INTRESOURCE(psInfo->ppshheader.pszCaption))
2746       Free ((LPVOID)psInfo->ppshheader.pszCaption);
2747 
2748   for (i = 0; i < psInfo->nPages; i++)
2749   {
2750      PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
2751 
2752      /* Unsubclass the page dialog window */
2753      if((psInfo->ppshheader.dwFlags & (PSH_WIZARD97_NEW | PSH_WIZARD97_OLD)) &&
2754         (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2755         (psp->dwFlags & PSP_HIDEHEADER))
2756      {
2757         RemoveWindowSubclass(psInfo->proppage[i].hwndPage,
2758                              PROPSHEET_WizardSubclassProc, 1);
2759      }
2760 
2761      if(psInfo->proppage[i].hwndPage)
2762         DestroyWindow(psInfo->proppage[i].hwndPage);
2763 
2764      if(psp)
2765      {
2766         if (psp->dwFlags & PSP_USETITLE)
2767            Free ((LPVOID)psInfo->proppage[i].pszText);
2768 
2769         DestroyPropertySheetPage(psInfo->proppage[i].hpage);
2770      }
2771   }
2772 
2773   DeleteObject(psInfo->hFont);
2774   DeleteObject(psInfo->hFontBold);
2775   /* If we created the bitmaps, destroy them */
2776   if ((psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
2777       (!(psInfo->ppshheader.dwFlags & PSH_USEHBMWATERMARK)) )
2778       DeleteObject(psInfo->ppshheader.u4.hbmWatermark);
2779   if ((psInfo->ppshheader.dwFlags & PSH_HEADER) &&
2780       (!(psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER)) )
2781       DeleteObject(psInfo->ppshheader.u5.hbmHeader);
2782 
2783   Free(psInfo->proppage);
2784   Free(psInfo->strPropertiesFor);
2785   ImageList_Destroy(psInfo->hImageList);
2786 
2787   GlobalFree(psInfo);
2788 }
2789 
2790 static INT do_loop(const PropSheetInfo *psInfo)
2791 {
2792     MSG msg;
2793     INT ret = -1;
2794     HWND hwnd = psInfo->hwnd;
2795     HWND parent = psInfo->ppshheader.hwndParent;
2796 
2797     while(IsWindow(hwnd) && !psInfo->ended && (ret = GetMessageW(&msg, NULL, 0, 0)))
2798     {
2799         if(ret == -1)
2800             break;
2801 
2802         if(!IsDialogMessageW(hwnd, &msg))
2803         {
2804             TranslateMessage(&msg);
2805             DispatchMessageW(&msg);
2806         }
2807     }
2808 
2809     if(ret == 0)
2810     {
2811         PostQuitMessage(msg.wParam);
2812         ret = -1;
2813     }
2814 
2815     if(ret != -1)
2816         ret = psInfo->result;
2817 
2818     if(parent)
2819         EnableWindow(parent, TRUE);
2820 
2821     DestroyWindow(hwnd);
2822     return ret;
2823 }
2824 
2825 /******************************************************************************
2826  *            PROPSHEET_PropertySheet
2827  *
2828  * Common code between PropertySheetA/W
2829  */
2830 static INT_PTR PROPSHEET_PropertySheet(PropSheetInfo* psInfo, BOOL unicode)
2831 {
2832   INT_PTR bRet = 0;
2833   HWND parent = NULL;
2834   if (psInfo->active_page >= psInfo->nPages) psInfo->active_page = 0;
2835   TRACE("startpage: %d of %d pages\n", psInfo->active_page, psInfo->nPages);
2836 
2837   psInfo->unicode = unicode;
2838   psInfo->ended = FALSE;
2839 
2840   if(!psInfo->isModeless)
2841   {
2842       parent = psInfo->ppshheader.hwndParent;
2843       if (parent) EnableWindow(parent, FALSE);
2844   }
2845   bRet = PROPSHEET_CreateDialog(psInfo);
2846   if(!psInfo->isModeless)
2847       bRet = do_loop(psInfo);
2848   return bRet;
2849 }
2850 
2851 /******************************************************************************
2852  *            PropertySheet    (COMCTL32.@)
2853  *            PropertySheetA   (COMCTL32.@)
2854  *
2855  * Creates a property sheet in the specified property sheet header.
2856  *
2857  * RETURNS
2858  *     Modal property sheets: Positive if successful or -1 otherwise.
2859  *     Modeless property sheets: Property sheet handle.
2860  *     Or:
2861  *| ID_PSREBOOTSYSTEM - The user must reboot the computer for the changes to take effect.
2862  *| ID_PSRESTARTWINDOWS - The user must restart Windows for the changes to take effect.
2863  */
2864 INT_PTR WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2865 {
2866   PropSheetInfo* psInfo = GlobalAlloc(GPTR, sizeof(PropSheetInfo));
2867   UINT i, n;
2868   const BYTE* pByte;
2869 
2870   TRACE("(%p)\n", lppsh);
2871 
2872   PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2873 
2874   psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2875   pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2876 
2877   for (n = i = 0; i < lppsh->nPages; i++, n++)
2878   {
2879     if (!psInfo->usePropPage)
2880       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2881     else
2882     {
2883        psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
2884        pByte += ((LPCPROPSHEETPAGEA)pByte)->dwSize;
2885     }
2886 
2887     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2888                                psInfo, n, TRUE))
2889     {
2890 	if (psInfo->usePropPage)
2891 	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2892 	n--;
2893 	psInfo->nPages--;
2894     }
2895   }
2896 
2897   return PROPSHEET_PropertySheet(psInfo, FALSE);
2898 }
2899 
2900 /******************************************************************************
2901  *            PropertySheetW   (COMCTL32.@)
2902  *
2903  * See PropertySheetA.
2904  */
2905 INT_PTR WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2906 {
2907   PropSheetInfo* psInfo = GlobalAlloc(GPTR, sizeof(PropSheetInfo));
2908   UINT i, n;
2909   const BYTE* pByte;
2910 
2911   TRACE("(%p)\n", lppsh);
2912 
2913   PROPSHEET_CollectSheetInfoW(lppsh, psInfo);
2914 
2915   psInfo->proppage = Alloc(sizeof(PropPageInfo) * lppsh->nPages);
2916   pByte = (const BYTE*) psInfo->ppshheader.u3.ppsp;
2917 
2918   for (n = i = 0; i < lppsh->nPages; i++, n++)
2919   {
2920     if (!psInfo->usePropPage)
2921       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2922     else
2923     {
2924        psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2925        pByte += ((LPCPROPSHEETPAGEW)pByte)->dwSize;
2926     }
2927 
2928     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2929                                psInfo, n, TRUE))
2930     {
2931 	if (psInfo->usePropPage)
2932 	    DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2933 	n--;
2934 	psInfo->nPages--;
2935     }
2936   }
2937 
2938   return PROPSHEET_PropertySheet(psInfo, TRUE);
2939 }
2940 
2941 static LPWSTR load_string( HINSTANCE instance, LPCWSTR str )
2942 {
2943     LPWSTR ret;
2944 
2945     if (IS_INTRESOURCE(str))
2946     {
2947         HRSRC hrsrc;
2948         HGLOBAL hmem;
2949         WCHAR *ptr;
2950         WORD i, id = LOWORD(str);
2951         UINT len;
2952 
2953         if (!(hrsrc = FindResourceW( instance, MAKEINTRESOURCEW((id >> 4) + 1), (LPWSTR)RT_STRING )))
2954             return NULL;
2955         if (!(hmem = LoadResource( instance, hrsrc ))) return NULL;
2956         if (!(ptr = LockResource( hmem ))) return NULL;
2957         for (i = id & 0x0f; i > 0; i--) ptr += *ptr + 1;
2958         len = *ptr;
2959         if (!len) return NULL;
2960         ret = Alloc( (len + 1) * sizeof(WCHAR) );
2961         if (ret)
2962         {
2963             memcpy( ret, ptr + 1, len * sizeof(WCHAR) );
2964             ret[len] = 0;
2965         }
2966     }
2967     else
2968     {
2969         int len = (strlenW(str) + 1) * sizeof(WCHAR);
2970         ret = Alloc( len );
2971         if (ret) memcpy( ret, str, len );
2972     }
2973     return ret;
2974 }
2975 
2976 
2977 /******************************************************************************
2978  *            CreatePropertySheetPage    (COMCTL32.@)
2979  *            CreatePropertySheetPageA   (COMCTL32.@)
2980  *
2981  * Creates a new property sheet page.
2982  *
2983  * RETURNS
2984  *     Success: Handle to new property sheet page.
2985  *     Failure: NULL.
2986  *
2987  * NOTES
2988  *     An application must use the PSM_ADDPAGE message to add the new page to
2989  *     an existing property sheet.
2990  */
2991 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
2992                           LPCPROPSHEETPAGEA lpPropSheetPage)
2993 {
2994     PROPSHEETPAGEW *ppsp;
2995 
2996     if (lpPropSheetPage->dwSize < PROPSHEETPAGEA_V1_SIZE)
2997         return NULL;
2998 
2999     /* original data is used for callback notifications */
3000     if ((lpPropSheetPage->dwFlags & PSP_USECALLBACK) && lpPropSheetPage->pfnCallback)
3001     {
3002         ppsp = Alloc(2 * sizeof(*ppsp));
3003         memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEA)));
3004         memcpy(ppsp + 1, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEA)));
3005     }
3006     else
3007     {
3008         ppsp = Alloc(sizeof(*ppsp));
3009         memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEA)));
3010     }
3011 
3012     ppsp->dwFlags &= ~PSP_INTERNAL_UNICODE;
3013 
3014     if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
3015     {
3016         if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
3017         {
3018             int len = strlen(lpPropSheetPage->u.pszTemplate) + 1;
3019             char *template = Alloc( len );
3020 
3021             ppsp->u.pszTemplate = (LPWSTR)strcpy( template, lpPropSheetPage->u.pszTemplate );
3022         }
3023     }
3024 
3025     if (ppsp->dwFlags & PSP_USEICONID)
3026     {
3027         if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
3028             ppsp->u2.pszIcon = heap_strdupAtoW( lpPropSheetPage->u2.pszIcon );
3029     }
3030 
3031     if (ppsp->dwFlags & PSP_USETITLE)
3032     {
3033         if (IS_INTRESOURCE( ppsp->pszTitle ))
3034             ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
3035         else
3036             ppsp->pszTitle = heap_strdupAtoW( lpPropSheetPage->pszTitle );
3037     }
3038     else
3039         ppsp->pszTitle = NULL;
3040 
3041     if (ppsp->dwFlags & PSP_HIDEHEADER)
3042         ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);
3043 
3044     if (ppsp->dwFlags & PSP_USEHEADERTITLE)
3045     {
3046         if (IS_INTRESOURCE( ppsp->pszHeaderTitle ))
3047             ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3048         else
3049             ppsp->pszHeaderTitle = heap_strdupAtoW( lpPropSheetPage->pszHeaderTitle );
3050     }
3051     else
3052         ppsp->pszHeaderTitle = NULL;
3053 
3054     if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3055     {
3056         if (IS_INTRESOURCE( ppsp->pszHeaderSubTitle ))
3057             ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3058         else
3059             ppsp->pszHeaderSubTitle = heap_strdupAtoW( lpPropSheetPage->pszHeaderSubTitle );
3060     }
3061     else
3062         ppsp->pszHeaderSubTitle = NULL;
3063 
3064     if ((ppsp->dwFlags & PSP_USECALLBACK) && ppsp->dwSize > PROPSHEETPAGEA_V1_SIZE && ppsp->pfnCallback)
3065         ppsp->pfnCallback(0, PSPCB_ADDREF, ppsp + 1);
3066 
3067     return (HPROPSHEETPAGE)ppsp;
3068 }
3069 
3070 /******************************************************************************
3071  *            CreatePropertySheetPageW   (COMCTL32.@)
3072  *
3073  * See CreatePropertySheetA.
3074  */
3075 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
3076 {
3077     PROPSHEETPAGEW *ppsp;
3078 
3079     if (lpPropSheetPage->dwSize < PROPSHEETPAGEW_V1_SIZE)
3080         return NULL;
3081 
3082     /* original data is used for callback notifications */
3083     if ((lpPropSheetPage->dwFlags & PSP_USECALLBACK) && lpPropSheetPage->pfnCallback)
3084     {
3085         ppsp = Alloc(2 * sizeof(*ppsp));
3086         memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEW)));
3087         memcpy(ppsp + 1, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEW)));
3088     }
3089     else
3090     {
3091         ppsp = Alloc(sizeof(*ppsp));
3092         memcpy(ppsp, lpPropSheetPage, min(lpPropSheetPage->dwSize, sizeof(PROPSHEETPAGEW)));
3093     }
3094 
3095     ppsp->dwFlags |= PSP_INTERNAL_UNICODE;
3096 
3097     if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) )
3098     {
3099         if (!IS_INTRESOURCE( ppsp->u.pszTemplate ))
3100             ppsp->u.pszTemplate = heap_strdupW( lpPropSheetPage->u.pszTemplate );
3101     }
3102 
3103     if ( ppsp->dwFlags & PSP_USEICONID )
3104     {
3105         if (!IS_INTRESOURCE( ppsp->u2.pszIcon ))
3106             ppsp->u2.pszIcon = heap_strdupW( lpPropSheetPage->u2.pszIcon );
3107     }
3108 
3109     if (ppsp->dwFlags & PSP_USETITLE)
3110         ppsp->pszTitle = load_string( ppsp->hInstance, ppsp->pszTitle );
3111     else
3112         ppsp->pszTitle = NULL;
3113 
3114     if (ppsp->dwFlags & PSP_HIDEHEADER)
3115         ppsp->dwFlags &= ~(PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE);
3116 
3117     if (ppsp->dwFlags & PSP_USEHEADERTITLE)
3118         ppsp->pszHeaderTitle = load_string( ppsp->hInstance, ppsp->pszHeaderTitle );
3119     else
3120         ppsp->pszHeaderTitle = NULL;
3121 
3122     if (ppsp->dwFlags & PSP_USEHEADERSUBTITLE)
3123         ppsp->pszHeaderSubTitle = load_string( ppsp->hInstance, ppsp->pszHeaderSubTitle );
3124     else
3125         ppsp->pszHeaderSubTitle = NULL;
3126 
3127     if ((ppsp->dwFlags & PSP_USECALLBACK) && ppsp->dwSize > PROPSHEETPAGEW_V1_SIZE && ppsp->pfnCallback)
3128         ppsp->pfnCallback(0, PSPCB_ADDREF, ppsp + 1);
3129 
3130     return (HPROPSHEETPAGE)ppsp;
3131 }
3132 
3133 /******************************************************************************
3134  *            DestroyPropertySheetPage   (COMCTL32.@)
3135  *
3136  * Destroys a property sheet page previously created with
3137  * CreatePropertySheetA() or CreatePropertySheetW() and frees the associated
3138  * memory.
3139  *
3140  * RETURNS
3141  *     Success: TRUE
3142  *     Failure: FALSE
3143  */
3144 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
3145 {
3146   PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
3147 
3148   if (!psp)
3149      return FALSE;
3150 
3151   if ((psp->dwFlags & PSP_USECALLBACK) && psp->pfnCallback)
3152      psp->pfnCallback(0, PSPCB_RELEASE, psp + 1);
3153 
3154   if (!(psp->dwFlags & PSP_DLGINDIRECT) && !IS_INTRESOURCE( psp->u.pszTemplate ))
3155      Free ((LPVOID)psp->u.pszTemplate);
3156 
3157   if ((psp->dwFlags & PSP_USEICONID) && !IS_INTRESOURCE( psp->u2.pszIcon ))
3158      Free ((LPVOID)psp->u2.pszIcon);
3159 
3160   if ((psp->dwFlags & PSP_USETITLE) && !IS_INTRESOURCE( psp->pszTitle ))
3161      Free ((LPVOID)psp->pszTitle);
3162 
3163   if ((psp->dwFlags & PSP_USEHEADERTITLE) && !IS_INTRESOURCE( psp->pszHeaderTitle ))
3164      Free ((LPVOID)psp->pszHeaderTitle);
3165 
3166   if ((psp->dwFlags & PSP_USEHEADERSUBTITLE) && !IS_INTRESOURCE( psp->pszHeaderSubTitle ))
3167      Free ((LPVOID)psp->pszHeaderSubTitle);
3168 
3169   Free(hPropPage);
3170 
3171   return TRUE;
3172 }
3173 
3174 /******************************************************************************
3175  *            PROPSHEET_IsDialogMessage
3176  */
3177 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
3178 {
3179    PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3180 
3181    TRACE("\n");
3182    if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
3183       return FALSE;
3184 
3185    if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
3186    {
3187       int new_page = 0;
3188       INT dlgCode = SendMessageW(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
3189 
3190       if (!(dlgCode & DLGC_WANTMESSAGE))
3191       {
3192          switch (lpMsg->wParam)
3193          {
3194             case VK_TAB:
3195                if (GetKeyState(VK_SHIFT) & 0x8000)
3196                    new_page = -1;
3197                 else
3198                    new_page = 1;
3199                break;
3200 
3201             case VK_NEXT:   new_page = 1;  break;
3202             case VK_PRIOR:  new_page = -1; break;
3203          }
3204       }
3205 
3206       if (new_page)
3207       {
3208          if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
3209          {
3210             new_page += psInfo->active_page;
3211 
3212             if (new_page < 0)
3213                new_page = psInfo->nPages - 1;
3214             else if (new_page >= psInfo->nPages)
3215                new_page = 0;
3216 
3217             PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
3218          }
3219 
3220          return TRUE;
3221       }
3222    }
3223 
3224    return IsDialogMessageW(hwnd, lpMsg);
3225 }
3226 
3227 /******************************************************************************
3228  *            PROPSHEET_DoCommand
3229  */
3230 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID)
3231 {
3232 
3233     switch (wID) {
3234 
3235     case IDOK:
3236     case IDC_APPLY_BUTTON:
3237 	{
3238 	    HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
3239 
3240 	    if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
3241 		break;
3242 
3243 	    if (wID == IDOK)
3244 		{
3245                     PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3246 
3247                     /* don't overwrite ID_PSRESTARTWINDOWS or ID_PSREBOOTSYSTEM */
3248                     if (psInfo->result == 0)
3249                         psInfo->result = IDOK;
3250 
3251 		    if (psInfo->isModeless)
3252 			psInfo->activeValid = FALSE;
3253 		    else
3254                         psInfo->ended = TRUE;
3255 		}
3256 	    else
3257 		EnableWindow(hwndApplyBtn, FALSE);
3258 
3259 	    break;
3260 	}
3261 
3262     case IDC_BACK_BUTTON:
3263 	PROPSHEET_Back(hwnd);
3264 	break;
3265 
3266     case IDC_NEXT_BUTTON:
3267 	PROPSHEET_Next(hwnd);
3268 	break;
3269 
3270     case IDC_FINISH_BUTTON:
3271 	PROPSHEET_Finish(hwnd);
3272 	break;
3273 
3274     case IDCANCEL:
3275 	PROPSHEET_Cancel(hwnd, 0);
3276 	break;
3277 
3278     case IDHELP:
3279 	PROPSHEET_Help(hwnd);
3280 	break;
3281 
3282     default:
3283         return FALSE;
3284     }
3285 
3286     return TRUE;
3287 }
3288 
3289 /******************************************************************************
3290  *            PROPSHEET_Paint
3291  */
3292 static LRESULT PROPSHEET_Paint(HWND hwnd, HDC hdcParam)
3293 {
3294     PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3295     PAINTSTRUCT ps;
3296     HDC hdc, hdcSrc;
3297     BITMAP bm;
3298     HBITMAP hbmp;
3299     HPALETTE hOldPal = 0;
3300     int offsety = 0;
3301     HBRUSH hbr;
3302     RECT r, rzone;
3303     LPCPROPSHEETPAGEW ppshpage;
3304     WCHAR szBuffer[256];
3305     int nLength;
3306 
3307     hdc = hdcParam ? hdcParam : BeginPaint(hwnd, &ps);
3308     if (!hdc) return 1;
3309 
3310     hdcSrc = CreateCompatibleDC(0);
3311 
3312     if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK)
3313 	hOldPal = SelectPalette(hdc, psInfo->ppshheader.hplWatermark, FALSE);
3314 
3315     if (psInfo->active_page < 0)
3316         ppshpage = NULL;
3317     else
3318         ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[psInfo->active_page].hpage;
3319 
3320     if ( (ppshpage && !(ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3321 	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
3322 	 (psInfo->ppshheader.dwFlags & PSH_HEADER) )
3323     {
3324 	HWND hwndLineHeader = GetDlgItem(hwnd, IDC_SUNKEN_LINEHEADER);
3325 	HFONT hOldFont;
3326 	COLORREF clrOld = 0;
3327 	int oldBkMode = 0;
3328 
3329         GetClientRect(hwndLineHeader, &r);
3330         MapWindowPoints(hwndLineHeader, hwnd, (LPPOINT) &r, 2);
3331         SetRect(&rzone, 0, 0, r.right + 1, r.top - 1);
3332 
3333         hOldFont = SelectObject(hdc, psInfo->hFontBold);
3334 
3335 #ifdef __REACTOS__
3336         if (psInfo->ppshheader.u5.hbmHeader)
3337 #else
3338         if (psInfo->ppshheader.dwFlags & PSH_USEHBMHEADER)
3339 #endif
3340         {
3341             hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u5.hbmHeader);
3342 
3343             GetObjectW(psInfo->ppshheader.u5.hbmHeader, sizeof(BITMAP), &bm);
3344             if (psInfo->ppshheader.dwFlags & PSH_WIZARD97_OLD)
3345             {
3346                 /* Fill the unoccupied part of the header with color of the
3347                  * left-top pixel, but do it only when needed.
3348                  */
3349                 if (bm.bmWidth < r.right || bm.bmHeight < r.bottom)
3350                 {
3351                     hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
3352                     r = rzone;
3353                     if (bm.bmWidth < r.right)
3354                     {
3355                         r.left = bm.bmWidth;
3356                         FillRect(hdc, &r, hbr);
3357                     }
3358                     if (bm.bmHeight < r.bottom)
3359                     {
3360                         r.left = 0;
3361                         r.top = bm.bmHeight;
3362                         FillRect(hdc, &r, hbr);
3363                     }
3364                     DeleteObject(hbr);
3365                 }
3366 
3367                 /* Draw the header itself. */
3368                 BitBlt(hdc, 0, 0, bm.bmWidth, min(bm.bmHeight, rzone.bottom),
3369                         hdcSrc, 0, 0, SRCCOPY);
3370             }
3371             else
3372             {
3373                 int margin;
3374                 hbr = GetSysColorBrush(COLOR_WINDOW);
3375                 FillRect(hdc, &rzone, hbr);
3376 
3377                 /* Draw the header bitmap. It's always centered like a
3378                  * common 49 x 49 bitmap. */
3379                 margin = (rzone.bottom - 49) / 2;
3380                 BitBlt(hdc, rzone.right - 49 - margin, margin,
3381                         min(bm.bmWidth, 49), min(bm.bmHeight, 49),
3382                         hdcSrc, 0, 0, SRCCOPY);
3383 
3384                 /* NOTE: Native COMCTL32 draws a white stripe over the bitmap
3385                  * if its height is smaller than 49 pixels. Because the reason
3386                  * for this bug is unknown the current code doesn't try to
3387                  * replicate it. */
3388             }
3389 
3390             SelectObject(hdcSrc, hbmp);
3391         }
3392 
3393 	clrOld = SetTextColor (hdc, 0x00000000);
3394 	oldBkMode = SetBkMode (hdc, TRANSPARENT);
3395 
3396 	if (ppshpage->dwFlags & PSP_USEHEADERTITLE) {
3397 	    SetRect(&r, 20, 10, 0, 0);
3398             if (!IS_INTRESOURCE(ppshpage->pszHeaderTitle))
3399                 DrawTextW(hdc, ppshpage->pszHeaderTitle, -1, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3400 	    else
3401 	    {
3402 		nLength = LoadStringW(ppshpage->hInstance, (UINT_PTR)ppshpage->pszHeaderTitle,
3403 				      szBuffer, 256);
3404 		if (nLength != 0)
3405 		{
3406 		    DrawTextW(hdc, szBuffer, nLength, &r, DT_LEFT | DT_SINGLELINE | DT_NOCLIP);
3407 		}
3408 	    }
3409 	}
3410 
3411 	if (ppshpage->dwFlags & PSP_USEHEADERSUBTITLE) {
3412 	    SelectObject(hdc, psInfo->hFont);
3413 	    SetRect(&r, 40, 25, rzone.right - 69, rzone.bottom);
3414 #ifdef __REACTOS__
3415             if (!IS_INTRESOURCE(ppshpage->pszHeaderSubTitle))
3416 #else
3417             if (!IS_INTRESOURCE(ppshpage->pszHeaderTitle))
3418 #endif
3419                 DrawTextW(hdc, ppshpage->pszHeaderSubTitle, -1, &r, DT_LEFT | DT_WORDBREAK);
3420 	    else
3421 	    {
3422 		nLength = LoadStringW(ppshpage->hInstance, (UINT_PTR)ppshpage->pszHeaderSubTitle,
3423 				      szBuffer, 256);
3424 		if (nLength != 0)
3425 		{
3426 		    DrawTextW(hdc, szBuffer, nLength, &r, DT_LEFT | DT_WORDBREAK);
3427 		}
3428 	    }
3429 	}
3430 
3431 	offsety = rzone.bottom + 2;
3432 
3433 	SetTextColor(hdc, clrOld);
3434 	SetBkMode(hdc, oldBkMode);
3435 	SelectObject(hdc, hOldFont);
3436     }
3437 
3438     if ( (ppshpage && (ppshpage->dwFlags & PSP_HIDEHEADER)) &&
3439 	 (psInfo->ppshheader.dwFlags & (PSH_WIZARD97_OLD | PSH_WIZARD97_NEW)) &&
3440 #ifdef __REACTOS__
3441 	 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) &&
3442 	 (psInfo->ppshheader.u4.hbmWatermark) )
3443 #else
3444 	 (psInfo->ppshheader.dwFlags & PSH_WATERMARK) )
3445 #endif
3446     {
3447 	HWND hwndLine = GetDlgItem(hwnd, IDC_SUNKEN_LINE);
3448 
3449 	GetClientRect(hwndLine, &r);
3450 	MapWindowPoints(hwndLine, hwnd, (LPPOINT) &r, 2);
3451         SetRect(&rzone, 0, 0, r.right, r.top - 1);
3452 
3453 	hbr = GetSysColorBrush(COLOR_WINDOW);
3454 	FillRect(hdc, &rzone, hbr);
3455 
3456 	GetObjectW(psInfo->ppshheader.u4.hbmWatermark, sizeof(BITMAP), &bm);
3457 	hbmp = SelectObject(hdcSrc, psInfo->ppshheader.u4.hbmWatermark);
3458 
3459         /* The watermark is truncated to a width of 164 pixels */
3460         r.right = min(r.right, 164);
3461 	BitBlt(hdc, 0, offsety, min(bm.bmWidth, r.right),
3462 	       min(bm.bmHeight, r.bottom), hdcSrc, 0, 0, SRCCOPY);
3463 
3464 	/* If the bitmap is not big enough, fill the remaining area
3465 	   with the color of pixel (0,0) of bitmap - see MSDN */
3466 	if (r.top > bm.bmHeight) {
3467 	    r.bottom = r.top - 1;
3468 	    r.top = bm.bmHeight;
3469 	    r.left = 0;
3470 	    r.right = bm.bmWidth;
3471 	    hbr = CreateSolidBrush(GetPixel(hdcSrc, 0, 0));
3472 	    FillRect(hdc, &r, hbr);
3473 	    DeleteObject(hbr);
3474 	}
3475 
3476 	SelectObject(hdcSrc, hbmp);
3477     }
3478 
3479     if (psInfo->ppshheader.dwFlags & PSH_USEHPLWATERMARK)
3480 	SelectPalette(hdc, hOldPal, FALSE);
3481 
3482     DeleteDC(hdcSrc);
3483 
3484     if (!hdcParam) EndPaint(hwnd, &ps);
3485 
3486     return 0;
3487 }
3488 
3489 /******************************************************************************
3490  *            PROPSHEET_DialogProc
3491  */
3492 static INT_PTR CALLBACK
3493 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3494 {
3495   TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n",
3496 	hwnd, uMsg, wParam, lParam);
3497 
3498   switch (uMsg)
3499   {
3500     case WM_INITDIALOG:
3501     {
3502       PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
3503       WCHAR* strCaption = Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
3504       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
3505       int idx;
3506       LOGFONTW logFont;
3507 
3508       /* Using PropSheetInfoStr to store extra data doesn't match the native
3509        * common control: native uses TCM_[GS]ETITEM
3510        */
3511       SetPropW(hwnd, PropSheetInfoStr, psInfo);
3512 
3513       /*
3514        * psInfo->hwnd is not being used by WINE code - it exists
3515        * for compatibility with "real" Windoze. The same about
3516        * SetWindowLongPtr - WINE is only using the PropSheetInfoStr
3517        * property.
3518        */
3519       psInfo->hwnd = hwnd;
3520       SetWindowLongPtrW(hwnd, DWLP_USER, (DWORD_PTR)psInfo);
3521 
3522       if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3523       {
3524         /* set up the Next and Back buttons by default */
3525         PROPSHEET_SetWizButtons(hwnd, PSWIZB_BACK|PSWIZB_NEXT);
3526       }
3527 
3528       /* Set up fonts */
3529       SystemParametersInfoW (SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
3530       psInfo->hFont = CreateFontIndirectW (&logFont);
3531       logFont.lfWeight = FW_BOLD;
3532       psInfo->hFontBold = CreateFontIndirectW (&logFont);
3533 
3534       /*
3535        * Small icon in the title bar.
3536        */
3537       if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
3538           (psInfo->ppshheader.dwFlags & PSH_USEHICON))
3539       {
3540         HICON hIcon;
3541         int icon_cx = GetSystemMetrics(SM_CXSMICON);
3542         int icon_cy = GetSystemMetrics(SM_CYSMICON);
3543 
3544         if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
3545           hIcon = LoadImageW(psInfo->ppshheader.hInstance,
3546                              psInfo->ppshheader.u.pszIcon,
3547                              IMAGE_ICON,
3548                              icon_cx, icon_cy,
3549                              LR_DEFAULTCOLOR);
3550         else
3551           hIcon = psInfo->ppshheader.u.hIcon;
3552 
3553         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)hIcon);
3554       }
3555 
3556       if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
3557         SendMessageW(hwnd, WM_SETICON, 0, (LPARAM)psInfo->ppshheader.u.hIcon);
3558 
3559       psInfo->strPropertiesFor = strCaption;
3560 
3561       GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
3562 
3563       PROPSHEET_CreateTabControl(hwnd, psInfo);
3564 
3565       PROPSHEET_LoadWizardBitmaps(psInfo);
3566 
3567       if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3568       {
3569         ShowWindow(hwndTabCtrl, SW_HIDE);
3570         PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
3571         PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
3572         SetFocus(GetDlgItem(hwnd, IDC_NEXT_BUTTON));
3573       }
3574       else
3575       {
3576         if (PROPSHEET_SizeMismatch(hwnd, psInfo))
3577         {
3578           PROPSHEET_AdjustSize(hwnd, psInfo);
3579           PROPSHEET_AdjustButtons(hwnd, psInfo);
3580         }
3581         SetFocus(GetDlgItem(hwnd, IDOK));
3582       }
3583 #ifdef __REACTOS__
3584       /* Move the window position if necessary */
3585       {
3586           INT dx, dy;
3587           RECT rcInit;
3588           HWND hwndParent = psInfo->ppshheader.hwndParent;
3589           BOOL bMove = FALSE;
3590 
3591           GetWindowRect(hwnd, &rcInit);
3592           dx = rcInit.right - rcInit.left;
3593           dy = rcInit.bottom - rcInit.top;
3594 
3595           if (IsWindow(hwndParent))
3596           {
3597               WINDOWPLACEMENT wndpl = { sizeof(wndpl) };
3598               bMove = TRUE;
3599 
3600               /* hwndParent can be minimized (See Control_ShowAppletInTaskbar).
3601                  Use normal position. */
3602               GetWindowPlacement(hwndParent, &wndpl);
3603               rcInit = wndpl.rcNormalPosition;
3604               if (IsWindowVisible(hwndParent) && !IsIconic(hwndParent))
3605               {
3606                   /* Is it Right-to-Left layout? */
3607                   if (GetWindowLongPtrW(hwndParent, GWL_EXSTYLE) & WS_EX_RTLREADING)
3608                       rcInit.left = rcInit.right - dx - GetSystemMetrics(SM_CXSMICON);
3609                   else
3610                       rcInit.left += GetSystemMetrics(SM_CXSMICON);
3611 
3612                   rcInit.top += GetSystemMetrics(SM_CYSMICON);
3613               }
3614           }
3615           else
3616           {
3617               /* We cannot foresee CW_USEDEFAULT's position without communicating with USER32.
3618                  Use a top-level STATIC control to get the proper position. */
3619               HWND hwndDummy = CreateWindowExW(0, WC_STATICW, NULL, 0,
3620                                                CW_USEDEFAULT, CW_USEDEFAULT, dx, dy,
3621                                                NULL, NULL, GetModuleHandleW(NULL), NULL);
3622               if (hwndDummy)
3623               {
3624                   bMove = TRUE;
3625                   GetWindowRect(hwndDummy, &rcInit);
3626                   DestroyWindow(hwndDummy);
3627               }
3628           }
3629 
3630           if (bMove)
3631           {
3632               MONITORINFO mi = { sizeof(mi) };
3633               HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
3634               if (GetMonitorInfo(hMonitor, &mi))
3635               {
3636                   /* Try to fit it onto the desktop */
3637                   if (mi.rcWork.right < rcInit.left + dx)
3638                       rcInit.left = mi.rcWork.right - dx;
3639                   if (mi.rcWork.bottom < rcInit.top + dy)
3640                       rcInit.top = mi.rcWork.bottom - dy;
3641                   if (rcInit.left < mi.rcWork.left)
3642                       rcInit.left = mi.rcWork.left;
3643                   if (rcInit.top < mi.rcWork.top)
3644                       rcInit.top = mi.rcWork.top;
3645                   SetWindowPos(hwnd, NULL, rcInit.left, rcInit.top, 0, 0,
3646                                SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
3647               }
3648           }
3649       }
3650 #endif
3651 
3652       if (IS_INTRESOURCE(psInfo->ppshheader.pszCaption) &&
3653               psInfo->ppshheader.hInstance)
3654       {
3655          WCHAR szText[256];
3656 
3657          if (LoadStringW(psInfo->ppshheader.hInstance,
3658                          (UINT_PTR)psInfo->ppshheader.pszCaption, szText, 255))
3659             PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
3660       }
3661       else
3662       {
3663          PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
3664                          psInfo->ppshheader.pszCaption);
3665       }
3666 
3667 
3668       if (psInfo->useCallback)
3669              (*(psInfo->ppshheader.pfnCallback))(hwnd, PSCB_INITIALIZED, 0);
3670 
3671       idx = psInfo->active_page;
3672       psInfo->active_page = -1;
3673 
3674       PROPSHEET_SetCurSel(hwnd, idx, 1, psInfo->proppage[idx].hpage);
3675 
3676       /* doing TCM_SETCURSEL seems to be needed even in case of PSH_WIZARD,
3677        * as some programs call TCM_GETCURSEL to get the current selection
3678        * from which to switch to the next page */
3679       SendMessageW(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
3680 
3681       PROPSHEET_UnChanged(hwnd, NULL);
3682 
3683       /* wizards set their focus during init */
3684       if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
3685           return FALSE;
3686 
3687       return TRUE;
3688     }
3689 
3690     case WM_PRINTCLIENT:
3691     case WM_PAINT:
3692       PROPSHEET_Paint(hwnd, (HDC)wParam);
3693       return TRUE;
3694 
3695     case WM_DESTROY:
3696       PROPSHEET_CleanUp(hwnd);
3697       return TRUE;
3698 
3699     case WM_CLOSE:
3700       PROPSHEET_Cancel(hwnd, 1);
3701       return FALSE; /* let DefDlgProc post us WM_COMMAND/IDCANCEL */
3702 
3703     case WM_COMMAND:
3704       if (!PROPSHEET_DoCommand(hwnd, LOWORD(wParam)))
3705       {
3706           PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3707 
3708           if (!psInfo)
3709               return FALSE;
3710 
3711           /* No default handler, forward notification to active page */
3712           if (psInfo->activeValid && psInfo->active_page != -1)
3713           {
3714              HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
3715              SendMessageW(hwndPage, WM_COMMAND, wParam, lParam);
3716           }
3717       }
3718       return TRUE;
3719 
3720     case WM_NOTIFY:
3721     {
3722       NMHDR* pnmh = (LPNMHDR) lParam;
3723 
3724       if (pnmh->code == TCN_SELCHANGE)
3725       {
3726         int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
3727         PROPSHEET_SetCurSel(hwnd, index, 1, 0);
3728       }
3729 
3730       if(pnmh->code == TCN_SELCHANGING)
3731       {
3732         BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
3733         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, !bRet);
3734         return TRUE;
3735       }
3736 
3737       return FALSE;
3738     }
3739 
3740     case WM_SYSCOLORCHANGE:
3741       COMCTL32_RefreshSysColors();
3742       return FALSE;
3743 
3744     case PSM_GETCURRENTPAGEHWND:
3745     {
3746       PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3747       HWND hwndPage = 0;
3748 
3749       if (!psInfo)
3750         return FALSE;
3751 
3752       if (psInfo->activeValid && psInfo->active_page != -1)
3753         hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
3754 
3755       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndPage);
3756 
3757       return TRUE;
3758     }
3759 
3760     case PSM_CHANGED:
3761       PROPSHEET_Changed(hwnd, (HWND)wParam);
3762       return TRUE;
3763 
3764     case PSM_UNCHANGED:
3765       PROPSHEET_UnChanged(hwnd, (HWND)wParam);
3766       return TRUE;
3767 
3768     case PSM_GETTABCONTROL:
3769     {
3770       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
3771 
3772       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (DWORD_PTR)hwndTabCtrl);
3773 
3774       return TRUE;
3775     }
3776 
3777     case PSM_SETCURSEL:
3778     {
3779       BOOL msgResult;
3780 
3781       msgResult = PROPSHEET_CanSetCurSel(hwnd);
3782       if(msgResult != FALSE)
3783       {
3784         msgResult = PROPSHEET_SetCurSel(hwnd,
3785                                        (int)wParam,
3786 				       1,
3787                                        (HPROPSHEETPAGE)lParam);
3788       }
3789 
3790       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3791 
3792       return TRUE;
3793     }
3794 
3795     case PSM_CANCELTOCLOSE:
3796     {
3797       WCHAR buf[MAX_BUTTONTEXT_LENGTH];
3798       HWND hwndOK = GetDlgItem(hwnd, IDOK);
3799       HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
3800 
3801       EnableWindow(hwndCancel, FALSE);
3802       if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, ARRAY_SIZE(buf)))
3803          SetWindowTextW(hwndOK, buf);
3804 
3805       return FALSE;
3806     }
3807 
3808     case PSM_RESTARTWINDOWS:
3809     {
3810       PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3811 
3812       if (!psInfo)
3813         return FALSE;
3814 
3815       /* reboot system takes precedence over restart windows */
3816       if (psInfo->result != ID_PSREBOOTSYSTEM)
3817           psInfo->result = ID_PSRESTARTWINDOWS;
3818 
3819       return TRUE;
3820     }
3821 
3822     case PSM_REBOOTSYSTEM:
3823     {
3824       PropSheetInfo* psInfo = GetPropW(hwnd, PropSheetInfoStr);
3825 
3826       if (!psInfo)
3827         return FALSE;
3828 
3829       psInfo->result = ID_PSREBOOTSYSTEM;
3830 
3831       return TRUE;
3832     }
3833 
3834     case PSM_SETTITLEA:
3835       PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
3836       return TRUE;
3837 
3838     case PSM_SETTITLEW:
3839       PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
3840       return TRUE;
3841 
3842     case PSM_APPLY:
3843     {
3844       BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
3845 
3846       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3847 
3848       return TRUE;
3849     }
3850 
3851     case PSM_QUERYSIBLINGS:
3852     {
3853       LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
3854 
3855       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3856 
3857       return TRUE;
3858     }
3859 
3860     case PSM_ADDPAGE:
3861     {
3862       /*
3863        * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have
3864        *       a return value. This is not true. PSM_ADDPAGE returns TRUE
3865        *       on success or FALSE otherwise, as specified on MSDN Online.
3866        *       Also see the MFC code for
3867        *       CPropertySheet::AddPage(CPropertyPage* pPage).
3868        */
3869 
3870       BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
3871 
3872       SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3873 
3874       return TRUE;
3875     }
3876 
3877     case PSM_REMOVEPAGE:
3878       PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
3879       return TRUE;
3880 
3881     case PSM_ISDIALOGMESSAGE:
3882     {
3883        BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
3884        SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3885        return TRUE;
3886     }
3887 
3888     case PSM_PRESSBUTTON:
3889       PROPSHEET_PressButton(hwnd, (int)wParam);
3890       return TRUE;
3891 
3892     case PSM_SETFINISHTEXTA:
3893       PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
3894       return TRUE;
3895 
3896     case PSM_SETWIZBUTTONS:
3897       PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
3898       return TRUE;
3899 
3900     case PSM_SETCURSELID:
3901         PROPSHEET_SetCurSelId(hwnd, (int)lParam);
3902         return TRUE;
3903 
3904     case PSM_SETFINISHTEXTW:
3905         PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
3906         return FALSE;
3907 
3908     case PSM_INSERTPAGE:
3909     {
3910         BOOL msgResult = PROPSHEET_InsertPage(hwnd, (HPROPSHEETPAGE)wParam, (HPROPSHEETPAGE)lParam);
3911         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3912         return TRUE;
3913     }
3914 
3915     case PSM_SETHEADERTITLEW:
3916         PROPSHEET_SetHeaderTitleW(hwnd, wParam, (LPCWSTR)lParam);
3917         return TRUE;
3918 
3919     case PSM_SETHEADERTITLEA:
3920         PROPSHEET_SetHeaderTitleA(hwnd, wParam, (LPCSTR)lParam);
3921         return TRUE;
3922 
3923     case PSM_SETHEADERSUBTITLEW:
3924         PROPSHEET_SetHeaderSubTitleW(hwnd, wParam, (LPCWSTR)lParam);
3925         return TRUE;
3926 
3927     case PSM_SETHEADERSUBTITLEA:
3928         PROPSHEET_SetHeaderSubTitleA(hwnd, wParam, (LPCSTR)lParam);
3929         return TRUE;
3930 
3931     case PSM_HWNDTOINDEX:
3932     {
3933         LRESULT msgResult = PROPSHEET_HwndToIndex(hwnd, (HWND)wParam);
3934         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3935         return TRUE;
3936     }
3937 
3938     case PSM_INDEXTOHWND:
3939     {
3940         LRESULT msgResult = PROPSHEET_IndexToHwnd(hwnd, (int)wParam);
3941         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3942         return TRUE;
3943     }
3944 
3945     case PSM_PAGETOINDEX:
3946     {
3947         LRESULT msgResult = PROPSHEET_PageToIndex(hwnd, (HPROPSHEETPAGE)wParam);
3948         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3949         return TRUE;
3950     }
3951 
3952     case PSM_INDEXTOPAGE:
3953     {
3954         LRESULT msgResult = PROPSHEET_IndexToPage(hwnd, (int)wParam);
3955         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3956         return TRUE;
3957     }
3958 
3959     case PSM_IDTOINDEX:
3960     {
3961         LRESULT msgResult = PROPSHEET_IdToIndex(hwnd, (int)lParam);
3962         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3963         return TRUE;
3964     }
3965 
3966     case PSM_INDEXTOID:
3967     {
3968         LRESULT msgResult = PROPSHEET_IndexToId(hwnd, (int)wParam);
3969         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3970         return TRUE;
3971     }
3972 
3973     case PSM_GETRESULT:
3974     {
3975         LRESULT msgResult = PROPSHEET_GetResult(hwnd);
3976         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3977         return TRUE;
3978     }
3979 
3980     case PSM_RECALCPAGESIZES:
3981     {
3982         LRESULT msgResult = PROPSHEET_RecalcPageSizes(hwnd);
3983         SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, msgResult);
3984         return TRUE;
3985     }
3986 
3987     default:
3988       return FALSE;
3989   }
3990 }
3991