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