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