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