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