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