Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / comctl32 / propsheet.c
1 /*
2  * Property Sheets
3  *
4  * Copyright 1998 Francis Beaudet
5  * Copyright 1999 Thuy Nguyen
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * TODO:
22  *   - Tab order
23  *   - Unicode property sheets
24  */
25
26 #include <string.h>
27 #include "winbase.h"
28 #include "commctrl.h"
29 #include "prsht.h"
30 #include "winnls.h"
31 #include "comctl32.h"
32 #include "wine/debug.h"
33 #include "heap.h"
34
35
36 /******************************************************************************
37  * Data structures
38  */
39 typedef struct
40 {
41   WORD dlgVer;
42   WORD signature;
43   DWORD helpID;
44   DWORD exStyle;
45   DWORD style;
46 } MyDLGTEMPLATEEX;
47
48 typedef struct tagPropPageInfo
49 {
50   HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
51   HWND hwndPage;
52   BOOL isDirty;
53   LPCWSTR pszText;
54   BOOL hasHelp;
55   BOOL useCallback;
56   BOOL hasIcon;
57 } PropPageInfo;
58
59 typedef struct tagPropSheetInfo
60 {
61   HWND hwnd;
62   PROPSHEETHEADERW ppshheader;
63   LPWSTR strPropertiesFor;
64   int nPages;
65   int active_page;
66   BOOL isModeless;
67   BOOL hasHelp;
68   BOOL hasApply;
69   BOOL useCallback;
70   BOOL restartWindows;
71   BOOL rebootSystem;
72   BOOL activeValid;
73   PropPageInfo* proppage;
74   int x;
75   int y;
76   int width;
77   int height;
78   HIMAGELIST hImageList;
79 } PropSheetInfo;
80
81 typedef struct
82 {
83   int x;
84   int y;
85 } PADDING_INFO;
86
87 /******************************************************************************
88  * Defines and global variables
89  */
90
91 const WCHAR PropSheetInfoStr[] =
92     {'P','r','o','p','e','r','t','y','S','h','e','e','t','I','n','f','o',0 };
93
94 #define MAX_CAPTION_LENGTH 255
95 #define MAX_TABTEXT_LENGTH 255
96 #define MAX_BUTTONTEXT_LENGTH 64
97
98 #define PSH_WIZARD97_OLD   0x00002000
99 #define PSH_WIZARD97_NEW   0x01000000
100 #define INTRNL_ANY_WIZARD (PSH_WIZARD | PSH_WIZARD97_OLD | PSH_WIZARD97_NEW | PSH_WIZARD_LITE)
101
102 /******************************************************************************
103  * Prototypes
104  */
105 static BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo);
106 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo);
107 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo);
108 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo);
109 static BOOL PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
110                                        PropSheetInfo * psInfo);
111 static BOOL PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
112                                        PropSheetInfo * psInfo);
113 static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
114                                       PropSheetInfo * psInfo,
115                                       int index);
116 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
117                                        PropSheetInfo * psInfo);
118 static BOOL PROPSHEET_CreatePage(HWND hwndParent, int index,
119                                 const PropSheetInfo * psInfo,
120                                 LPCPROPSHEETPAGEW ppshpage);
121 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo);
122 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
123 static BOOL PROPSHEET_Back(HWND hwndDlg);
124 static BOOL PROPSHEET_Next(HWND hwndDlg);
125 static BOOL PROPSHEET_Finish(HWND hwndDlg);
126 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam);
127 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam);
128 static void PROPSHEET_Help(HWND hwndDlg);
129 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage);
130 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage);
131 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID);
132 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText);
133 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText);
134 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText);
135 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText);
136 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg);
137 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
138                                 int index,
139                                 int skipdir,
140                                 HPROPSHEETPAGE hpage);
141 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
142                                        WPARAM wParam, LPARAM lParam);
143 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
144                               HPROPSHEETPAGE hpage);
145
146 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
147                                  int index,
148                                  HPROPSHEETPAGE hpage);
149 static void PROPSHEET_CleanUp();
150 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo);
151 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags);
152 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo* psInfo);
153 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg);
154 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID);
155
156 BOOL WINAPI
157 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
158
159 WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
160
161 #define add_flag(a) if (dwFlags & a) {strcat(string, #a );strcat(string," ");}
162 /******************************************************************************
163  *            PROPSHEET_UnImplementedFlags
164  *
165  * Document use of flags we don't implement yet.
166  */
167 static VOID PROPSHEET_UnImplementedFlags(DWORD dwFlags)
168 {
169     CHAR string[256];
170
171     string[0] = '\0';
172
173   /*
174    * unhandled header flags:
175    *  PSH_DEFAULT            0x00000000
176    *  PSH_WIZARDHASFINISH    0x00000010
177    *  PSH_RTLREADING         0x00000800
178    *  PSH_WIZARDCONTEXTHELP  0x00001000
179    *  PSH_WIZARD97           0x00002000  (pre IE 5)
180    *  PSH_WATERMARK          0x00008000
181    *  PSH_USEHBMWATERMARK    0x00010000
182    *  PSH_USEHPLWATERMARK    0x00020000
183    *  PSH_STRETCHWATERMARK   0x00040000
184    *  PSH_HEADER             0x00080000
185    *  PSH_USEHBMHEADER       0x00100000
186    *  PSH_USEPAGELANG        0x00200000
187    *  PSH_WIZARD_LITE        0x00400000      also not in .h
188    *  PSH_WIZARD97           0x01000000  (IE 5 and above)
189    *  PSH_NOCONTEXTHELP      0x02000000      also not in .h
190    */
191
192     add_flag(PSH_WIZARDHASFINISH);
193     add_flag(PSH_RTLREADING);
194     add_flag(PSH_WIZARDCONTEXTHELP);
195     add_flag(PSH_WIZARD97_OLD);
196     add_flag(PSH_WATERMARK);
197     add_flag(PSH_USEHBMWATERMARK);
198     add_flag(PSH_USEHPLWATERMARK);
199     add_flag(PSH_STRETCHWATERMARK);
200     add_flag(PSH_HEADER);
201     add_flag(PSH_USEHBMHEADER);
202     add_flag(PSH_USEPAGELANG);
203     add_flag(PSH_WIZARD_LITE);
204     add_flag(PSH_WIZARD97_NEW);
205     add_flag(PSH_NOCONTEXTHELP);
206     if (string[0] != '\0')
207         FIXME("%s\n", string);
208 }
209 #undef add_flag
210
211 /******************************************************************************
212  *            PROPSHEET_GetPageRect
213  *
214  * Retrieve rect from tab control and map into the dialog for SetWindowPos
215  */
216 static void PROPSHEET_GetPageRect(const PropSheetInfo * psInfo, HWND hwndDlg, RECT *rc)
217 {
218     HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
219
220     GetClientRect(hwndTabCtrl, rc);
221     SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)rc);
222     MapWindowPoints(hwndTabCtrl, hwndDlg, (LPPOINT)rc, 2);
223 }
224
225 /******************************************************************************
226  *            PROPSHEET_FindPageByResId
227  *
228  * Find page index corresponding to page resource id.
229  */
230 INT PROPSHEET_FindPageByResId(PropSheetInfo * psInfo, LRESULT resId)
231 {
232    INT i;
233
234    for (i = 0; i < psInfo->nPages; i++)
235    {
236       LPCPROPSHEETPAGEA lppsp = (LPCPROPSHEETPAGEA)psInfo->proppage[i].hpage;
237
238       /* Fixme: if resource ID is a string shall we use strcmp ??? */
239       if (lppsp->u.pszTemplate == (LPVOID)resId)
240          break;
241    }
242
243    return i;
244 }
245
246 /******************************************************************************
247  *            PROPSHEET_AtoW
248  *
249  * Convert ASCII to Unicode since all data is saved as Unicode.
250  */
251 static void PROPSHEET_AtoW(LPCWSTR *tostr, LPCSTR frstr)
252 {
253     INT len;
254
255     TRACE("<%s>\n", frstr);
256     len = MultiByteToWideChar(CP_ACP, 0, frstr, -1, 0, 0);
257     *tostr = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
258     MultiByteToWideChar(CP_ACP, 0, frstr, -1, (LPWSTR)*tostr, len);
259 }
260
261 /******************************************************************************
262  *            PROPSHEET_CollectSheetInfoA
263  *
264  * Collect relevant data.
265  */
266 static BOOL PROPSHEET_CollectSheetInfoA(LPCPROPSHEETHEADERA lppsh,
267                                        PropSheetInfo * psInfo)
268 {
269   DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
270   DWORD dwFlags = lppsh->dwFlags;
271
272   psInfo->hasHelp = dwFlags & PSH_HASHELP;
273   psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
274   psInfo->useCallback = dwFlags & PSH_USECALLBACK;
275   psInfo->isModeless = dwFlags & PSH_MODELESS;
276
277   memcpy(&psInfo->ppshheader,lppsh,dwSize);
278   TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%ld\ndwFlags\t\t%08lx\nhwndParent\t%04x\nhInstance\t%08x\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
279         lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance,
280         debugstr_a(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
281
282   PROPSHEET_UnImplementedFlags(lppsh->dwFlags);
283
284   if (HIWORD(lppsh->pszCaption))
285   {
286      int len = strlen(lppsh->pszCaption);
287      psInfo->ppshheader.pszCaption = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof (WCHAR) );
288      MultiByteToWideChar(CP_ACP, 0, lppsh->pszCaption, -1, (LPWSTR) psInfo->ppshheader.pszCaption, len+1);
289      /* strcpy( (char *)psInfo->ppshheader.pszCaption, lppsh->pszCaption ); */
290   }
291   psInfo->nPages = lppsh->nPages;
292
293   if (dwFlags & PSH_USEPSTARTPAGE)
294   {
295     TRACE("PSH_USEPSTARTPAGE is on");
296     psInfo->active_page = 0;
297   }
298   else
299     psInfo->active_page = lppsh->u2.nStartPage;
300
301   if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
302      psInfo->active_page = 0;
303
304   psInfo->restartWindows = FALSE;
305   psInfo->rebootSystem = FALSE;
306   psInfo->hImageList = 0;
307   psInfo->activeValid = FALSE;
308
309   return TRUE;
310 }
311
312 /******************************************************************************
313  *            PROPSHEET_CollectSheetInfoW
314  *
315  * Collect relevant data.
316  */
317 static BOOL PROPSHEET_CollectSheetInfoW(LPCPROPSHEETHEADERW lppsh,
318                                        PropSheetInfo * psInfo)
319 {
320   DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERW));
321   DWORD dwFlags = lppsh->dwFlags;
322
323   psInfo->hasHelp = dwFlags & PSH_HASHELP;
324   psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
325   psInfo->useCallback = dwFlags & PSH_USECALLBACK;
326   psInfo->isModeless = dwFlags & PSH_MODELESS;
327
328   memcpy(&psInfo->ppshheader,lppsh,dwSize);
329   TRACE("\n** PROPSHEETHEADER **\ndwSize\t\t%ld\ndwFlags\t\t%08lx\nhwndParent\t%04x\nhInstance\t%08x\npszCaption\t'%s'\nnPages\t\t%d\npfnCallback\t%p\n",
330       lppsh->dwSize, lppsh->dwFlags, lppsh->hwndParent, lppsh->hInstance, debugstr_w(lppsh->pszCaption), lppsh->nPages, lppsh->pfnCallback);
331
332   PROPSHEET_UnImplementedFlags(lppsh->dwFlags);
333
334   if (HIWORD(lppsh->pszCaption))
335   {
336      int len = strlenW(lppsh->pszCaption);
337      psInfo->ppshheader.pszCaption = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof(WCHAR) );
338      strcpyW( (WCHAR *)psInfo->ppshheader.pszCaption, lppsh->pszCaption );
339   }
340   psInfo->nPages = lppsh->nPages;
341
342   if (dwFlags & PSH_USEPSTARTPAGE)
343   {
344     TRACE("PSH_USEPSTARTPAGE is on");
345     psInfo->active_page = 0;
346   }
347   else
348     psInfo->active_page = lppsh->u2.nStartPage;
349
350   if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
351      psInfo->active_page = 0;
352
353   psInfo->restartWindows = FALSE;
354   psInfo->rebootSystem = FALSE;
355   psInfo->hImageList = 0;
356   psInfo->activeValid = FALSE;
357
358   return TRUE;
359 }
360
361 /******************************************************************************
362  *            PROPSHEET_CollectPageInfo
363  *
364  * Collect property sheet data.
365  * With code taken from DIALOG_ParseTemplate32.
366  */
367 BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp,
368                                PropSheetInfo * psInfo,
369                                int index)
370 {
371   DLGTEMPLATE* pTemplate;
372   const WORD*  p;
373   DWORD dwFlags;
374   int width, height;
375
376   TRACE("\n");
377   psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
378   psInfo->proppage[index].hwndPage = 0;
379   psInfo->proppage[index].isDirty = FALSE;
380
381   /*
382    * Process property page flags.
383    */
384   dwFlags = lppsp->dwFlags;
385   psInfo->proppage[index].useCallback = (dwFlags & PSP_USECALLBACK) && (lppsp->pfnCallback);
386   psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
387   psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID);
388
389   /* as soon as we have a page with the help flag, set the sheet flag on */
390   if (psInfo->proppage[index].hasHelp)
391     psInfo->hasHelp = TRUE;
392
393   /*
394    * Process page template.
395    */
396   if (dwFlags & PSP_DLGINDIRECT)
397     pTemplate = (DLGTEMPLATE*)lppsp->u.pResource;
398   else
399   {
400     HRSRC hResource = FindResourceW(lppsp->hInstance,
401                                     lppsp->u.pszTemplate,
402                                     RT_DIALOGW);
403     HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
404                                      hResource);
405     pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
406   }
407
408   /*
409    * Extract the size of the page and the caption.
410    */
411   if (!pTemplate)
412       return FALSE;
413
414   p = (const WORD *)pTemplate;
415
416   if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
417   {
418     /* DIALOGEX template */
419
420     p++;       /* dlgVer    */
421     p++;       /* signature */
422     p += 2;    /* help ID   */
423     p += 2;    /* ext style */
424     p += 2;    /* style     */
425   }
426   else
427   {
428     /* DIALOG template */
429
430     p += 2;    /* style     */
431     p += 2;    /* ext style */
432   }
433
434   p++;    /* nb items */
435   p++;    /*   x      */
436   p++;    /*   y      */
437   width  = (WORD)*p; p++;
438   height = (WORD)*p; p++;
439
440   /* remember the largest width and height */
441   if (width > psInfo->width)
442     psInfo->width = width;
443
444   if (height > psInfo->height)
445     psInfo->height = height;
446
447   /* menu */
448   switch ((WORD)*p)
449   {
450     case 0x0000:
451       p++;
452       break;
453     case 0xffff:
454       p += 2;
455       break;
456     default:
457       p += lstrlenW( (LPCWSTR)p ) + 1;
458       break;
459   }
460
461   /* class */
462   switch ((WORD)*p)
463   {
464     case 0x0000:
465       p++;
466       break;
467     case 0xffff:
468       p += 2;
469       break;
470     default:
471       p += lstrlenW( (LPCWSTR)p ) + 1;
472       break;
473   }
474
475   /* Extract the caption */
476   psInfo->proppage[index].pszText = (LPCWSTR)p;
477   TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p));
478   p += lstrlenW((LPCWSTR)p) + 1;
479
480   if (dwFlags & PSP_USETITLE)
481   {
482     WCHAR szTitle[256];
483     const WCHAR *pTitle;
484     static WCHAR pszNull[] = { '(','n','u','l','l',')',0 };
485     int len;
486
487     if ( !HIWORD( lppsp->pszTitle ) )
488     {
489       if (!LoadStringW( lppsp->hInstance, (UINT)lppsp->pszTitle,szTitle,sizeof szTitle ))
490       {
491         pTitle = pszNull;
492         FIXME("Could not load resource #%04x?\n",LOWORD(lppsp->pszTitle));
493       }
494       else
495         pTitle = szTitle;
496     }
497     else
498       pTitle = lppsp->pszTitle;
499
500     len = strlenW(pTitle);
501     psInfo->proppage[index].pszText = COMCTL32_Alloc( (len+1)*sizeof (WCHAR) );
502     strcpyW( (LPWSTR)psInfo->proppage[index].pszText,pTitle);
503   }
504
505   /*
506    * Build the image list for icons
507    */
508   if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
509   {
510     HICON hIcon;
511     int icon_cx = GetSystemMetrics(SM_CXSMICON);
512     int icon_cy = GetSystemMetrics(SM_CYSMICON);
513
514     if (dwFlags & PSP_USEICONID)
515       hIcon = LoadImageW(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
516                          icon_cx, icon_cy, LR_DEFAULTCOLOR);
517     else
518       hIcon = lppsp->u2.hIcon;
519
520     if ( hIcon )
521     {
522       if (psInfo->hImageList == 0 )
523         psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);
524
525       ImageList_AddIcon(psInfo->hImageList, hIcon);
526     }
527
528   }
529
530   return TRUE;
531 }
532
533 /******************************************************************************
534  *            PROPSHEET_CreateDialog
535  *
536  * Creates the actual property sheet.
537  */
538 BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
539 {
540   LRESULT ret;
541   LPCVOID template;
542   LPVOID temp = 0;
543   HRSRC hRes;
544   DWORD resSize;
545   WORD resID = IDD_PROPSHEET;
546
547   TRACE("\n");
548   if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
549     resID = IDD_WIZARD;
550
551   if(!(hRes = FindResourceW(COMCTL32_hModule,
552                             MAKEINTRESOURCEW(resID),
553                             RT_DIALOGW)))
554     return FALSE;
555
556   if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
557     return FALSE;
558
559   /*
560    * Make a copy of the dialog template.
561    */
562   resSize = SizeofResource(COMCTL32_hModule, hRes);
563
564   temp = COMCTL32_Alloc(resSize);
565
566   if (!temp)
567     return FALSE;
568
569   memcpy(temp, template, resSize);
570
571   if (psInfo->useCallback)
572     (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
573
574   if (!(psInfo->ppshheader.dwFlags & PSH_MODELESS))
575       ret = DialogBoxIndirectParamW(psInfo->ppshheader.hInstance,
576                                     (LPDLGTEMPLATEW) temp,
577                                     psInfo->ppshheader.hwndParent,
578                                     (DLGPROC) PROPSHEET_DialogProc,
579                                     (LPARAM)psInfo);
580   else
581       ret = CreateDialogIndirectParamW(psInfo->ppshheader.hInstance,
582                                        (LPDLGTEMPLATEW) temp,
583                                        psInfo->ppshheader.hwndParent,
584                                        (DLGPROC) PROPSHEET_DialogProc,
585                                        (LPARAM)psInfo);
586
587   COMCTL32_Free(temp);
588
589   return ret;
590 }
591
592 /******************************************************************************
593  *            PROPSHEET_SizeMismatch
594  *
595  *     Verify that the tab control and the "largest" property sheet page dlg. template
596  *     match in size.
597  */
598 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo)
599 {
600   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
601   RECT rcOrigTab, rcPage;
602
603   /*
604    * Original tab size.
605    */
606   GetClientRect(hwndTabCtrl, &rcOrigTab);
607   TRACE("orig tab %d %d %d %d\n", rcOrigTab.left, rcOrigTab.top,
608         rcOrigTab.right, rcOrigTab.bottom);
609
610   /*
611    * Biggest page size.
612    */
613   rcPage.left   = psInfo->x;
614   rcPage.top    = psInfo->y;
615   rcPage.right  = psInfo->width;
616   rcPage.bottom = psInfo->height;
617
618   MapDialogRect(hwndDlg, &rcPage);
619   TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
620         rcPage.right, rcPage.bottom);
621
622   if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
623     return TRUE;
624   if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
625     return TRUE;
626
627   return FALSE;
628 }
629
630 /******************************************************************************
631  *            PROPSHEET_IsTooSmallWizard
632  *
633  * Verify that the default property sheet is big enough.
634  */
635 static BOOL PROPSHEET_IsTooSmallWizard(HWND hwndDlg, PropSheetInfo* psInfo)
636 {
637   RECT rcSheetRect, rcPage, rcLine, rcSheetClient;
638   HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
639   PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
640
641   GetClientRect(hwndDlg, &rcSheetClient);
642   GetWindowRect(hwndDlg, &rcSheetRect);
643   GetWindowRect(hwndLine, &rcLine);
644
645   /* Remove the space below the sunken line */
646   rcSheetClient.bottom -= (rcSheetRect.bottom - rcLine.top);
647
648   /* Remove the buffer zone all around the edge */
649   rcSheetClient.bottom -= (padding.y * 2);
650   rcSheetClient.right -= (padding.x * 2);
651
652   /*
653    * Biggest page size.
654    */
655   rcPage.left   = psInfo->x;
656   rcPage.top    = psInfo->y;
657   rcPage.right  = psInfo->width;
658   rcPage.bottom = psInfo->height;
659
660   MapDialogRect(hwndDlg, &rcPage);
661   TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
662         rcPage.right, rcPage.bottom);
663
664   if (rcPage.right > rcSheetClient.right)
665     return TRUE;
666
667   if (rcPage.bottom > rcSheetClient.bottom)
668     return TRUE;
669
670   return FALSE;
671 }
672
673 /******************************************************************************
674  *            PROPSHEET_AdjustSize
675  *
676  * Resizes the property sheet and the tab control to fit the largest page.
677  */
678 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
679 {
680   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
681   HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
682   RECT rc,tabRect;
683   int tabOffsetX, tabOffsetY, buttonHeight;
684   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
685   RECT units;
686
687   /* Get the height of buttons */
688   GetClientRect(hwndButton, &rc);
689   buttonHeight = rc.bottom;
690
691   /*
692    * Biggest page size.
693    */
694   rc.left   = psInfo->x;
695   rc.top    = psInfo->y;
696   rc.right  = psInfo->width;
697   rc.bottom = psInfo->height;
698
699   MapDialogRect(hwndDlg, &rc);
700
701   /* retrieve the dialog units */
702   units.left = units.right = 4;
703   units.top = units.bottom = 8;
704   MapDialogRect(hwndDlg, &units);
705
706   /*
707    * Resize the tab control.
708    */
709   GetClientRect(hwndTabCtrl,&tabRect);
710
711   SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
712
713   if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
714   {
715       rc.bottom = rc.top + tabRect.bottom - tabRect.top;
716       psInfo->height = MulDiv((rc.bottom - rc.top),8,units.top);
717   }
718
719   if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
720   {
721       rc.right = rc.left + tabRect.right - tabRect.left;
722       psInfo->width  = MulDiv((rc.right - rc.left),4,units.left);
723   }
724
725   SendMessageW(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
726
727   tabOffsetX = -(rc.left);
728   tabOffsetY = -(rc.top);
729
730   rc.right -= rc.left;
731   rc.bottom -= rc.top;
732   TRACE("setting tab %08lx, rc (0,0)-(%d,%d)\n",
733         (DWORD)hwndTabCtrl, rc.right, rc.bottom);
734   SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
735                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
736
737   GetClientRect(hwndTabCtrl, &rc);
738
739   TRACE("tab client rc %d %d %d %d\n",
740         rc.left, rc.top, rc.right, rc.bottom);
741
742   rc.right += ((padding.x * 2) + tabOffsetX);
743   rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
744
745   /*
746    * Resize the property sheet.
747    */
748   TRACE("setting dialog %08lx, rc (0,0)-(%d,%d)\n",
749         (DWORD)hwndDlg, rc.right, rc.bottom);
750   SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
751                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
752   return TRUE;
753 }
754
755 /******************************************************************************
756  *            PROPSHEET_AdjustSizeWizard
757  *
758  * Resizes the property sheet to fit the largest page.
759  */
760 static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, PropSheetInfo* psInfo)
761 {
762   HWND hwndButton = GetDlgItem(hwndDlg, IDCANCEL);
763   HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
764   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
765   RECT rc,tabRect;
766   int buttonHeight, lineHeight;
767   PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
768   RECT units;
769
770   /* Get the height of buttons */
771   GetClientRect(hwndButton, &rc);
772   buttonHeight = rc.bottom;
773
774   GetClientRect(hwndLine, &rc);
775   lineHeight = rc.bottom;
776
777   /* retrieve the dialog units */
778   units.left = units.right = 4;
779   units.top = units.bottom = 8;
780   MapDialogRect(hwndDlg, &units);
781
782   /*
783    * Biggest page size.
784    */
785   rc.left   = psInfo->x;
786   rc.top    = psInfo->y;
787   rc.right  = psInfo->width;
788   rc.bottom = psInfo->height;
789
790   MapDialogRect(hwndDlg, &rc);
791
792   GetClientRect(hwndTabCtrl,&tabRect);
793
794   if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
795   {
796       rc.bottom = rc.top + tabRect.bottom - tabRect.top;
797       psInfo->height = MulDiv((rc.bottom - rc.top), 8, units.top);
798   }
799
800   if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
801   {
802       rc.right = rc.left + tabRect.right - tabRect.left;
803       psInfo->width  = MulDiv((rc.right - rc.left), 4, units.left);
804   }
805
806   TRACE("Biggest page %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom);
807   TRACE("   constants padx=%d, pady=%d, butH=%d, lH=%d\n",
808         padding.x, padding.y, buttonHeight, lineHeight);
809
810   /* Make room */
811   rc.right += (padding.x * 2);
812   rc.bottom += (buttonHeight + (5 * padding.y) + lineHeight);
813
814   /*
815    * Resize the property sheet.
816    */
817   TRACE("setting dialog %08lx, rc (0,0)-(%d,%d)\n",
818         (DWORD)hwndDlg, rc.right, rc.bottom);
819   SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
820                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
821   return TRUE;
822 }
823
824 /******************************************************************************
825  *            PROPSHEET_AdjustButtons
826  *
827  * Adjusts the buttons' positions.
828  */
829 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo)
830 {
831   HWND hwndButton = GetDlgItem(hwndParent, IDOK);
832   RECT rcSheet;
833   int x, y;
834   int num_buttons = 2;
835   int buttonWidth, buttonHeight;
836   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
837
838   if (psInfo->hasApply)
839     num_buttons++;
840
841   if (psInfo->hasHelp)
842     num_buttons++;
843
844   /*
845    * Obtain the size of the buttons.
846    */
847   GetClientRect(hwndButton, &rcSheet);
848   buttonWidth = rcSheet.right;
849   buttonHeight = rcSheet.bottom;
850
851   /*
852    * Get the size of the property sheet.
853    */
854   GetClientRect(hwndParent, &rcSheet);
855
856   /*
857    * All buttons will be at this y coordinate.
858    */
859   y = rcSheet.bottom - (padding.y + buttonHeight);
860
861   /*
862    * Position OK button.
863    */
864   hwndButton = GetDlgItem(hwndParent, IDOK);
865
866   x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
867
868   SetWindowPos(hwndButton, 0, x, y, 0, 0,
869                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
870
871   /*
872    * Position Cancel button.
873    */
874   hwndButton = GetDlgItem(hwndParent, IDCANCEL);
875
876   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
877
878   SetWindowPos(hwndButton, 0, x, y, 0, 0,
879                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
880
881   /*
882    * Position Apply button.
883    */
884   hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
885
886   if (psInfo->hasApply)
887   {
888     if (psInfo->hasHelp)
889       x = rcSheet.right - ((padding.x + buttonWidth) * 2);
890     else
891       x = rcSheet.right - (padding.x + buttonWidth);
892
893     SetWindowPos(hwndButton, 0, x, y, 0, 0,
894                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
895
896     EnableWindow(hwndButton, FALSE);
897   }
898   else
899     ShowWindow(hwndButton, SW_HIDE);
900
901   /*
902    * Position Help button.
903    */
904   hwndButton = GetDlgItem(hwndParent, IDHELP);
905
906   if (psInfo->hasHelp)
907   {
908     x = rcSheet.right - (padding.x + buttonWidth);
909
910     SetWindowPos(hwndButton, 0, x, y, 0, 0,
911                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
912   }
913   else
914     ShowWindow(hwndButton, SW_HIDE);
915
916   return TRUE;
917 }
918
919 /******************************************************************************
920  *            PROPSHEET_AdjustButtonsWizard
921  *
922  * Adjusts the buttons' positions.
923  */
924 static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
925                                           PropSheetInfo* psInfo)
926 {
927   HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
928   HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
929   RECT rcSheet;
930   int x, y;
931   int num_buttons = 3;
932   int buttonWidth, buttonHeight, lineHeight, lineWidth;
933   PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
934
935   if (psInfo->hasHelp)
936     num_buttons++;
937
938   /*
939    * Obtain the size of the buttons.
940    */
941   GetClientRect(hwndButton, &rcSheet);
942   buttonWidth = rcSheet.right;
943   buttonHeight = rcSheet.bottom;
944
945   GetClientRect(hwndLine, &rcSheet);
946   lineHeight = rcSheet.bottom;
947
948   /*
949    * Get the size of the property sheet.
950    */
951   GetClientRect(hwndParent, &rcSheet);
952
953   /*
954    * All buttons will be at this y coordinate.
955    */
956   y = rcSheet.bottom - (padding.y + buttonHeight);
957
958   /*
959    * Position the Next and the Finish buttons.
960    */
961   hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
962
963   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
964
965   SetWindowPos(hwndButton, 0, x, y, 0, 0,
966                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
967
968   hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
969
970   SetWindowPos(hwndButton, 0, x, y, 0, 0,
971                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
972
973   ShowWindow(hwndButton, SW_HIDE);
974
975   /*
976    * Position the Back button.
977    */
978   hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
979
980   x -= buttonWidth;
981
982   SetWindowPos(hwndButton, 0, x, y, 0, 0,
983                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
984
985   /*
986    * Position the Cancel button.
987    */
988   hwndButton = GetDlgItem(hwndParent, IDCANCEL);
989
990   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 2));
991
992   SetWindowPos(hwndButton, 0, x, y, 0, 0,
993                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
994
995   /*
996    * Position Help button.
997    */
998   hwndButton = GetDlgItem(hwndParent, IDHELP);
999
1000   if (psInfo->hasHelp)
1001   {
1002     x = rcSheet.right - (padding.x + buttonWidth);
1003
1004     SetWindowPos(hwndButton, 0, x, y, 0, 0,
1005                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
1006   }
1007   else
1008     ShowWindow(hwndButton, SW_HIDE);
1009
1010   /*
1011    * Position and resize the sunken line.
1012    */
1013   x = padding.x;
1014   y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight);
1015
1016   GetClientRect(hwndParent, &rcSheet);
1017   lineWidth = rcSheet.right - (padding.x * 2);
1018
1019   SetWindowPos(hwndLine, 0, x, y, lineWidth, 2,
1020                SWP_NOZORDER | SWP_NOACTIVATE);
1021
1022   return TRUE;
1023 }
1024
1025 /******************************************************************************
1026  *            PROPSHEET_GetPaddingInfo
1027  *
1028  * Returns the layout information.
1029  */
1030 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
1031 {
1032   HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1033   RECT rcTab;
1034   POINT tl;
1035   PADDING_INFO padding;
1036
1037   GetWindowRect(hwndTab, &rcTab);
1038
1039   tl.x = rcTab.left;
1040   tl.y = rcTab.top;
1041
1042   ScreenToClient(hwndDlg, &tl);
1043
1044   padding.x = tl.x;
1045   padding.y = tl.y;
1046
1047   return padding;
1048 }
1049
1050 /******************************************************************************
1051  *            PROPSHEET_GetPaddingInfoWizard
1052  *
1053  * Returns the layout information.
1054  * Vertical spacing is the distance between the line and the buttons.
1055  * Do NOT use the Help button to gather padding information when it isn't mapped
1056  * (PSH_HASHELP), as app writers aren't forced to supply correct coordinates
1057  * for it in this case !
1058  * FIXME: I'm not sure about any other coordinate problems with these evil
1059  * buttons. Fix it in case additional problems appear or maybe calculate
1060  * a padding in a completely different way, as this is somewhat messy.
1061  */
1062 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
1063  psInfo)
1064 {
1065   PADDING_INFO padding;
1066   RECT rc;
1067   HWND hwndControl;
1068   INT idButton;
1069   POINT ptButton, ptLine;
1070
1071   TRACE("\n");
1072   if (psInfo->hasHelp)
1073   {
1074         idButton = IDHELP;
1075   }
1076   else
1077   {
1078     if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
1079     {
1080         idButton = IDC_NEXT_BUTTON;
1081     }
1082     else
1083     {
1084         /* hopefully this is ok */
1085         idButton = IDCANCEL;
1086     }
1087   }
1088
1089   hwndControl = GetDlgItem(hwndDlg, idButton);
1090   GetWindowRect(hwndControl, &rc);
1091
1092   ptButton.x = rc.left;
1093   ptButton.y = rc.top;
1094
1095   ScreenToClient(hwndDlg, &ptButton);
1096
1097   /* Line */
1098   hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
1099   GetWindowRect(hwndControl, &rc);
1100
1101   ptLine.x = 0;
1102   ptLine.y = rc.bottom;
1103
1104   ScreenToClient(hwndDlg, &ptLine);
1105
1106   padding.y = ptButton.y - ptLine.y;
1107
1108   if (padding.y < 0)
1109           ERR("padding negative ! Please report this !\n");
1110
1111   /* this is most probably not correct, but the best we have now */
1112   padding.x = padding.y;
1113   return padding;
1114 }
1115
1116 /******************************************************************************
1117  *            PROPSHEET_CreateTabControl
1118  *
1119  * Insert the tabs in the tab control.
1120  */
1121 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
1122                                        PropSheetInfo * psInfo)
1123 {
1124   HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1125   TCITEMW item;
1126   int i, nTabs;
1127   int iImage = 0;
1128
1129   TRACE("\n");
1130   item.mask = TCIF_TEXT;
1131   item.cchTextMax = MAX_TABTEXT_LENGTH;
1132
1133   nTabs = psInfo->nPages;
1134
1135   /*
1136    * Set the image list for icons.
1137    */
1138   if (psInfo->hImageList)
1139   {
1140     SendMessageW(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1141   }
1142
1143   for (i = 0; i < nTabs; i++)
1144   {
1145     if ( psInfo->proppage[i].hasIcon )
1146     {
1147       item.mask |= TCIF_IMAGE;
1148       item.iImage = iImage++;
1149     }
1150     else
1151     {
1152       item.mask &= ~TCIF_IMAGE;
1153     }
1154
1155     item.pszText = (LPWSTR) psInfo->proppage[i].pszText;
1156     SendMessageW(hwndTabCtrl, TCM_INSERTITEMW, (WPARAM)i, (LPARAM)&item);
1157   }
1158
1159   return TRUE;
1160 }
1161
1162 /******************************************************************************
1163  *            PROPSHEET_CreatePage
1164  *
1165  * Creates a page.
1166  */
1167 static BOOL PROPSHEET_CreatePage(HWND hwndParent,
1168                                 int index,
1169                                 const PropSheetInfo * psInfo,
1170                                 LPCPROPSHEETPAGEW ppshpage)
1171 {
1172   DLGTEMPLATE* pTemplate;
1173   HWND hwndPage;
1174   RECT rc;
1175   PropPageInfo* ppInfo = psInfo->proppage;
1176   PADDING_INFO padding;
1177   UINT pageWidth,pageHeight;
1178   DWORD resSize;
1179   LPVOID temp = NULL;
1180
1181   TRACE("index %d\n", index);
1182
1183   if (ppshpage == NULL)
1184   {
1185     return FALSE;
1186   }
1187
1188   if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1189     pTemplate = (DLGTEMPLATE*)ppshpage->u.pResource;
1190   else
1191   {
1192     HRSRC hResource;
1193     HANDLE hTemplate;
1194
1195     hResource = FindResourceW(ppshpage->hInstance,
1196                                     ppshpage->u.pszTemplate,
1197                                     RT_DIALOGW);
1198     if(!hResource)
1199         return FALSE;
1200
1201     resSize = SizeofResource(ppshpage->hInstance, hResource);
1202
1203     hTemplate = LoadResource(ppshpage->hInstance, hResource);
1204     if(!hTemplate)
1205         return FALSE;
1206
1207     pTemplate = (LPDLGTEMPLATEW)LockResource(hTemplate);
1208     /*
1209      * Make a copy of the dialog template to make it writable
1210      */
1211     temp = COMCTL32_Alloc(resSize);
1212     if (!temp)
1213       return FALSE;
1214
1215     memcpy(temp, pTemplate, resSize);
1216     pTemplate = temp;
1217   }
1218
1219   if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
1220   {
1221     ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD | DS_CONTROL;
1222     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME;
1223     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION;
1224     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU;
1225     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP;
1226     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_DISABLED;
1227     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_VISIBLE;
1228     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_THICKFRAME;
1229   }
1230   else
1231   {
1232     pTemplate->style |= WS_CHILD | DS_CONTROL;
1233     pTemplate->style &= ~DS_MODALFRAME;
1234     pTemplate->style &= ~WS_CAPTION;
1235     pTemplate->style &= ~WS_SYSMENU;
1236     pTemplate->style &= ~WS_POPUP;
1237     pTemplate->style &= ~WS_DISABLED;
1238     pTemplate->style &= ~WS_VISIBLE;
1239     pTemplate->style &= ~WS_THICKFRAME;
1240   }
1241
1242   if (psInfo->proppage[index].useCallback)
1243     (*(ppshpage->pfnCallback))(hwndParent,
1244                                PSPCB_CREATE,
1245                                (LPPROPSHEETPAGEW)ppshpage);
1246
1247   hwndPage = CreateDialogIndirectParamW(ppshpage->hInstance,
1248                                         pTemplate,
1249                                         hwndParent,
1250                                         ppshpage->pfnDlgProc,
1251                                         (LPARAM)ppshpage);
1252   /* Free a no more needed copy */
1253   if(temp)
1254       COMCTL32_Free(temp);
1255
1256   ppInfo[index].hwndPage = hwndPage;
1257
1258   if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD) {
1259       /* FIXME: This code may no longer be correct.
1260        *        It was not for the non-wizard path.  (GLA 6/02)
1261        */
1262       rc.left = psInfo->x;
1263       rc.top = psInfo->y;
1264       rc.right = psInfo->width;
1265       rc.bottom = psInfo->height;
1266
1267       MapDialogRect(hwndParent, &rc);
1268
1269       pageWidth = rc.right - rc.left;
1270       pageHeight = rc.bottom - rc.top;
1271
1272       padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
1273       TRACE("setting page %08lx, rc (%d,%d)-(%d,%d) w=%d, h=%d, padx=%d, pady=%d\n",
1274             (DWORD)hwndPage, rc.left, rc.top, rc.right, rc.bottom,
1275             pageWidth, pageHeight, padding.x, padding.y);
1276       SetWindowPos(hwndPage, HWND_TOP,
1277                    rc.left + padding.x/2,
1278                    rc.top + padding.y/2,
1279                    pageWidth, pageHeight, 0);
1280   }
1281   else {
1282       /*
1283        * Ask the Tab control to reduce the client rectangle to that
1284        * it has available.
1285        */
1286       PROPSHEET_GetPageRect(psInfo, hwndParent, &rc);
1287       pageWidth = rc.right - rc.left;
1288       pageHeight = rc.bottom - rc.top;
1289       TRACE("setting page %08lx, rc (%d,%d)-(%d,%d) w=%d, h=%d\n",
1290             (DWORD)hwndPage, rc.left, rc.top, rc.right, rc.bottom,
1291             pageWidth, pageHeight);
1292       SetWindowPos(hwndPage, HWND_TOP,
1293                    rc.left, rc.top,
1294                    pageWidth, pageHeight, 0);
1295   }
1296
1297   return TRUE;
1298 }
1299
1300 /******************************************************************************
1301  *            PROPSHEET_ShowPage
1302  *
1303  * Displays or creates the specified page.
1304  */
1305 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
1306 {
1307   HWND hwndTabCtrl;
1308
1309   TRACE("active_page %d, index %d\n", psInfo->active_page, index);
1310   if (index == psInfo->active_page)
1311   {
1312       if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
1313           SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1314       return TRUE;
1315   }
1316
1317   if (psInfo->proppage[index].hwndPage == 0)
1318   {
1319      LPCPROPSHEETPAGEW ppshpage;
1320
1321      ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1322      PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1323   }
1324
1325   if (psInfo->active_page != -1)
1326      ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1327
1328   ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1329
1330   /* Synchronize current selection with tab control
1331    * It seems to be needed even in case of PSH_WIZARD (no tab controls there) */
1332   hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1333   SendMessageW(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1334
1335   psInfo->active_page = index;
1336   psInfo->activeValid = TRUE;
1337
1338   return TRUE;
1339 }
1340
1341 /******************************************************************************
1342  *            PROPSHEET_Back
1343  */
1344 static BOOL PROPSHEET_Back(HWND hwndDlg)
1345 {
1346   PSHNOTIFY psn;
1347   HWND hwndPage;
1348   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1349                                                     PropSheetInfoStr);
1350   LRESULT result;
1351   int idx;
1352
1353   TRACE("active_page %d\n", psInfo->active_page);
1354   if (psInfo->active_page < 0)
1355      return FALSE;
1356
1357   psn.hdr.code     = PSN_WIZBACK;
1358   psn.hdr.hwndFrom = hwndDlg;
1359   psn.hdr.idFrom   = 0;
1360   psn.lParam       = 0;
1361
1362   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1363
1364   result = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1365   if (result == -1)
1366     return FALSE;
1367   else if (result == 0)
1368      idx = psInfo->active_page - 1;
1369   else
1370      idx = PROPSHEET_FindPageByResId(psInfo, result);
1371
1372   if (idx >= 0 && idx < psInfo->nPages)
1373   {
1374      if (PROPSHEET_CanSetCurSel(hwndDlg))
1375         PROPSHEET_SetCurSel(hwndDlg, idx, -1, 0);
1376   }
1377   return TRUE;
1378 }
1379
1380 /******************************************************************************
1381  *            PROPSHEET_Next
1382  */
1383 static BOOL PROPSHEET_Next(HWND hwndDlg)
1384 {
1385   PSHNOTIFY psn;
1386   HWND hwndPage;
1387   LRESULT msgResult = 0;
1388   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1389                                                     PropSheetInfoStr);
1390   int idx;
1391
1392   TRACE("active_page %d\n", psInfo->active_page);
1393   if (psInfo->active_page < 0)
1394      return FALSE;
1395
1396   psn.hdr.code     = PSN_WIZNEXT;
1397   psn.hdr.hwndFrom = hwndDlg;
1398   psn.hdr.idFrom   = 0;
1399   psn.lParam       = 0;
1400
1401   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1402
1403   msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1404   if (msgResult == -1)
1405     return FALSE;
1406   else if (msgResult == 0)
1407      idx = psInfo->active_page + 1;
1408   else
1409      idx = PROPSHEET_FindPageByResId(psInfo, msgResult);
1410
1411   if (idx < psInfo->nPages )
1412   {
1413      if (PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1414         PROPSHEET_SetCurSel(hwndDlg, idx, 1, 0);
1415   }
1416
1417   return TRUE;
1418 }
1419
1420 /******************************************************************************
1421  *            PROPSHEET_Finish
1422  */
1423 static BOOL PROPSHEET_Finish(HWND hwndDlg)
1424 {
1425   PSHNOTIFY psn;
1426   HWND hwndPage;
1427   LRESULT msgResult = 0;
1428   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1429                                                     PropSheetInfoStr);
1430
1431   TRACE("active_page %d\n", psInfo->active_page);
1432   if (psInfo->active_page < 0)
1433      return FALSE;
1434
1435   psn.hdr.code     = PSN_WIZFINISH;
1436   psn.hdr.hwndFrom = hwndDlg;
1437   psn.hdr.idFrom   = 0;
1438   psn.lParam       = 0;
1439
1440   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1441
1442   msgResult = SendMessageW(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1443
1444   TRACE("msg result %ld\n", msgResult);
1445
1446   if (msgResult != 0)
1447     return FALSE;
1448
1449   if (psInfo->isModeless)
1450     psInfo->activeValid = FALSE;
1451   else
1452     EndDialog(hwndDlg, TRUE);
1453
1454   return TRUE;
1455 }
1456
1457 /******************************************************************************
1458  *            PROPSHEET_Apply
1459  */
1460 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1461 {
1462   int i;
1463   HWND hwndPage;
1464   PSHNOTIFY psn;
1465   LRESULT msgResult;
1466   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1467                                                     PropSheetInfoStr);
1468
1469   TRACE("active_page %d\n", psInfo->active_page);
1470   if (psInfo->active_page < 0)
1471      return FALSE;
1472
1473   psn.hdr.hwndFrom = hwndDlg;
1474   psn.hdr.idFrom   = 0;
1475   psn.lParam       = 0;
1476
1477
1478   /*
1479    * Send PSN_KILLACTIVE to the current page.
1480    */
1481   psn.hdr.code = PSN_KILLACTIVE;
1482
1483   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1484
1485   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1486     return FALSE;
1487
1488   /*
1489    * Send PSN_APPLY to all pages.
1490    */
1491   psn.hdr.code = PSN_APPLY;
1492   psn.lParam   = lParam;
1493
1494   for (i = 0; i < psInfo->nPages; i++)
1495   {
1496     hwndPage = psInfo->proppage[i].hwndPage;
1497     if (hwndPage)
1498     {
1499        msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1500        if (msgResult == PSNRET_INVALID_NOCHANGEPAGE)
1501           return FALSE;
1502     }
1503   }
1504
1505   if(lParam)
1506   {
1507      psInfo->activeValid = FALSE;
1508   }
1509   else if(psInfo->active_page >= 0)
1510   {
1511      psn.hdr.code = PSN_SETACTIVE;
1512      psn.lParam   = 0;
1513      hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1514      SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1515   }
1516
1517   return TRUE;
1518 }
1519
1520 /******************************************************************************
1521  *            PROPSHEET_Cancel
1522  */
1523 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1524 {
1525   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1526                                                     PropSheetInfoStr);
1527   HWND hwndPage;
1528   PSHNOTIFY psn;
1529   int i;
1530
1531   TRACE("active_page %d\n", psInfo->active_page);
1532   if (psInfo->active_page < 0)
1533      return;
1534
1535   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1536   psn.hdr.code     = PSN_QUERYCANCEL;
1537   psn.hdr.hwndFrom = hwndDlg;
1538   psn.hdr.idFrom   = 0;
1539   psn.lParam       = 0;
1540
1541   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1542     return;
1543
1544   psn.hdr.code = PSN_RESET;
1545   psn.lParam   = lParam;
1546
1547   for (i = 0; i < psInfo->nPages; i++)
1548   {
1549     hwndPage = psInfo->proppage[i].hwndPage;
1550
1551     if (hwndPage)
1552        SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1553   }
1554
1555   if (psInfo->isModeless)
1556   {
1557      /* makes PSM_GETCURRENTPAGEHWND return NULL */
1558      psInfo->activeValid = FALSE;
1559   }
1560   else
1561     EndDialog(hwndDlg, FALSE);
1562 }
1563
1564 /******************************************************************************
1565  *            PROPSHEET_Help
1566  */
1567 static void PROPSHEET_Help(HWND hwndDlg)
1568 {
1569   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1570                                                     PropSheetInfoStr);
1571   HWND hwndPage;
1572   PSHNOTIFY psn;
1573
1574   TRACE("active_page %d\n", psInfo->active_page);
1575   if (psInfo->active_page < 0)
1576      return;
1577
1578   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1579   psn.hdr.code     = PSN_HELP;
1580   psn.hdr.hwndFrom = hwndDlg;
1581   psn.hdr.idFrom   = 0;
1582   psn.lParam       = 0;
1583
1584   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1585 }
1586
1587 /******************************************************************************
1588  *            PROPSHEET_Changed
1589  */
1590 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1591 {
1592   int i;
1593   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1594                                                     PropSheetInfoStr);
1595
1596   TRACE("\n");
1597   if (!psInfo) return;
1598   /*
1599    * Set the dirty flag of this page.
1600    */
1601   for (i = 0; i < psInfo->nPages; i++)
1602   {
1603     if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1604       psInfo->proppage[i].isDirty = TRUE;
1605   }
1606
1607   /*
1608    * Enable the Apply button.
1609    */
1610   if (psInfo->hasApply)
1611   {
1612     HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1613
1614     EnableWindow(hwndApplyBtn, TRUE);
1615   }
1616 }
1617
1618 /******************************************************************************
1619  *            PROPSHEET_UnChanged
1620  */
1621 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1622 {
1623   int i;
1624   BOOL noPageDirty = TRUE;
1625   HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1626   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1627                                                     PropSheetInfoStr);
1628
1629   TRACE("\n");
1630   if ( !psInfo ) return;
1631   for (i = 0; i < psInfo->nPages; i++)
1632   {
1633     /* set the specified page as clean */
1634     if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1635       psInfo->proppage[i].isDirty = FALSE;
1636
1637     /* look to see if there's any dirty pages */
1638     if (psInfo->proppage[i].isDirty)
1639       noPageDirty = FALSE;
1640   }
1641
1642   /*
1643    * Disable Apply button.
1644    */
1645   if (noPageDirty)
1646     EnableWindow(hwndApplyBtn, FALSE);
1647 }
1648
1649 /******************************************************************************
1650  *            PROPSHEET_PressButton
1651  */
1652 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1653 {
1654   TRACE("buttonID %d\n", buttonID);
1655   switch (buttonID)
1656   {
1657     case PSBTN_APPLYNOW:
1658       PROPSHEET_DoCommand(hwndDlg, IDC_APPLY_BUTTON);
1659       break;
1660     case PSBTN_BACK:
1661       PROPSHEET_Back(hwndDlg);
1662       break;
1663     case PSBTN_CANCEL:
1664       PROPSHEET_DoCommand(hwndDlg, IDCANCEL);
1665       break;
1666     case PSBTN_FINISH:
1667       PROPSHEET_Finish(hwndDlg);
1668       break;
1669     case PSBTN_HELP:
1670       PROPSHEET_DoCommand(hwndDlg, IDHELP);
1671       break;
1672     case PSBTN_NEXT:
1673       PROPSHEET_Next(hwndDlg);
1674       break;
1675     case PSBTN_OK:
1676       PROPSHEET_DoCommand(hwndDlg, IDOK);
1677       break;
1678     default:
1679       FIXME("Invalid button index %d\n", buttonID);
1680   }
1681 }
1682
1683
1684 /*************************************************************************
1685  * BOOL PROPSHEET_CanSetCurSel [Internal]
1686  *
1687  * Test whether the current page can be changed by sending a PSN_KILLACTIVE
1688  *
1689  * PARAMS
1690  *     hwndDlg        [I] handle to a Dialog hWnd
1691  *
1692  * RETURNS
1693  *     TRUE if Current Selection can change
1694  *
1695  * NOTES
1696  */
1697 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1698 {
1699   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1700                                                     PropSheetInfoStr);
1701   HWND hwndPage;
1702   PSHNOTIFY psn;
1703   BOOL res = FALSE;
1704
1705   TRACE("active_page %d\n", psInfo->active_page);
1706   if (!psInfo)
1707   {
1708      res = FALSE;
1709      goto end;
1710   }
1711
1712   if (psInfo->active_page < 0)
1713   {
1714      res = TRUE;
1715      goto end;
1716   }
1717
1718   /*
1719    * Notify the current page.
1720    */
1721   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1722   psn.hdr.code     = PSN_KILLACTIVE;
1723   psn.hdr.hwndFrom = hwndDlg;
1724   psn.hdr.idFrom   = 0;
1725   psn.lParam       = 0;
1726
1727   res = !SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1728
1729 end:
1730   TRACE("<-- %d\n", res);
1731   return res;
1732 }
1733
1734 /******************************************************************************
1735  *            PROPSHEET_SetCurSel
1736  */
1737 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
1738                                 int index,
1739                                 int skipdir,
1740                                 HPROPSHEETPAGE hpage
1741                                 )
1742 {
1743   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
1744   HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
1745
1746   TRACE("index %d, skipdir %d, hpage %p\n", index, skipdir, hpage);
1747   /* hpage takes precedence over index */
1748   if (hpage != NULL)
1749     index = PROPSHEET_GetPageIndex(hpage, psInfo);
1750
1751   if (index < 0 || index >= psInfo->nPages)
1752   {
1753     TRACE("Could not find page to select!\n");
1754     return FALSE;
1755   }
1756
1757   while (1) {
1758     int result;
1759     PSHNOTIFY psn;
1760
1761     psn.hdr.code     = PSN_SETACTIVE;
1762     psn.hdr.hwndFrom = hwndDlg;
1763     psn.hdr.idFrom   = 0;
1764     psn.lParam       = 0;
1765
1766     if (!psInfo->proppage[index].hwndPage) {
1767       LPCPROPSHEETPAGEW ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[index].hpage;
1768       PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1769     }
1770
1771     result = SendMessageW(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1772     if (!result)
1773       break;
1774     if (result == -1) {
1775       index+=skipdir;
1776       if (index < 0) {
1777         index = 0;
1778         FIXME("Tried to skip before first property sheet page!\n");
1779         break;
1780       }
1781       if (index >= psInfo->nPages) {
1782         FIXME("Tried to skip after last property sheet page!\n");
1783         index = psInfo->nPages-1;
1784         break;
1785       }
1786     }
1787     else if (result != 0)
1788     {
1789        index = PROPSHEET_FindPageByResId(psInfo, result);
1790        continue;
1791     }
1792   }
1793   /*
1794    * Display the new page.
1795    */
1796   PROPSHEET_ShowPage(hwndDlg, index, psInfo);
1797
1798   if (psInfo->proppage[index].hasHelp)
1799     EnableWindow(hwndHelp, TRUE);
1800   else
1801     EnableWindow(hwndHelp, FALSE);
1802
1803   return TRUE;
1804 }
1805
1806 /******************************************************************************
1807  *            PROPSHEET_SetTitleA
1808  */
1809 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
1810 {
1811   if(HIWORD(lpszText))
1812   {
1813      WCHAR szTitle[256];
1814      MultiByteToWideChar(CP_ACP, 0, lpszText, -1,
1815                              szTitle, sizeof szTitle);
1816      PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle);
1817   }
1818   else
1819   {
1820      PROPSHEET_SetTitleW(hwndDlg, dwStyle, (LPCWSTR)lpszText);
1821   }
1822 }
1823
1824 /******************************************************************************
1825  *            PROPSHEET_SetTitleW
1826  */
1827 static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
1828 {
1829   PropSheetInfo*        psInfo = (PropSheetInfo*) GetPropW(hwndDlg, PropSheetInfoStr);
1830   WCHAR                 szTitle[256];
1831
1832   TRACE("'%s' (style %08lx)\n", debugstr_w(lpszText), dwStyle);
1833   if (HIWORD(lpszText) == 0) {
1834     if (!LoadStringW(psInfo->ppshheader.hInstance,
1835                      LOWORD(lpszText), szTitle, sizeof(szTitle)-sizeof(WCHAR)))
1836       return;
1837     lpszText = szTitle;
1838   }
1839   if (dwStyle & PSH_PROPTITLE)
1840   {
1841     WCHAR* dest;
1842     int lentitle = strlenW(lpszText);
1843     int lenprop  = strlenW(psInfo->strPropertiesFor);
1844
1845     dest = COMCTL32_Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
1846     strcpyW(dest, psInfo->strPropertiesFor);
1847     strcatW(dest, lpszText);
1848
1849     SetWindowTextW(hwndDlg, dest);
1850     COMCTL32_Free(dest);
1851   }
1852   else
1853     SetWindowTextW(hwndDlg, lpszText);
1854 }
1855
1856 /******************************************************************************
1857  *            PROPSHEET_SetFinishTextA
1858  */
1859 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
1860 {
1861   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
1862
1863   TRACE("'%s'\n", lpszText);
1864   /* Set text, show and enable the Finish button */
1865   SetWindowTextA(hwndButton, lpszText);
1866   ShowWindow(hwndButton, SW_SHOW);
1867   EnableWindow(hwndButton, TRUE);
1868
1869   /* Make it default pushbutton */
1870   SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
1871
1872   /* Hide Back button */
1873   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
1874   ShowWindow(hwndButton, SW_HIDE);
1875
1876   /* Hide Next button */
1877   hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
1878   ShowWindow(hwndButton, SW_HIDE);
1879 }
1880
1881 /******************************************************************************
1882  *            PROPSHEET_SetFinishTextW
1883  */
1884 static void PROPSHEET_SetFinishTextW(HWND hwndDlg, LPCWSTR lpszText)
1885 {
1886   HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
1887
1888   TRACE("'%s'\n", debugstr_w(lpszText));
1889   /* Set text, show and enable the Finish button */
1890   SetWindowTextW(hwndButton, lpszText);
1891   ShowWindow(hwndButton, SW_SHOW);
1892   EnableWindow(hwndButton, TRUE);
1893
1894   /* Make it default pushbutton */
1895   SendMessageW(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
1896
1897   /* Hide Back button */
1898   hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
1899   ShowWindow(hwndButton, SW_HIDE);
1900
1901   /* Hide Next button */
1902   hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
1903   ShowWindow(hwndButton, SW_HIDE);
1904 }
1905
1906 /******************************************************************************
1907  *            PROPSHEET_QuerySiblings
1908  */
1909 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
1910                                        WPARAM wParam, LPARAM lParam)
1911 {
1912   int i = 0;
1913   HWND hwndPage;
1914   LRESULT msgResult = 0;
1915   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1916                                                     PropSheetInfoStr);
1917
1918   while ((i < psInfo->nPages) && (msgResult == 0))
1919   {
1920     hwndPage = psInfo->proppage[i].hwndPage;
1921     msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
1922     i++;
1923   }
1924
1925   return msgResult;
1926 }
1927
1928
1929 /******************************************************************************
1930  *            PROPSHEET_AddPage
1931  */
1932 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
1933                               HPROPSHEETPAGE hpage)
1934 {
1935   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1936                                                      PropSheetInfoStr);
1937   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1938   TCITEMW item;
1939   LPCPROPSHEETPAGEW ppsp = (LPCPROPSHEETPAGEW)hpage;
1940
1941   TRACE("hpage %p\n", hpage);
1942   /*
1943    * Allocate and fill in a new PropPageInfo entry.
1944    */
1945   psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage,
1946                                                       sizeof(PropPageInfo) *
1947                                                       (psInfo->nPages + 1));
1948   if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages))
1949       return FALSE;
1950
1951   psInfo->proppage[psInfo->nPages].hpage = hpage;
1952
1953   if (ppsp->dwFlags & PSP_PREMATURE)
1954   {
1955      /* Create the page but don't show it */
1956      PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp);
1957   }
1958
1959   /*
1960    * Add a new tab to the tab control.
1961    */
1962   item.mask = TCIF_TEXT;
1963   item.pszText = (LPWSTR) psInfo->proppage[psInfo->nPages].pszText;
1964   item.cchTextMax = MAX_TABTEXT_LENGTH;
1965
1966   if (psInfo->hImageList)
1967   {
1968     SendMessageW(hwndTabControl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
1969   }
1970
1971   if ( psInfo->proppage[psInfo->nPages].hasIcon )
1972   {
1973     item.mask |= TCIF_IMAGE;
1974     item.iImage = psInfo->nPages;
1975   }
1976
1977   SendMessageW(hwndTabControl, TCM_INSERTITEMW, psInfo->nPages + 1,
1978                (LPARAM)&item);
1979
1980   psInfo->nPages++;
1981
1982   /* If it is the only page - show it */
1983   if(psInfo->nPages == 1)
1984      PROPSHEET_SetCurSel(hwndDlg, 0, 1, 0);
1985   return TRUE;
1986 }
1987
1988 /******************************************************************************
1989  *            PROPSHEET_RemovePage
1990  */
1991 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
1992                                  int index,
1993                                  HPROPSHEETPAGE hpage)
1994 {
1995   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropW(hwndDlg,
1996                                                      PropSheetInfoStr);
1997   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1998   PropPageInfo* oldPages;
1999
2000   TRACE("index %d, hpage %p\n", index, hpage);
2001   if (!psInfo) {
2002     return FALSE;
2003   }
2004   oldPages = psInfo->proppage;
2005   /*
2006    * hpage takes precedence over index.
2007    */
2008   if (hpage != 0)
2009   {
2010     index = PROPSHEET_GetPageIndex(hpage, psInfo);
2011   }
2012
2013   /* Make sure that index is within range */
2014   if (index < 0 || index >= psInfo->nPages)
2015   {
2016       TRACE("Could not find page to remove!\n");
2017       return FALSE;
2018   }
2019
2020   TRACE("total pages %d removing page %d active page %d\n",
2021         psInfo->nPages, index, psInfo->active_page);
2022   /*
2023    * Check if we're removing the active page.
2024    */
2025   if (index == psInfo->active_page)
2026   {
2027     if (psInfo->nPages > 1)
2028     {
2029       if (index > 0)
2030       {
2031         /* activate previous page  */
2032         PROPSHEET_SetCurSel(hwndDlg, index - 1, -1, 0);
2033       }
2034       else
2035       {
2036         /* activate the next page */
2037         PROPSHEET_SetCurSel(hwndDlg, index + 1, 1, 0);
2038         psInfo->active_page = index;
2039       }
2040     }
2041     else
2042     {
2043       psInfo->active_page = -1;
2044       if (!psInfo->isModeless)
2045       {
2046          EndDialog(hwndDlg, FALSE);
2047          return TRUE;
2048       }
2049     }
2050   }
2051   else if (index < psInfo->active_page)
2052     psInfo->active_page--;
2053
2054   /* Destroy page dialog window */
2055   DestroyWindow(psInfo->proppage[index].hwndPage);
2056
2057   /* Free page resources */
2058   if(psInfo->proppage[index].hpage)
2059   {
2060      PROPSHEETPAGEW* psp = (PROPSHEETPAGEW*)psInfo->proppage[index].hpage;
2061
2062      if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[index].pszText)
2063         HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[index].pszText);
2064
2065      DestroyPropertySheetPage(psInfo->proppage[index].hpage);
2066   }
2067
2068   /* Remove the tab */
2069   SendMessageW(hwndTabControl, TCM_DELETEITEM, index, 0);
2070
2071   psInfo->nPages--;
2072   psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages);
2073
2074   if (index > 0)
2075     memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
2076
2077   if (index < psInfo->nPages)
2078     memcpy(&psInfo->proppage[index], &oldPages[index + 1],
2079            (psInfo->nPages - index) * sizeof(PropPageInfo));
2080
2081   COMCTL32_Free(oldPages);
2082
2083   return FALSE;
2084 }
2085
2086 /******************************************************************************
2087  *            PROPSHEET_SetWizButtons
2088  *
2089  * This code will work if (and assumes that) the Next button is on top of the
2090  * Finish button. ie. Finish comes after Next in the Z order.
2091  * This means make sure the dialog template reflects this.
2092  *
2093  */
2094 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
2095 {
2096   HWND hwndBack   = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
2097   HWND hwndNext   = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
2098   HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
2099
2100   TRACE("%ld\n", dwFlags);
2101
2102   EnableWindow(hwndBack, FALSE);
2103   EnableWindow(hwndNext, FALSE);
2104   EnableWindow(hwndFinish, FALSE);
2105
2106   if (dwFlags & PSWIZB_BACK)
2107     EnableWindow(hwndBack, TRUE);
2108
2109   if (dwFlags & PSWIZB_NEXT)
2110   {
2111     /* Hide the Finish button */
2112     ShowWindow(hwndFinish, SW_HIDE);
2113
2114     /* Show and enable the Next button */
2115     ShowWindow(hwndNext, SW_SHOW);
2116     EnableWindow(hwndNext, TRUE);
2117
2118     /* Set the Next button as the default pushbutton  */
2119     SendMessageA(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
2120   }
2121
2122   if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
2123   {
2124     /* Hide the Next button */
2125     ShowWindow(hwndNext, SW_HIDE);
2126
2127     /* Show the Finish button */
2128     ShowWindow(hwndFinish, SW_SHOW);
2129
2130     if (dwFlags & PSWIZB_FINISH)
2131       EnableWindow(hwndFinish, TRUE);
2132
2133     /* Set the Finish button as the default pushbutton  */
2134     SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
2135   }
2136 }
2137
2138 /******************************************************************************
2139  *            PROPSHEET_GetPageIndex
2140  *
2141  * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
2142  * the array of PropPageInfo.
2143  */
2144 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
2145 {
2146   BOOL found = FALSE;
2147   int index = 0;
2148
2149   TRACE("hpage %p\n", hpage);
2150   while ((index < psInfo->nPages) && (found == FALSE))
2151   {
2152     if (psInfo->proppage[index].hpage == hpage)
2153       found = TRUE;
2154     else
2155       index++;
2156   }
2157
2158   if (found == FALSE)
2159     index = -1;
2160
2161   return index;
2162 }
2163
2164 /******************************************************************************
2165  *            PROPSHEET_CleanUp
2166  */
2167 static void PROPSHEET_CleanUp(HWND hwndDlg)
2168 {
2169   int i;
2170   PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropW(hwndDlg,
2171                                                        PropSheetInfoStr);
2172
2173   TRACE("\n");
2174   if (!psInfo) return;
2175   if (HIWORD(psInfo->ppshheader.pszCaption))
2176       HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->ppshheader.pszCaption);
2177
2178   for (i = 0; i < psInfo->nPages; i++)
2179   {
2180      PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
2181
2182      if(psInfo->proppage[i].hwndPage)
2183         DestroyWindow(psInfo->proppage[i].hwndPage);
2184
2185      if(psp)
2186      {
2187         if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[i].pszText)
2188            HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[i].pszText);
2189
2190         DestroyPropertySheetPage(psInfo->proppage[i].hpage);
2191      }
2192   }
2193
2194   COMCTL32_Free(psInfo->proppage);
2195   COMCTL32_Free(psInfo->strPropertiesFor);
2196   ImageList_Destroy(psInfo->hImageList);
2197
2198   GlobalFree((HGLOBAL)psInfo);
2199 }
2200
2201 /******************************************************************************
2202  *            PropertySheet    (COMCTL32.87)
2203  *            PropertySheetA   (COMCTL32.88)
2204  */
2205 INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
2206 {
2207   int bRet = 0;
2208   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2209                                                        sizeof(PropSheetInfo));
2210   int i, n;
2211   BYTE* pByte;
2212
2213   TRACE("(%p)\n", lppsh);
2214
2215   PROPSHEET_CollectSheetInfoA(lppsh, psInfo);
2216
2217   psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
2218                                                     lppsh->nPages);
2219   pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2220
2221   for (n = i = 0; i < lppsh->nPages; i++, n++)
2222   {
2223     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
2224       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2225     else
2226     {
2227        psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
2228        pByte += ((LPPROPSHEETPAGEA)pByte)->dwSize;
2229     }
2230
2231     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2232                                psInfo, n))
2233     {
2234         if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
2235             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2236         n--;
2237         psInfo->nPages--;
2238     }
2239   }
2240
2241   bRet = PROPSHEET_CreateDialog(psInfo);
2242
2243   return bRet;
2244 }
2245
2246 /******************************************************************************
2247  *            PropertySheetW   (COMCTL32.89)
2248  */
2249 INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW lppsh)
2250 {
2251   int bRet = 0;
2252   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
2253                                                        sizeof(PropSheetInfo));
2254   int i, n;
2255   BYTE* pByte;
2256
2257   TRACE("(%p)\n", lppsh);
2258
2259   PROPSHEET_CollectSheetInfoW(lppsh, psInfo);
2260
2261   psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
2262                                                     lppsh->nPages);
2263   pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
2264
2265   for (n = i = 0; i < lppsh->nPages; i++, n++)
2266   {
2267     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
2268       psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
2269     else
2270     {
2271        psInfo->proppage[n].hpage = CreatePropertySheetPageW((LPCPROPSHEETPAGEW)pByte);
2272        pByte += ((LPPROPSHEETPAGEW)pByte)->dwSize;
2273     }
2274
2275     if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEW)psInfo->proppage[n].hpage,
2276                                psInfo, n))
2277     {
2278         if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
2279             DestroyPropertySheetPage(psInfo->proppage[n].hpage);
2280         n--;
2281         psInfo->nPages--;
2282     }
2283   }
2284
2285   bRet = PROPSHEET_CreateDialog(psInfo);
2286
2287   return bRet;
2288 }
2289
2290 /******************************************************************************
2291  *            CreatePropertySheetPage    (COMCTL32.18)
2292  *            CreatePropertySheetPageA   (COMCTL32.19)
2293  */
2294 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
2295                           LPCPROPSHEETPAGEA lpPropSheetPage)
2296 {
2297   PROPSHEETPAGEW* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEW));
2298
2299   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEA)));
2300
2301   if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
2302   {
2303       PROPSHEET_AtoW(&ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate);
2304   }
2305   if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2306   {
2307       PROPSHEET_AtoW(&ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon);
2308   }
2309
2310   if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2311   {
2312       PROPSHEET_AtoW(&ppsp->pszTitle, lpPropSheetPage->pszTitle);
2313   }
2314   else if ( !(ppsp->dwFlags & PSP_USETITLE) )
2315       ppsp->pszTitle = NULL;
2316
2317   return (HPROPSHEETPAGE)ppsp;
2318 }
2319
2320 /******************************************************************************
2321  *            CreatePropertySheetPageW   (COMCTL32.20)
2322  */
2323 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
2324 {
2325   PROPSHEETPAGEW* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEW));
2326
2327   memcpy(ppsp,lpPropSheetPage,min(lpPropSheetPage->dwSize,sizeof(PROPSHEETPAGEW)));
2328
2329   if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
2330   {
2331     int len = strlenW(lpPropSheetPage->u.pszTemplate);
2332
2333     ppsp->u.pszTemplate = HeapAlloc( GetProcessHeap(),0,(len+1)*sizeof (WCHAR) );
2334     strcpyW( (WCHAR *)ppsp->u.pszTemplate, lpPropSheetPage->u.pszTemplate );
2335   }
2336   if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
2337   {
2338       int len = strlenW(lpPropSheetPage->u2.pszIcon);
2339       ppsp->u2.pszIcon = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof (WCHAR) );
2340       strcpyW( (WCHAR *)ppsp->u2.pszIcon, lpPropSheetPage->u2.pszIcon );
2341   }
2342
2343   if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle ))
2344   {
2345       int len = strlenW(lpPropSheetPage->pszTitle);
2346       ppsp->pszTitle = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof (WCHAR) );
2347       strcpyW( (WCHAR *)ppsp->pszTitle, lpPropSheetPage->pszTitle );
2348   }
2349   else if ( !(ppsp->dwFlags & PSP_USETITLE) )
2350       ppsp->pszTitle = NULL;
2351
2352   return (HPROPSHEETPAGE)ppsp;
2353 }
2354
2355 /******************************************************************************
2356  *            DestroyPropertySheetPage   (COMCTL32.24)
2357  */
2358 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
2359 {
2360   PROPSHEETPAGEW *psp = (PROPSHEETPAGEW *)hPropPage;
2361
2362   if (!psp)
2363      return FALSE;
2364
2365   if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u.pszTemplate ) )
2366      HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u.pszTemplate);
2367
2368   if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) )
2369      HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u2.pszIcon);
2370
2371   if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle ))
2372       HeapFree(GetProcessHeap(), 0, (LPVOID)psp->pszTitle);
2373
2374   COMCTL32_Free(hPropPage);
2375
2376   return TRUE;
2377 }
2378
2379 /******************************************************************************
2380  *            PROPSHEET_IsDialogMessage
2381  */
2382 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
2383 {
2384    PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd, PropSheetInfoStr);
2385
2386    TRACE("\n");
2387    if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
2388       return FALSE;
2389
2390    if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
2391    {
2392       int new_page = 0;
2393       INT dlgCode = SendMessageA(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
2394
2395       if (!(dlgCode & DLGC_WANTMESSAGE))
2396       {
2397          switch (lpMsg->wParam)
2398          {
2399             case VK_TAB:
2400                if (GetKeyState(VK_SHIFT) & 0x8000)
2401                    new_page = -1;
2402                 else
2403                    new_page = 1;
2404                break;
2405
2406             case VK_NEXT:   new_page = 1;  break;
2407             case VK_PRIOR:  new_page = -1; break;
2408          }
2409       }
2410
2411       if (new_page)
2412       {
2413          if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
2414          {
2415             new_page += psInfo->active_page;
2416
2417             if (new_page < 0)
2418                new_page = psInfo->nPages - 1;
2419             else if (new_page >= psInfo->nPages)
2420                new_page = 0;
2421
2422             PROPSHEET_SetCurSel(hwnd, new_page, 1, 0);
2423          }
2424
2425          return TRUE;
2426       }
2427    }
2428
2429    return IsDialogMessageA(hwnd, lpMsg);
2430 }
2431
2432 /******************************************************************************
2433  *            PROPSHEET_DoCommand
2434  */
2435 static BOOL PROPSHEET_DoCommand(HWND hwnd, WORD wID)
2436 {
2437
2438     switch (wID) {
2439
2440     case IDOK:
2441     case IDC_APPLY_BUTTON:
2442         {
2443             HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
2444
2445             if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
2446                 break;
2447
2448             if (wID == IDOK)
2449                 {
2450                     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2451                                                                       PropSheetInfoStr);
2452                     int result = TRUE;
2453
2454                     if (psInfo->restartWindows)
2455                         result = ID_PSRESTARTWINDOWS;
2456
2457                     /* reboot system takes precedence over restart windows */
2458                     if (psInfo->rebootSystem)
2459                         result = ID_PSREBOOTSYSTEM;
2460
2461                     if (psInfo->isModeless)
2462                         psInfo->activeValid = FALSE;
2463                     else
2464                         EndDialog(hwnd, result);
2465                 }
2466             else
2467                 EnableWindow(hwndApplyBtn, FALSE);
2468
2469             break;
2470         }
2471
2472     case IDC_BACK_BUTTON:
2473         PROPSHEET_Back(hwnd);
2474         break;
2475
2476     case IDC_NEXT_BUTTON:
2477         PROPSHEET_Next(hwnd);
2478         break;
2479
2480     case IDC_FINISH_BUTTON:
2481         PROPSHEET_Finish(hwnd);
2482         break;
2483
2484     case IDCANCEL:
2485         PROPSHEET_Cancel(hwnd, 0);
2486         break;
2487
2488     case IDHELP:
2489         PROPSHEET_Help(hwnd);
2490         break;
2491     }
2492
2493     return TRUE;
2494 }
2495
2496 /******************************************************************************
2497  *            PROPSHEET_DialogProc
2498  */
2499 BOOL WINAPI
2500 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2501 {
2502   TRACE("hwnd=%x msg=%x wparam=%x lparam=%lx\n",
2503         hwnd, uMsg, wParam, lParam);
2504
2505   switch (uMsg)
2506   {
2507     case WM_INITDIALOG:
2508     {
2509       PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
2510       WCHAR* strCaption = (WCHAR*)COMCTL32_Alloc(MAX_CAPTION_LENGTH*sizeof(WCHAR));
2511       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2512       LPCPROPSHEETPAGEW ppshpage;
2513       int idx;
2514
2515       SetPropW(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
2516
2517       /*
2518        * psInfo->hwnd is not being used by WINE code - it exists
2519        * for compatibility with "real" Windoze. The same about
2520        * SetWindowLong - WINE is only using the PropSheetInfoStr
2521        * property.
2522        */
2523       psInfo->hwnd = hwnd;
2524       SetWindowLongW(hwnd,DWL_USER,(LONG)psInfo);
2525
2526       /*
2527        * Small icon in the title bar.
2528        */
2529       if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
2530           (psInfo->ppshheader.dwFlags & PSH_USEHICON))
2531       {
2532         HICON hIcon;
2533         int icon_cx = GetSystemMetrics(SM_CXSMICON);
2534         int icon_cy = GetSystemMetrics(SM_CYSMICON);
2535
2536         if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
2537           hIcon = LoadImageW(psInfo->ppshheader.hInstance,
2538                              psInfo->ppshheader.u.pszIcon,
2539                              IMAGE_ICON,
2540                              icon_cx, icon_cy,
2541                              LR_DEFAULTCOLOR);
2542         else
2543           hIcon = psInfo->ppshheader.u.hIcon;
2544
2545         SendMessageW(hwnd, WM_SETICON, 0, hIcon);
2546       }
2547
2548       if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
2549         SendMessageW(hwnd, WM_SETICON, 0, psInfo->ppshheader.u.hIcon);
2550
2551       psInfo->strPropertiesFor = strCaption;
2552
2553       GetWindowTextW(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
2554
2555       PROPSHEET_CreateTabControl(hwnd, psInfo);
2556
2557       if (psInfo->ppshheader.dwFlags & INTRNL_ANY_WIZARD)
2558       {
2559         if (PROPSHEET_IsTooSmallWizard(hwnd, psInfo))
2560         {
2561           PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
2562           PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
2563         }
2564       }
2565       else
2566       {
2567         if (PROPSHEET_SizeMismatch(hwnd, psInfo))
2568         {
2569           PROPSHEET_AdjustSize(hwnd, psInfo);
2570           PROPSHEET_AdjustButtons(hwnd, psInfo);
2571         }
2572       }
2573
2574       if (psInfo->useCallback)
2575              (*(psInfo->ppshheader.pfnCallback))(hwnd,
2576                                               PSCB_INITIALIZED, (LPARAM)0);
2577
2578       idx = psInfo->active_page;
2579       ppshpage = (LPCPROPSHEETPAGEW)psInfo->proppage[idx].hpage;
2580       psInfo->active_page = -1;
2581
2582       PROPSHEET_SetCurSel(hwnd, idx, 1, psInfo->proppage[idx].hpage);
2583
2584       /* doing TCM_SETCURSEL seems to be needed even in case of PSH_WIZARD,
2585        * as some programs call TCM_GETCURSEL to get the current selection
2586        * from which to switch to the next page */
2587       SendMessageW(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
2588
2589       if (!HIWORD(psInfo->ppshheader.pszCaption) &&
2590               psInfo->ppshheader.hInstance)
2591       {
2592          WCHAR szText[256];
2593
2594          if (LoadStringW(psInfo->ppshheader.hInstance,
2595                  (UINT)psInfo->ppshheader.pszCaption, szText, 255))
2596             PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags, szText);
2597       }
2598       else
2599       {
2600          PROPSHEET_SetTitleW(hwnd, psInfo->ppshheader.dwFlags,
2601                          psInfo->ppshheader.pszCaption);
2602       }
2603
2604       return TRUE;
2605     }
2606
2607     case WM_DESTROY:
2608       PROPSHEET_CleanUp(hwnd);
2609       return TRUE;
2610
2611     case WM_CLOSE:
2612       PROPSHEET_Cancel(hwnd, 1);
2613       return TRUE;
2614
2615     case WM_COMMAND:
2616       return PROPSHEET_DoCommand(hwnd, LOWORD(wParam));
2617
2618     case WM_NOTIFY:
2619     {
2620       NMHDR* pnmh = (LPNMHDR) lParam;
2621
2622       if (pnmh->code == TCN_SELCHANGE)
2623       {
2624         int index = SendMessageW(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
2625         PROPSHEET_SetCurSel(hwnd, index, 1, 0);
2626       }
2627
2628       if(pnmh->code == TCN_SELCHANGING)
2629       {
2630         BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
2631         SetWindowLongW(hwnd, DWL_MSGRESULT, !bRet);
2632         return TRUE;
2633       }
2634
2635       return FALSE;
2636     }
2637
2638     case PSM_GETCURRENTPAGEHWND:
2639     {
2640       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2641                                                         PropSheetInfoStr);
2642       HWND hwndPage = 0;
2643
2644       if (psInfo->activeValid && psInfo->active_page != -1)
2645         hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
2646
2647       SetWindowLongW(hwnd, DWL_MSGRESULT, hwndPage);
2648
2649       return TRUE;
2650     }
2651
2652     case PSM_CHANGED:
2653       PROPSHEET_Changed(hwnd, (HWND)wParam);
2654       return TRUE;
2655
2656     case PSM_UNCHANGED:
2657       PROPSHEET_UnChanged(hwnd, (HWND)wParam);
2658       return TRUE;
2659
2660     case PSM_GETTABCONTROL:
2661     {
2662       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2663
2664       SetWindowLongW(hwnd, DWL_MSGRESULT, hwndTabCtrl);
2665
2666       return TRUE;
2667     }
2668
2669     case PSM_SETCURSEL:
2670     {
2671       BOOL msgResult;
2672
2673       msgResult = PROPSHEET_CanSetCurSel(hwnd);
2674       if(msgResult != FALSE)
2675       {
2676         msgResult = PROPSHEET_SetCurSel(hwnd,
2677                                        (int)wParam,
2678                                        1,
2679                                        (HPROPSHEETPAGE)lParam);
2680       }
2681
2682       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2683
2684       return TRUE;
2685     }
2686
2687     case PSM_CANCELTOCLOSE:
2688     {
2689       WCHAR buf[MAX_BUTTONTEXT_LENGTH];
2690       HWND hwndOK = GetDlgItem(hwnd, IDOK);
2691       HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
2692
2693       EnableWindow(hwndCancel, FALSE);
2694       if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)))
2695          SetWindowTextW(hwndOK, buf);
2696
2697       return FALSE;
2698     }
2699
2700     case PSM_RESTARTWINDOWS:
2701     {
2702       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2703                                                         PropSheetInfoStr);
2704
2705       psInfo->restartWindows = TRUE;
2706       return TRUE;
2707     }
2708
2709     case PSM_REBOOTSYSTEM:
2710     {
2711       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropW(hwnd,
2712                                                         PropSheetInfoStr);
2713
2714       psInfo->rebootSystem = TRUE;
2715       return TRUE;
2716     }
2717
2718     case PSM_SETTITLEA:
2719       PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
2720       return TRUE;
2721
2722     case PSM_SETTITLEW:
2723       PROPSHEET_SetTitleW(hwnd, (DWORD) wParam, (LPCWSTR) lParam);
2724       return TRUE;
2725
2726     case PSM_APPLY:
2727     {
2728       BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
2729
2730       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2731
2732       return TRUE;
2733     }
2734
2735     case PSM_QUERYSIBLINGS:
2736     {
2737       LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
2738
2739       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2740
2741       return TRUE;
2742     }
2743
2744     case PSM_ADDPAGE:
2745     {
2746       /*
2747        * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have
2748        *       a return value. This is not true. PSM_ADDPAGE returns TRUE
2749        *       on success or FALSE otherwise, as specified on MSDN Online.
2750        *       Also see the MFC code for
2751        *       CPropertySheet::AddPage(CPropertyPage* pPage).
2752        */
2753
2754       BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
2755
2756       SetWindowLongW(hwnd, DWL_MSGRESULT, msgResult);
2757
2758       return TRUE;
2759     }
2760
2761     case PSM_REMOVEPAGE:
2762       PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
2763       return TRUE;
2764
2765     case PSM_ISDIALOGMESSAGE:
2766     {
2767        BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
2768        SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2769        return TRUE;
2770     }
2771
2772     case PSM_PRESSBUTTON:
2773       PROPSHEET_PressButton(hwnd, (int)wParam);
2774       return TRUE;
2775
2776     case PSM_SETFINISHTEXTA:
2777       PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
2778       return TRUE;
2779
2780     case PSM_SETWIZBUTTONS:
2781       PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
2782       return TRUE;
2783
2784     case PSM_SETCURSELID:
2785         FIXME("Unimplemented msg PSM_SETCURSELID\n");
2786         return FALSE;
2787
2788     case PSM_SETFINISHTEXTW:
2789         PROPSHEET_SetFinishTextW(hwnd, (LPCWSTR) lParam);
2790         return FALSE;
2791
2792     default:
2793       return FALSE;
2794   }
2795
2796   return FALSE;
2797 }
2798