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