Fix (well kludge around) FreeBSD's `Invalid address' errors.
[wine] / dlls / comctl32 / propsheet.c
1 /*
2  * Property Sheets
3  *
4  * Copyright 1998 Francis Beaudet
5  * Copyright 1999 Thuy Nguyen
6  *
7  * TODO:
8  *   - Modeless mode
9  *   - Wizard mode
10  *   - Unicode property sheets
11  */
12
13 #include <string.h>
14 #include "winbase.h"
15 #include "commctrl.h"
16 #include "prsht.h"
17 #include "winnls.h"
18 #include "comctl32.h"
19 #include "debugtools.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   int index; /* corresponds to the index in ppshheader->ppsp */
37   HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
38   HWND hwndPage;
39   BOOL isDirty;
40   LPCWSTR pszText;
41   BOOL hasHelp;
42   BOOL useCallback;
43 } PropPageInfo;
44
45 typedef struct tagPropSheetInfo
46 {
47   LPSTR strPropertiesFor;
48   int nPages;
49   int active_page;
50   LPCPROPSHEETHEADERA ppshheader;
51   BOOL isModeless;
52   BOOL hasHelp;
53   BOOL hasApply;
54   BOOL useCallback;
55   BOOL restartWindows;
56   BOOL rebootSystem;
57   PropPageInfo* proppage;
58   int x;
59   int y;
60   int width;
61   int height;
62 } PropSheetInfo;
63
64 typedef struct
65 {
66   int x;
67   int y;
68 } PADDING_INFO;
69
70 /******************************************************************************
71  * Defines and global variables
72  */
73
74 const char * PropSheetInfoStr = "PropertySheetInfo";
75
76 #define MAX_CAPTION_LENGTH 255
77 #define MAX_TABTEXT_LENGTH 255
78
79
80 /******************************************************************************
81  * Prototypes
82  */
83 static BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo);
84 static BOOL PROPSHEET_IsTooSmall(HWND hwndDlg, PropSheetInfo* psInfo);
85 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo);
86 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo);
87 static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh,
88                                        PropSheetInfo * psInfo);
89 static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp,
90                                       PropSheetInfo * psInfo,
91                                       int index);
92 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
93                                        PropSheetInfo * psInfo);
94 static int PROPSHEET_CreatePage(HWND hwndParent, int index,
95                                 const PropSheetInfo * psInfo,
96                                 LPCPROPSHEETPAGEA ppshpage,
97                                 BOOL showPage);
98 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo);
99 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg);
100 static BOOL PROPSHEET_Apply(HWND hwndDlg);
101 static void PROPSHEET_Cancel(HWND hwndDlg);
102 static void PROPSHEET_Help(HWND hwndDlg);
103 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage);
104 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage);
105 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID);
106 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText);
107 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
108                                 int index,
109                                 HPROPSHEETPAGE hpage);
110 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
111                                        WPARAM wParam, LPARAM lParam);
112 static LPCPROPSHEETPAGEA PROPSHEET_GetPSPPage(const PropSheetInfo * psInfo,
113                                               int index);
114 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
115                               HPROPSHEETPAGE hpage);
116
117 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
118                                  int index,
119                                  HPROPSHEETPAGE hpage);
120 static void PROPSHEET_CleanUp();
121 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo);
122
123 BOOL WINAPI
124 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
125
126 DEFAULT_DEBUG_CHANNEL(propsheet)
127
128 /******************************************************************************
129  *            PROPSHEET_CollectSheetInfo
130  *
131  * Collect relevant data.
132  */
133 static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh,
134                                        PropSheetInfo * psInfo)
135 {
136   DWORD dwFlags = lppsh->dwFlags;
137
138   psInfo->hasHelp = dwFlags & PSH_HASHELP;
139   psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
140   psInfo->useCallback = dwFlags & PSH_USECALLBACK;
141   psInfo->isModeless = dwFlags & PSH_MODELESS;
142   psInfo->ppshheader = lppsh;
143   psInfo->nPages = lppsh->nPages;
144
145   if (dwFlags & PSH_USEPSTARTPAGE)
146   {
147     TRACE("PSH_USEPSTARTPAGE is on");
148     psInfo->active_page = 0;
149   }
150   else
151     psInfo->active_page = lppsh->u2.nStartPage;
152
153   psInfo->restartWindows = FALSE;
154   psInfo->rebootSystem = FALSE;
155
156   return TRUE;
157 }
158
159 /******************************************************************************
160  *            PROPSHEET_CollectPageInfo
161  *
162  * Collect property sheet data.
163  * With code taken from DIALOG_ParseTemplate32.
164  */
165 BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp,
166                                PropSheetInfo * psInfo,
167                                int index)
168 {
169   DLGTEMPLATE* pTemplate;
170   const WORD*  p;
171   DWORD dwFlags;
172   int width, height;
173
174   if (psInfo->ppshheader->dwFlags & PSH_PROPSHEETPAGE)
175     psInfo->proppage[index].hpage = 0;
176   psInfo->proppage[index].hwndPage = 0;
177   psInfo->proppage[index].isDirty = FALSE;
178
179   /*
180    * Process property page flags.
181    */
182   dwFlags = lppsp->dwFlags;
183   psInfo->proppage[index].useCallback = dwFlags & PSP_USECALLBACK;
184   psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP;
185
186   /* as soon as we have a page with the help flag, set the sheet flag on */
187   if (psInfo->proppage[index].hasHelp)
188     psInfo->hasHelp = TRUE;
189
190   /*
191    * Process page template.
192    */
193   if (dwFlags & PSP_DLGINDIRECT)
194     pTemplate = (DLGTEMPLATE*)lppsp->u1.pResource;
195   else
196   {
197     HRSRC hResource = FindResourceA(lppsp->hInstance,
198                                     lppsp->u1.pszTemplate,
199                                     RT_DIALOGA);
200     HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
201                                      hResource);
202     pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
203   }
204
205   /*
206    * Extract the size of the page and the caption.
207    */
208   p = (const WORD *)pTemplate;
209
210   if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
211   {
212     /* DIALOGEX template */
213
214     p++;       /* dlgVer    */
215     p++;       /* signature */
216     p += 2;    /* help ID   */
217     p += 2;    /* ext style */
218     p += 2;    /* style     */
219   }
220   else
221   {
222     /* DIALOG template */
223
224     p += 2;    /* style     */
225     p += 2;    /* ext style */
226   }
227
228   p++;    /* nb items */
229   p++;    /*   x      */
230   p++;    /*   y      */
231   width  = (WORD)*p; p++;
232   height = (WORD)*p; p++;
233
234   /* remember the largest width and height */
235   if (width > psInfo->width)
236     psInfo->width = width;
237
238   if (height > psInfo->height)
239     psInfo->height = height;
240
241   /* menu */
242   switch ((WORD)*p)
243   {
244     case 0x0000:
245       p++;
246       break;
247     case 0xffff:
248       p += 2;
249       break;
250     default:
251       p += lstrlenW( (LPCWSTR)p ) + 1;
252       break;
253   } 
254
255   /* class */
256   switch ((WORD)*p)
257   {
258     case 0x0000:
259       p++;
260       break;
261     case 0xffff:
262       p += 2;
263       break;
264     default:
265       p += lstrlenW( (LPCWSTR)p ) + 1;
266       break;
267   }
268
269   /* Extract the caption */
270   psInfo->proppage[index].pszText = (LPCWSTR)p;
271   TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p));
272   p += lstrlenW((LPCWSTR)p) + 1;
273
274   return TRUE;
275 }
276
277 /******************************************************************************
278  *            PROPSHEET_CreateDialog
279  *
280  * Creates the actual property sheet.
281  */
282 BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
283 {
284   LRESULT ret;
285   LPCVOID template;
286   HRSRC hRes;
287
288   if(!(hRes = FindResourceA(COMCTL32_hModule,
289                             MAKEINTRESOURCEA(IDD_PROPSHEET),
290                             RT_DIALOGA)))
291     return FALSE;
292
293   if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
294     return FALSE;
295
296   if (psInfo->useCallback)
297     (*(psInfo->ppshheader->pfnCallback))(0, PSCB_PRECREATE, (LPARAM)template);
298
299   if (psInfo->ppshheader->dwFlags & PSH_MODELESS)
300     ret = CreateDialogIndirectParamA(psInfo->ppshheader->hInstance,
301                                      (LPDLGTEMPLATEA) template,
302                                      psInfo->ppshheader->hwndParent,
303                                      (DLGPROC) PROPSHEET_DialogProc,
304                                      (LPARAM)psInfo);
305   else
306     ret = DialogBoxIndirectParamA(psInfo->ppshheader->hInstance,
307                                   (LPDLGTEMPLATEA) template,
308                                   psInfo->ppshheader->hwndParent,
309                                   (DLGPROC) PROPSHEET_DialogProc,
310                                   (LPARAM)psInfo);
311
312   return ret;
313 }
314
315 /******************************************************************************
316  *            PROPSHEET_IsTooSmall
317  * 
318  * Verify that the resource property sheet is big enough.
319  */
320 static BOOL PROPSHEET_IsTooSmall(HWND hwndDlg, PropSheetInfo* psInfo)
321 {
322   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
323   RECT rcOrigTab, rcPage;
324
325   /*
326    * Original tab size.
327    */
328   GetClientRect(hwndTabCtrl, &rcOrigTab);
329   TRACE("orig tab %d %d %d %d\n", rcOrigTab.left, rcOrigTab.top,
330         rcOrigTab.right, rcOrigTab.bottom);
331
332   /*
333    * Biggest page size.
334    */
335   rcPage.left   = psInfo->x;
336   rcPage.top    = psInfo->y;
337   rcPage.right  = psInfo->width;
338   rcPage.bottom = psInfo->height;
339
340   MapDialogRect(hwndDlg, &rcPage);
341   TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
342         rcPage.right, rcPage.bottom);
343
344   if (rcPage.right > rcOrigTab.right)
345     return TRUE;
346
347   if (rcPage.bottom > rcOrigTab.bottom)
348     return TRUE;
349
350   return FALSE;
351 }
352
353 /******************************************************************************
354  *            PROPSHEET_AdjustSize
355  *
356  * Resizes the property sheet and the tab control to fit the largest page.
357  */
358 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
359 {
360   HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
361   HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
362   RECT rc;
363   int tabOffsetX, tabOffsetY, buttonHeight;
364   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
365
366   /* Get the height of buttons */
367   GetClientRect(hwndButton, &rc);
368   buttonHeight = rc.bottom;
369
370   /*
371    * Biggest page size.
372    */
373   rc.left   = psInfo->x;
374   rc.top    = psInfo->y;
375   rc.right  = psInfo->width;
376   rc.bottom = psInfo->height;
377
378   MapDialogRect(hwndDlg, &rc);
379
380   /*
381    * Resize the tab control.
382    */
383   SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
384
385   tabOffsetX = -(rc.left);
386   tabOffsetY = -(rc.top);
387
388   rc.right -= rc.left;
389   rc.bottom -= rc.top;
390   SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
391                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
392
393   GetClientRect(hwndTabCtrl, &rc);
394
395   TRACE("tab client rc %d %d %d %d\n",
396         rc.left, rc.top, rc.right, rc.bottom);
397
398   rc.right += ((padding.x * 2) + tabOffsetX);
399   rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
400
401   /*
402    * Resize the property sheet.
403    */
404   SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
405                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
406
407   return TRUE;
408 }
409
410 /******************************************************************************
411  *            PROPSHEET_AdjustButtons
412  *
413  * Adjusts the buttons' positions.
414  */
415 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo)
416 {
417   HWND hwndButton = GetDlgItem(hwndParent, IDOK);
418   RECT rcSheet;
419   int x, y;
420   int num_buttons = 2;
421   int buttonWidth, buttonHeight;
422   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
423
424   if (psInfo->hasApply)
425     num_buttons++;
426
427   if (psInfo->hasHelp)
428     num_buttons++;
429
430   /*
431    * Obtain the size of the buttons.
432    */
433   GetClientRect(hwndButton, &rcSheet);
434   buttonWidth = rcSheet.right;
435   buttonHeight = rcSheet.bottom;
436
437   /*
438    * Get the size of the property sheet.
439    */ 
440   GetClientRect(hwndParent, &rcSheet);
441
442   /* 
443    * All buttons will be at this y coordinate.
444    */
445   y = rcSheet.bottom - (padding.y + buttonHeight);
446
447   /*
448    * Position OK button.
449    */
450   hwndButton = GetDlgItem(hwndParent, IDOK);
451
452   x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
453
454   SetWindowPos(hwndButton, 0, x, y, 0, 0,
455                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
456
457   /*
458    * Position Cancel button.
459    */
460   hwndButton = GetDlgItem(hwndParent, IDCANCEL);
461
462   x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
463
464   SetWindowPos(hwndButton, 0, x, y, 0, 0,
465                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
466
467   /*
468    * Position Apply button.
469    */
470   hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
471
472   if (psInfo->hasApply)
473   {
474     if (psInfo->hasHelp)
475       x = rcSheet.right - ((padding.x + buttonWidth) * 2);
476     else
477       x = rcSheet.right - (padding.x + buttonWidth);
478   
479     SetWindowPos(hwndButton, 0, x, y, 0, 0,
480                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
481
482     EnableWindow(hwndButton, FALSE);
483   }
484   else
485     ShowWindow(hwndButton, SW_HIDE);
486
487   /*
488    * Position Help button.
489    */
490   hwndButton = GetDlgItem(hwndParent, IDHELP);
491
492   if (psInfo->hasHelp)
493   {
494     x = rcSheet.right - (padding.x + buttonWidth);
495   
496     SetWindowPos(hwndButton, 0, x, y, 0, 0,
497                  SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
498   }
499   else
500     ShowWindow(hwndButton, SW_HIDE);
501
502   return TRUE;
503 }
504
505 /******************************************************************************
506  *            PROPSHEET_GetPaddingInfo
507  *
508  * Returns the layout information.
509  */
510 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
511 {
512   HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
513   RECT rcTab;
514   POINT tl;
515   PADDING_INFO padding;
516
517   GetWindowRect(hwndTab, &rcTab);
518
519   tl.x = rcTab.left;
520   tl.y = rcTab.top;
521
522   ScreenToClient(hwndDlg, &tl);
523
524   padding.x = tl.x;
525   padding.y = tl.y;
526
527   return padding;
528 }
529
530 /******************************************************************************
531  *            PROPSHEET_CreateTabControl
532  *
533  * Insert the tabs in the tab control.
534  */
535 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
536                                        PropSheetInfo * psInfo)
537 {
538   HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
539   TCITEMA item;
540   int i, nTabs;
541   char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
542
543   item.mask = TCIF_TEXT;
544   item.pszText = tabtext;
545   item.cchTextMax = MAX_TABTEXT_LENGTH;
546
547   nTabs = psInfo->ppshheader->nPages;
548
549   for (i = 0; i < nTabs; i++)
550   {
551     WideCharToMultiByte(CP_ACP, 0,
552                         (LPCWSTR)psInfo->proppage[i].pszText,
553                         -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
554
555     SendMessageA(hwndTabCtrl, TCM_INSERTITEMA, (WPARAM)i, (LPARAM)&item);
556   }
557
558   return TRUE;
559 }
560
561 /******************************************************************************
562  *            PROPSHEET_CreatePage
563  *
564  * Creates a page.
565  */
566 static int PROPSHEET_CreatePage(HWND hwndParent,
567                                 int index,
568                                 const PropSheetInfo * psInfo,
569                                 LPCPROPSHEETPAGEA ppshpage,
570                                 BOOL showPage)
571 {
572   DLGTEMPLATE* pTemplate;
573   HWND hwndPage;
574   RECT rc;
575   PropPageInfo* ppInfo = psInfo->proppage;
576   PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
577   HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
578
579   TRACE("index %d\n", index);
580
581   if (ppshpage->dwFlags & PSP_DLGINDIRECT)
582     pTemplate = (DLGTEMPLATE*)ppshpage->u1.pResource;
583   else
584   {
585     HRSRC hResource = FindResourceA(ppshpage->hInstance,
586                                     ppshpage->u1.pszTemplate,
587                                     RT_DIALOGA);
588     HGLOBAL hTemplate = LoadResource(ppshpage->hInstance, hResource);
589     pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
590   }
591
592   if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
593   {
594     ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD;
595     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME;
596     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION;
597     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU;
598     ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP;
599   }
600   else
601   {
602     pTemplate->style |= WS_CHILD;
603     pTemplate->style &= ~DS_MODALFRAME;
604     pTemplate->style &= ~WS_CAPTION;
605     pTemplate->style &= ~WS_SYSMENU;
606     pTemplate->style &= ~WS_POPUP;
607   }
608
609   if (psInfo->proppage[index].useCallback)
610     (*(ppshpage->pfnCallback))(hwndParent,
611                                PSPCB_CREATE,
612                                (LPPROPSHEETPAGEA)ppshpage);
613
614   hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
615                                         pTemplate,
616                                         hwndParent,
617                                         ppshpage->pfnDlgProc,
618                                         (LPARAM)ppshpage);
619
620   ppInfo[index].hwndPage = hwndPage;
621
622   rc.left = psInfo->x;
623   rc.top = psInfo->y;
624   rc.right = psInfo->width;
625   rc.bottom = psInfo->height;
626
627   MapDialogRect(hwndParent, &rc);
628
629   /*
630    * Ask the Tab control to fit this page in.
631    */
632   SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&rc);
633
634   SetWindowPos(hwndPage, HWND_TOP,
635                rc.left + padding.x,
636                rc.top + padding.y,
637                0, 0, SWP_NOSIZE);
638
639   if (showPage)
640     ShowWindow(hwndPage, SW_SHOW);
641   else
642     ShowWindow(hwndPage, SW_HIDE);
643
644   return TRUE;
645 }
646
647 /******************************************************************************
648  *            PROPSHEET_ShowPage
649  *
650  * Displays or creates the specified page.
651  */
652 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
653 {
654   if (index == psInfo->active_page)
655     return TRUE;
656
657   ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
658
659   if (psInfo->proppage[index].hwndPage != 0)
660     ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
661   else
662   {
663     LPCPROPSHEETPAGEA ppshpage = PROPSHEET_GetPSPPage(psInfo, index);
664     PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage, TRUE);
665   }
666
667   psInfo->active_page = index;
668
669   return TRUE;
670 }
671
672 /******************************************************************************
673  *            PROPSHEET_Apply
674  */
675 static BOOL PROPSHEET_Apply(HWND hwndDlg)
676 {
677   int i;
678   NMHDR hdr;
679   HWND hwndPage;
680   LRESULT msgResult;
681   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
682                                                     PropSheetInfoStr);
683
684   hdr.hwndFrom = hwndDlg;
685
686   /*
687    * Send PSN_KILLACTIVE to the current page.
688    */
689   hdr.code = PSN_KILLACTIVE;
690
691   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
692
693   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr) != FALSE)
694     return FALSE;
695
696   /*
697    * Send PSN_APPLY to all pages.
698    */
699   hdr.code = PSN_APPLY;
700
701   for (i = 0; i < psInfo->nPages; i++)
702   {
703     hwndPage = psInfo->proppage[i].hwndPage;
704     msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
705
706     if (msgResult == PSNRET_INVALID_NOCHANGEPAGE)
707       return FALSE;
708   }
709
710   return TRUE;
711 }
712
713 /******************************************************************************
714  *            PROPSHEET_Cancel
715  */
716 static void PROPSHEET_Cancel(HWND hwndDlg)
717 {
718   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
719                                                     PropSheetInfoStr);
720   HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
721   NMHDR hdr;
722
723   hdr.hwndFrom = hwndDlg;
724   hdr.code = PSN_QUERYCANCEL;
725
726   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr))
727     return;
728
729   hdr.code = PSN_RESET;
730
731   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
732
733   if (psInfo->isModeless)
734     psInfo->active_page = -1; /* makes PSM_GETCURRENTPAGEHWND return NULL */
735   else
736     EndDialog(hwndDlg, FALSE);
737 }
738
739 /******************************************************************************
740  *            PROPSHEET_Help
741  */
742 static void PROPSHEET_Help(HWND hwndDlg)
743 {
744   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
745                                                     PropSheetInfoStr);
746   HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
747   NMHDR hdr;
748
749   hdr.hwndFrom = hwndDlg;
750   hdr.code = PSN_HELP;
751
752   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
753 }
754
755 /******************************************************************************
756  *            PROPSHEET_Changed
757  */
758 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
759 {
760   int i;
761   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
762                                                     PropSheetInfoStr);
763
764   /*
765    * Set the dirty flag of this page.
766    */
767   for (i = 0; i < psInfo->nPages; i++)
768   {
769     if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
770       psInfo->proppage[i].isDirty = TRUE;
771   }
772
773   /*
774    * Enable the Apply button.
775    */
776   if (psInfo->hasApply)
777   {
778     HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
779
780     EnableWindow(hwndApplyBtn, TRUE);
781   }
782 }
783
784 /******************************************************************************
785  *            PROPSHEET_UnChanged
786  */
787 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
788 {
789   int i;
790   BOOL noPageDirty = TRUE;
791   HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
792   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
793                                                     PropSheetInfoStr);
794
795   for (i = 0; i < psInfo->nPages; i++)
796   {
797     /* set the specified page as clean */
798     if (psInfo->proppage[i].hwndPage == hwndCleanPage)
799       psInfo->proppage[i].isDirty = FALSE;
800
801     /* look to see if there's any dirty pages */
802     if (psInfo->proppage[i].isDirty)
803       noPageDirty = FALSE;
804   }
805
806   /*
807    * Disable Apply button.
808    */
809   if (noPageDirty)
810     EnableWindow(hwndApplyBtn, FALSE);
811 }
812
813 /******************************************************************************
814  *            PROPSHEET_PressButton
815  */
816 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
817 {
818   switch (buttonID)
819   {
820     case PSBTN_APPLYNOW:
821       SendMessageA(hwndDlg, WM_COMMAND, IDC_APPLY_BUTTON, 0);
822       break;
823     case PSBTN_BACK:
824       FIXME("Wizard mode not implemented.\n");
825       break;
826     case PSBTN_CANCEL:
827       SendMessageA(hwndDlg, WM_COMMAND, IDCANCEL, 0);
828       break;
829     case PSBTN_FINISH:
830       FIXME("Wizard mode not implemented.\n");
831       break;
832     case PSBTN_HELP:
833       SendMessageA(hwndDlg, WM_COMMAND, IDHELP, 0);
834       break;
835     case PSBTN_NEXT:
836       FIXME("Wizard mode not implemented.\n");
837       break;
838     case PSBTN_OK:
839       SendMessageA(hwndDlg, WM_COMMAND, IDOK, 0);
840       break;
841     default:
842       FIXME("Invalid button index %d\n", buttonID);
843   }
844 }
845
846 /******************************************************************************
847  *            PROPSHEET_SetCurSel
848  */
849 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
850                                 int index,
851                                 HPROPSHEETPAGE hpage)
852 {
853   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
854                                                     PropSheetInfoStr);
855   HWND hwndPage;
856   HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
857   NMHDR hdr;
858
859   /*
860    * Notify the current page.
861    */
862   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
863
864   hdr.hwndFrom = hwndDlg;
865   hdr.code = PSN_KILLACTIVE;
866
867   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr))
868     return FALSE;
869
870   if (hpage != NULL)
871     FIXME("Implement HPROPSHEETPAGE!\n");
872   else
873     hwndPage = psInfo->proppage[index].hwndPage;
874
875   /*
876    * Notify the new page.
877    */
878   hdr.code = PSN_SETACTIVE;
879
880   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
881
882   /*
883    * Display the new page.
884    */
885   PROPSHEET_ShowPage(hwndDlg, index, psInfo);
886
887   if (psInfo->proppage[index].hasHelp)
888     EnableWindow(hwndHelp, TRUE);
889   else
890     EnableWindow(hwndHelp, FALSE);
891
892   return TRUE;
893 }
894
895 /******************************************************************************
896  *            PROPSHEET_SetTitleA
897  */
898 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
899 {
900   if (dwStyle & PSH_PROPTITLE)
901   {
902     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
903                                                       PropSheetInfoStr);
904     char* dest;
905     int lentitle = strlen(lpszText);
906     int lenprop  = strlen(psInfo->strPropertiesFor);
907
908     dest = COMCTL32_Alloc(lentitle + lenprop + 1);
909     strcpy(dest, psInfo->strPropertiesFor);
910     strcat(dest, lpszText);
911
912     SetWindowTextA(hwndDlg, dest);
913     COMCTL32_Free(dest);
914   }
915   else
916     SetWindowTextA(hwndDlg, lpszText);
917 }
918
919 /******************************************************************************
920  *            PROPSHEET_QuerySiblings
921  */
922 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
923                                        WPARAM wParam, LPARAM lParam)
924 {
925   int i = 0;
926   HWND hwndPage;
927   LRESULT msgResult = 0;
928   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
929                                                     PropSheetInfoStr);
930
931   while ((i < psInfo->nPages) && (msgResult == 0))
932   {
933     hwndPage = psInfo->proppage[i].hwndPage;
934     msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
935     i++;
936   }
937
938   return msgResult;
939 }
940
941 /******************************************************************************
942  *            PROPSHEET_GetPSPPage
943  */
944 static LPCPROPSHEETPAGEA PROPSHEET_GetPSPPage(const PropSheetInfo * psInfo,
945                                               int index)
946 {
947   BOOL usePSP = psInfo->ppshheader->dwFlags & PSH_PROPSHEETPAGE;
948   LPCPROPSHEETPAGEA lppsp;
949   int realIndex = psInfo->proppage[index].index;
950
951   if (usePSP)
952   {
953     BYTE* pByte;
954
955     lppsp = psInfo->ppshheader->u3.ppsp;
956
957     pByte = (BYTE*) lppsp;
958
959     pByte += (lppsp->dwSize * realIndex);
960     lppsp = (LPCPROPSHEETPAGEA)pByte;
961   }
962   else
963     lppsp = (LPCPROPSHEETPAGEA) psInfo->ppshheader->u3.phpage[realIndex];
964
965   return lppsp;
966 }
967
968 /******************************************************************************
969  *            PROPSHEET_AddPage
970  */
971 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
972                               HPROPSHEETPAGE hpage)
973 {
974   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
975                                                      PropSheetInfoStr);
976   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
977   TCITEMA item;
978   char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
979   LPCPROPSHEETPAGEA ppsp = (LPCPROPSHEETPAGEA)hpage;
980
981   /*
982    * Allocate and fill in a new PropPageInfo entry.
983    */
984   psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage,
985                                                       sizeof(PropPageInfo) *
986                                                       (psInfo->nPages + 1));
987
988   PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages);
989   psInfo->proppage[psInfo->nPages].index = -1;
990   psInfo->proppage[psInfo->nPages].hpage = hpage;
991
992   /*
993    * Create the page but don't show it.
994    */
995   PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp, FALSE);
996
997   /*
998    * Add a new tab to the tab control.
999    */
1000   item.mask = TCIF_TEXT;
1001   item.pszText = tabtext;
1002   item.cchTextMax = MAX_TABTEXT_LENGTH;
1003
1004   WideCharToMultiByte(CP_ACP, 0,
1005                       (LPCWSTR)psInfo->proppage[psInfo->nPages].pszText,
1006                       -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
1007
1008   SendMessageA(hwndTabControl, TCM_INSERTITEMA, psInfo->nPages + 1,
1009                (LPARAM)&item);
1010
1011   psInfo->nPages++;
1012
1013   return FALSE;
1014 }
1015
1016 /******************************************************************************
1017  *            PROPSHEET_RemovePage
1018  */
1019 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
1020                                  int index,
1021                                  HPROPSHEETPAGE hpage)
1022 {
1023   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1024                                                      PropSheetInfoStr);
1025   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1026   PropPageInfo* oldPages = psInfo->proppage;
1027
1028   /*
1029    * hpage takes precedence over index.
1030    */
1031   if (hpage != 0)
1032   {
1033     index = PROPSHEET_GetPageIndex(hpage, psInfo);
1034
1035     if (index == -1)
1036     {
1037       TRACE("Could not find page to remove!\n");
1038       return FALSE;
1039     }
1040   }
1041
1042   TRACE("total pages %d removing page %d active page %d\n",
1043         psInfo->nPages, index, psInfo->active_page);
1044   /*
1045    * Check if we're removing the active page.
1046    */
1047   if (index == psInfo->active_page)
1048   {
1049     if (psInfo->nPages > 1)
1050     {
1051       if (index > 0)
1052       {
1053         /* activate previous page  */
1054         PROPSHEET_ShowPage(hwndDlg, index - 1, psInfo);
1055       }
1056       else
1057       {
1058         /* activate the next page */
1059         PROPSHEET_ShowPage(hwndDlg, index + 1, psInfo);
1060       }
1061     }
1062     else
1063     {
1064       TRACE("Removing the only page, close the dialog!\n");
1065
1066       if (psInfo->isModeless)
1067         psInfo->active_page = -1;
1068       else
1069         EndDialog(hwndDlg, FALSE);
1070
1071       return TRUE;
1072     }
1073   }
1074
1075   if (index < psInfo->active_page)
1076     psInfo->active_page--;
1077
1078   /* Remove the tab */
1079   SendMessageA(hwndTabControl, TCM_DELETEITEM, index, 0);
1080
1081   psInfo->nPages--;
1082   psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages);
1083
1084   if (index > 0)  
1085     memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
1086
1087   if (index < psInfo->nPages)
1088     memcpy(&psInfo->proppage[index], &oldPages[index + 1],
1089            (psInfo->nPages - index) * sizeof(PropPageInfo));
1090
1091   COMCTL32_Free(oldPages);
1092
1093   return FALSE;
1094 }
1095
1096 /******************************************************************************
1097  *            PROPSHEET_GetPageIndex
1098  *
1099  * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
1100  * the array of PropPageInfo.
1101  */
1102 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
1103 {
1104   BOOL found = FALSE;
1105   int index = 0;
1106
1107   while ((index < psInfo->nPages) && (found == FALSE))
1108   {
1109     if (psInfo->proppage[index].hpage == hpage)
1110       found = TRUE;
1111     else
1112       index++;
1113   }
1114
1115   if (found == FALSE)
1116     index = -1;
1117
1118   return index;
1119 }
1120
1121 /******************************************************************************
1122  *            PROPSHEET_CleanUp
1123  */
1124 static void PROPSHEET_CleanUp(HWND hwndDlg)
1125 {
1126   PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropA(hwndDlg,
1127                                                        PropSheetInfoStr);
1128   COMCTL32_Free(psInfo->proppage);
1129   COMCTL32_Free(psInfo->strPropertiesFor);
1130
1131   GlobalFree((HGLOBAL)psInfo);
1132 }
1133
1134 /******************************************************************************
1135  *            PropertySheetA   (COMCTL32.84)(COMCTL32.83)
1136  */
1137 INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
1138 {
1139   int bRet = 0;
1140   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
1141                                                        sizeof(PropSheetInfo));
1142   LPCPROPSHEETPAGEA lppsp;
1143   int i;
1144
1145   PROPSHEET_CollectSheetInfo(lppsh, psInfo);
1146
1147   psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
1148                                                     lppsh->nPages);
1149
1150   for (i = 0; i < lppsh->nPages; i++)
1151   {
1152     psInfo->proppage[i].index = i;
1153     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
1154       psInfo->proppage[i].hpage = psInfo->ppshheader->u3.phpage[i];
1155     lppsp = PROPSHEET_GetPSPPage(psInfo, i);
1156     PROPSHEET_CollectPageInfo(lppsp, psInfo, i);
1157   }
1158
1159   bRet = PROPSHEET_CreateDialog(psInfo);
1160
1161   return bRet;
1162 }
1163
1164 /******************************************************************************
1165  *            PropertySheet32W   (COMCTL32.85)
1166  */
1167 INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW propertySheetHeader)
1168 {
1169     FIXME("(%p): stub\n", propertySheetHeader);
1170
1171     return -1;
1172 }
1173
1174 /******************************************************************************
1175  *            CreatePropertySheetPageA   (COMCTL32.19)(COMCTL32.18)
1176  */
1177 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
1178                           LPCPROPSHEETPAGEA lpPropSheetPage)
1179 {
1180   PROPSHEETPAGEA* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEA));
1181
1182   *ppsp = *lpPropSheetPage;
1183
1184   return (HPROPSHEETPAGE)ppsp;
1185 }
1186
1187 /******************************************************************************
1188  *            CreatePropertySheetPageW   (COMCTL32.20)
1189  */
1190 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
1191 {
1192     FIXME("(%p): stub\n", lpPropSheetPage);
1193
1194     return 0;
1195 }
1196
1197 /******************************************************************************
1198  *            DestroyPropertySheetPage   (COMCTL32.24)
1199  */
1200 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
1201 {
1202   COMCTL32_Free(hPropPage);
1203
1204   return TRUE;
1205 }
1206
1207 /******************************************************************************
1208  *            PROPSHEET_DialogProc
1209  */
1210 BOOL WINAPI
1211 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1212 {
1213   switch (uMsg)
1214   {
1215     case WM_INITDIALOG:
1216     {
1217       PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
1218       char* strCaption = (char*)COMCTL32_Alloc(MAX_CAPTION_LENGTH);
1219       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
1220       LPCPROPSHEETPAGEA ppshpage;
1221
1222       psInfo->strPropertiesFor = strCaption;
1223
1224       GetWindowTextA(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
1225
1226       PROPSHEET_CreateTabControl(hwnd, psInfo);
1227
1228       if (PROPSHEET_IsTooSmall(hwnd, psInfo))
1229       {
1230         PROPSHEET_AdjustSize(hwnd, psInfo);
1231         PROPSHEET_AdjustButtons(hwnd, psInfo);
1232       }
1233
1234       ppshpage = PROPSHEET_GetPSPPage(psInfo, psInfo->active_page);      
1235       PROPSHEET_CreatePage(hwnd, psInfo->active_page, psInfo, ppshpage, TRUE);
1236       SendMessageA(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
1237
1238       SetPropA(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
1239
1240       PROPSHEET_SetTitleA(hwnd,
1241                           psInfo->ppshheader->dwFlags,
1242                           psInfo->ppshheader->pszCaption);
1243
1244       return TRUE;
1245     }
1246
1247     case WM_DESTROY:
1248       PROPSHEET_CleanUp(hwnd);
1249       return TRUE;
1250
1251     case WM_CLOSE:
1252       PROPSHEET_Cancel(hwnd);
1253       return TRUE;
1254
1255     case WM_COMMAND:
1256     {
1257       WORD wID = LOWORD(wParam);
1258
1259       switch (wID)
1260       {
1261         case IDOK:
1262         case IDC_APPLY_BUTTON:
1263         {
1264           HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
1265
1266           if (PROPSHEET_Apply(hwnd) == FALSE)
1267             break;
1268
1269           EnableWindow(hwndApplyBtn, FALSE);
1270
1271           if (wID == IDOK)
1272           {
1273             PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1274                                                             PropSheetInfoStr);
1275             int result = TRUE;
1276
1277             if (psInfo->restartWindows)
1278               result = ID_PSRESTARTWINDOWS;
1279
1280             /* reboot system takes precedence over restart windows */
1281             if (psInfo->rebootSystem)
1282               result = ID_PSREBOOTSYSTEM;
1283
1284             if (psInfo->isModeless)
1285               psInfo->active_page = -1;
1286             else
1287               EndDialog(hwnd, result);
1288           }
1289
1290           break;
1291         }
1292
1293         case IDCANCEL:
1294           PROPSHEET_Cancel(hwnd);
1295           break;
1296
1297         case IDHELP:
1298           PROPSHEET_Help(hwnd);
1299           break;
1300       }
1301
1302       return TRUE;
1303     }
1304
1305     case WM_NOTIFY:
1306     {
1307       NMHDR* pnmh = (LPNMHDR) lParam;
1308
1309       if (pnmh->code == TCN_SELCHANGE)
1310       {
1311         PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1312                                                           PropSheetInfoStr);
1313         int index = SendMessageA(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
1314         HWND hwndHelp  = GetDlgItem(hwnd, IDHELP);
1315
1316         PROPSHEET_ShowPage(hwnd, index, psInfo);
1317
1318         if (psInfo->proppage[index].hasHelp)
1319           EnableWindow(hwndHelp, TRUE);
1320         else
1321           EnableWindow(hwndHelp, FALSE);
1322       }
1323
1324       return 0;
1325     }
1326
1327     case PSM_GETCURRENTPAGEHWND:
1328     {
1329       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1330                                                         PropSheetInfoStr);
1331       HWND hwndPage = 0;
1332
1333       if (psInfo->active_page != -1)
1334         hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1335
1336       SetWindowLongA(hwnd, DWL_MSGRESULT, hwndPage);
1337
1338       return TRUE;
1339     }
1340
1341     case PSM_CHANGED:
1342       PROPSHEET_Changed(hwnd, (HWND)wParam);
1343       return TRUE;
1344
1345     case PSM_UNCHANGED:
1346       PROPSHEET_UnChanged(hwnd, (HWND)wParam);
1347       return TRUE;
1348
1349     case PSM_GETTABCONTROL:
1350     {
1351       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
1352
1353       SetWindowLongA(hwnd, DWL_MSGRESULT, hwndTabCtrl);
1354
1355       return TRUE;
1356     }
1357
1358     case PSM_SETCURSEL:
1359     {
1360       BOOL msgResult;
1361
1362       msgResult = PROPSHEET_SetCurSel(hwnd,
1363                                       (int)wParam,
1364                                       (HPROPSHEETPAGE)lParam);
1365
1366       SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1367
1368       return TRUE;
1369     }
1370
1371     case PSM_CANCELTOCLOSE:
1372     {
1373       HWND hwndOK = GetDlgItem(hwnd, IDOK);
1374       HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
1375
1376       EnableWindow(hwndCancel, FALSE);
1377       SetWindowTextA(hwndOK, "Close"); /* FIXME: hardcoded string */
1378
1379       return TRUE;
1380     }
1381
1382     case PSM_RESTARTWINDOWS:
1383     {
1384       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1385                                                         PropSheetInfoStr);
1386
1387       psInfo->restartWindows = TRUE;
1388       return TRUE;
1389     }
1390
1391     case PSM_REBOOTSYSTEM:
1392     {
1393       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, 
1394                                                         PropSheetInfoStr);
1395
1396       psInfo->rebootSystem = TRUE;
1397       return TRUE;
1398     }
1399
1400     case PSM_SETTITLEA:
1401       PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
1402       return TRUE;
1403
1404     case PSM_APPLY:
1405     {
1406       BOOL msgResult = PROPSHEET_Apply(hwnd);
1407
1408       SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1409
1410       return TRUE;
1411     }
1412
1413     case PSM_QUERYSIBLINGS:
1414     {
1415       LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
1416
1417       SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1418
1419       return TRUE;
1420     }
1421
1422     case PSM_ADDPAGE:
1423       PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
1424       return TRUE;
1425
1426     case PSM_REMOVEPAGE:
1427       PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
1428       return TRUE;
1429
1430     case PSM_ISDIALOGMESSAGE:
1431     {
1432       FIXME("Unimplemented msg PSM_ISDIALOGMESSAGE\n");
1433       return 0;
1434     }
1435
1436     case PSM_PRESSBUTTON:
1437       PROPSHEET_PressButton(hwnd, (int)wParam);
1438       return TRUE;
1439
1440     case PSM_SETTITLEW:
1441         FIXME("Unimplemented msg PSM_SETTITLE32W\n");
1442         return 0;
1443     case PSM_SETWIZBUTTONS:
1444         FIXME("Unimplemented msg PSM_SETWIZBUTTONS\n");
1445         return 0;
1446     case PSM_SETCURSELID:
1447         FIXME("Unimplemented msg PSM_SETCURSELID\n");
1448         return 0;
1449     case PSM_SETFINISHTEXTA:
1450         FIXME("Unimplemented msg PSM_SETFINISHTEXT32A\n");
1451         return 0;
1452     case PSM_SETFINISHTEXTW:
1453         FIXME("Unimplemented msg PSM_SETFINISHTEXT32W\n");
1454         return 0;
1455
1456     default:
1457       return FALSE;
1458   }
1459 }
1460