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