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