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