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