Converted to the new debugging interface (done with the help of the
[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 "propsheet.h"
19 #include "debug.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(propsheet, "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(propsheet, "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(propsheet, "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(propsheet, "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(propsheet, "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(propsheet, "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 = CreateDialogIndirectA(ppshpage->hInstance,
615                                    pTemplate,
616                                    hwndParent,
617                                    ppshpage->pfnDlgProc);
618
619   ppInfo[index].hwndPage = hwndPage;
620
621   rc.left = psInfo->x;
622   rc.top = psInfo->y;
623   rc.right = psInfo->width;
624   rc.bottom = psInfo->height;
625
626   MapDialogRect(hwndParent, &rc);
627
628   /*
629    * Ask the Tab control to fit this page in.
630    */
631   SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&rc);
632
633   SetWindowPos(hwndPage, HWND_TOP,
634                rc.left + padding.x,
635                rc.top + padding.y,
636                0, 0, SWP_NOSIZE);
637
638   if (showPage)
639     ShowWindow(hwndPage, SW_SHOW);
640   else
641     ShowWindow(hwndPage, SW_HIDE);
642
643   return TRUE;
644 }
645
646 /******************************************************************************
647  *            PROPSHEET_ShowPage
648  *
649  * Displays or creates the specified page.
650  */
651 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
652 {
653   if (index == psInfo->active_page)
654     return TRUE;
655
656   ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
657
658   if (psInfo->proppage[index].hwndPage != 0)
659     ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
660   else
661   {
662     LPCPROPSHEETPAGEA ppshpage = PROPSHEET_GetPSPPage(psInfo, index);
663     PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage, TRUE);
664   }
665
666   psInfo->active_page = index;
667
668   return TRUE;
669 }
670
671 /******************************************************************************
672  *            PROPSHEET_Apply
673  */
674 static BOOL PROPSHEET_Apply(HWND hwndDlg)
675 {
676   int i;
677   NMHDR hdr;
678   HWND hwndPage;
679   LRESULT msgResult;
680   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
681                                                     PropSheetInfoStr);
682
683   hdr.hwndFrom = hwndDlg;
684
685   /*
686    * Send PSN_KILLACTIVE to the current page.
687    */
688   hdr.code = PSN_KILLACTIVE;
689
690   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
691
692   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr) != FALSE)
693     return FALSE;
694
695   /*
696    * Send PSN_APPLY to all pages.
697    */
698   hdr.code = PSN_APPLY;
699
700   for (i = 0; i < psInfo->nPages; i++)
701   {
702     hwndPage = psInfo->proppage[i].hwndPage;
703     msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
704
705     if (msgResult == PSNRET_INVALID_NOCHANGEPAGE)
706       return FALSE;
707   }
708
709   return TRUE;
710 }
711
712 /******************************************************************************
713  *            PROPSHEET_Cancel
714  */
715 static void PROPSHEET_Cancel(HWND hwndDlg)
716 {
717   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
718                                                     PropSheetInfoStr);
719   HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
720   NMHDR hdr;
721
722   hdr.hwndFrom = hwndDlg;
723   hdr.code = PSN_QUERYCANCEL;
724
725   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr))
726     return;
727
728   hdr.code = PSN_RESET;
729
730   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
731
732   if (psInfo->isModeless)
733     psInfo->active_page = -1; /* makes PSM_GETCURRENTPAGEHWND return NULL */
734   else
735     EndDialog(hwndDlg, FALSE);
736 }
737
738 /******************************************************************************
739  *            PROPSHEET_Help
740  */
741 static void PROPSHEET_Help(HWND hwndDlg)
742 {
743   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
744                                                     PropSheetInfoStr);
745   HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
746   NMHDR hdr;
747
748   hdr.hwndFrom = hwndDlg;
749   hdr.code = PSN_HELP;
750
751   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
752 }
753
754 /******************************************************************************
755  *            PROPSHEET_Changed
756  */
757 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
758 {
759   int i;
760   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
761                                                     PropSheetInfoStr);
762
763   /*
764    * Set the dirty flag of this page.
765    */
766   for (i = 0; i < psInfo->nPages; i++)
767   {
768     if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
769       psInfo->proppage[i].isDirty = TRUE;
770   }
771
772   /*
773    * Enable the Apply button.
774    */
775   if (psInfo->hasApply)
776   {
777     HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
778
779     EnableWindow(hwndApplyBtn, TRUE);
780   }
781 }
782
783 /******************************************************************************
784  *            PROPSHEET_UnChanged
785  */
786 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
787 {
788   int i;
789   BOOL noPageDirty = TRUE;
790   HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
791   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
792                                                     PropSheetInfoStr);
793
794   for (i = 0; i < psInfo->nPages; i++)
795   {
796     /* set the specified page as clean */
797     if (psInfo->proppage[i].hwndPage == hwndCleanPage)
798       psInfo->proppage[i].isDirty = FALSE;
799
800     /* look to see if there's any dirty pages */
801     if (psInfo->proppage[i].isDirty)
802       noPageDirty = FALSE;
803   }
804
805   /*
806    * Disable Apply button.
807    */
808   if (noPageDirty)
809     EnableWindow(hwndApplyBtn, FALSE);
810 }
811
812 /******************************************************************************
813  *            PROPSHEET_PressButton
814  */
815 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
816 {
817   switch (buttonID)
818   {
819     case PSBTN_APPLYNOW:
820       SendMessageA(hwndDlg, WM_COMMAND, IDC_APPLY_BUTTON, 0);
821       break;
822     case PSBTN_BACK:
823       FIXME(propsheet, "Wizard mode not implemented.\n");
824       break;
825     case PSBTN_CANCEL:
826       SendMessageA(hwndDlg, WM_COMMAND, IDCANCEL, 0);
827       break;
828     case PSBTN_FINISH:
829       FIXME(propsheet, "Wizard mode not implemented.\n");
830       break;
831     case PSBTN_HELP:
832       SendMessageA(hwndDlg, WM_COMMAND, IDHELP, 0);
833       break;
834     case PSBTN_NEXT:
835       FIXME(propsheet, "Wizard mode not implemented.\n");
836       break;
837     case PSBTN_OK:
838       SendMessageA(hwndDlg, WM_COMMAND, IDOK, 0);
839       break;
840     default:
841       FIXME(propsheet, "Invalid button index %d\n", buttonID);
842   }
843 }
844
845 /******************************************************************************
846  *            PROPSHEET_SetCurSel
847  */
848 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
849                                 int index,
850                                 HPROPSHEETPAGE hpage)
851 {
852   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
853                                                     PropSheetInfoStr);
854   HWND hwndPage;
855   HWND hwndHelp  = GetDlgItem(hwndDlg, IDHELP);
856   NMHDR hdr;
857
858   /*
859    * Notify the current page.
860    */
861   hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
862
863   hdr.hwndFrom = hwndDlg;
864   hdr.code = PSN_KILLACTIVE;
865
866   if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr))
867     return FALSE;
868
869   if (hpage != NULL)
870     FIXME(propsheet, "Implement HPROPSHEETPAGE!\n");
871   else
872     hwndPage = psInfo->proppage[index].hwndPage;
873
874   /*
875    * Notify the new page.
876    */
877   hdr.code = PSN_SETACTIVE;
878
879   SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr);
880
881   /*
882    * Display the new page.
883    */
884   PROPSHEET_ShowPage(hwndDlg, index, psInfo);
885
886   if (psInfo->proppage[index].hasHelp)
887     EnableWindow(hwndHelp, TRUE);
888   else
889     EnableWindow(hwndHelp, FALSE);
890
891   return TRUE;
892 }
893
894 /******************************************************************************
895  *            PROPSHEET_SetTitleA
896  */
897 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
898 {
899   if (dwStyle & PSH_PROPTITLE)
900   {
901     PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
902                                                       PropSheetInfoStr);
903     char* dest;
904     int lentitle = strlen(lpszText);
905     int lenprop  = strlen(psInfo->strPropertiesFor);
906
907     dest = COMCTL32_Alloc(lentitle + lenprop + 1);
908     strcpy(dest, psInfo->strPropertiesFor);
909     strcat(dest, lpszText);
910
911     SetWindowTextA(hwndDlg, dest);
912     COMCTL32_Free(dest);
913   }
914   else
915     SetWindowTextA(hwndDlg, lpszText);
916 }
917
918 /******************************************************************************
919  *            PROPSHEET_QuerySiblings
920  */
921 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
922                                        WPARAM wParam, LPARAM lParam)
923 {
924   int i = 0;
925   HWND hwndPage;
926   LRESULT msgResult = 0;
927   PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
928                                                     PropSheetInfoStr);
929
930   while ((i < psInfo->nPages) && (msgResult == 0))
931   {
932     hwndPage = psInfo->proppage[i].hwndPage;
933     msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
934     i++;
935   }
936
937   return msgResult;
938 }
939
940 /******************************************************************************
941  *            PROPSHEET_GetPSPPage
942  */
943 static LPCPROPSHEETPAGEA PROPSHEET_GetPSPPage(const PropSheetInfo * psInfo,
944                                               int index)
945 {
946   BOOL usePSP = psInfo->ppshheader->dwFlags & PSH_PROPSHEETPAGE;
947   LPCPROPSHEETPAGEA lppsp;
948   int realIndex = psInfo->proppage[index].index;
949
950   if (usePSP)
951   {
952     BYTE* pByte;
953
954     lppsp = psInfo->ppshheader->u3.ppsp;
955
956     pByte = (BYTE*) lppsp;
957
958     pByte += (lppsp->dwSize * realIndex);
959     lppsp = (LPCPROPSHEETPAGEA)pByte;
960   }
961   else
962     lppsp = (LPCPROPSHEETPAGEA) psInfo->ppshheader->u3.phpage[realIndex];
963
964   return lppsp;
965 }
966
967 /******************************************************************************
968  *            PROPSHEET_AddPage
969  */
970 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
971                               HPROPSHEETPAGE hpage)
972 {
973   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
974                                                      PropSheetInfoStr);
975   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
976   TCITEMA item;
977   char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
978   LPCPROPSHEETPAGEA ppsp = (LPCPROPSHEETPAGEA)hpage;
979
980   /*
981    * Allocate and fill in a new PropPageInfo entry.
982    */
983   psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage,
984                                                       sizeof(PropPageInfo) *
985                                                       (psInfo->nPages + 1));
986
987   PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages);
988   psInfo->proppage[psInfo->nPages].index = -1;
989   psInfo->proppage[psInfo->nPages].hpage = hpage;
990
991   /*
992    * Create the page but don't show it.
993    */
994   PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp, FALSE);
995
996   /*
997    * Add a new tab to the tab control.
998    */
999   item.mask = TCIF_TEXT;
1000   item.pszText = tabtext;
1001   item.cchTextMax = MAX_TABTEXT_LENGTH;
1002
1003   WideCharToMultiByte(CP_ACP, 0,
1004                       (LPCWSTR)psInfo->proppage[psInfo->nPages].pszText,
1005                       -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
1006
1007   SendMessageA(hwndTabControl, TCM_INSERTITEMA, psInfo->nPages + 1,
1008                (LPARAM)&item);
1009
1010   psInfo->nPages++;
1011
1012   return FALSE;
1013 }
1014
1015 /******************************************************************************
1016  *            PROPSHEET_RemovePage
1017  */
1018 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
1019                                  int index,
1020                                  HPROPSHEETPAGE hpage)
1021 {
1022   PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1023                                                      PropSheetInfoStr);
1024   HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1025   PropPageInfo* oldPages = psInfo->proppage;
1026
1027   /*
1028    * hpage takes precedence over index.
1029    */
1030   if (hpage != 0)
1031   {
1032     index = PROPSHEET_GetPageIndex(hpage, psInfo);
1033
1034     if (index == -1)
1035     {
1036       TRACE(propsheet, "Could not find page to remove!\n");
1037       return FALSE;
1038     }
1039   }
1040
1041   TRACE(propsheet, "total pages %d removing page %d active page %d\n",
1042         psInfo->nPages, index, psInfo->active_page);
1043   /*
1044    * Check if we're removing the active page.
1045    */
1046   if (index == psInfo->active_page)
1047   {
1048     if (psInfo->nPages > 1)
1049     {
1050       if (index > 0)
1051       {
1052         /* activate previous page  */
1053         PROPSHEET_ShowPage(hwndDlg, index - 1, psInfo);
1054       }
1055       else
1056       {
1057         /* activate the next page */
1058         PROPSHEET_ShowPage(hwndDlg, index + 1, psInfo);
1059       }
1060     }
1061     else
1062     {
1063       TRACE(propsheet, "Removing the only page, close the dialog!\n");
1064
1065       if (psInfo->isModeless)
1066         psInfo->active_page = -1;
1067       else
1068         EndDialog(hwndDlg, FALSE);
1069
1070       return TRUE;
1071     }
1072   }
1073
1074   if (index < psInfo->active_page)
1075     psInfo->active_page--;
1076
1077   /* Remove the tab */
1078   SendMessageA(hwndTabControl, TCM_DELETEITEM, index, 0);
1079
1080   psInfo->nPages--;
1081   psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages);
1082
1083   if (index > 0)  
1084     memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
1085
1086   if (index < psInfo->nPages)
1087     memcpy(&psInfo->proppage[index], &oldPages[index + 1],
1088            (psInfo->nPages - index) * sizeof(PropPageInfo));
1089
1090   COMCTL32_Free(oldPages);
1091
1092   return FALSE;
1093 }
1094
1095 /******************************************************************************
1096  *            PROPSHEET_GetPageIndex
1097  *
1098  * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
1099  * the array of PropPageInfo.
1100  */
1101 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
1102 {
1103   BOOL found = FALSE;
1104   int index = 0;
1105
1106   while ((index < psInfo->nPages) && (found == FALSE))
1107   {
1108     if (psInfo->proppage[index].hpage == hpage)
1109       found = TRUE;
1110     else
1111       index++;
1112   }
1113
1114   if (found == FALSE)
1115     index = -1;
1116
1117   return index;
1118 }
1119
1120 /******************************************************************************
1121  *            PROPSHEET_CleanUp
1122  */
1123 static void PROPSHEET_CleanUp(HWND hwndDlg)
1124 {
1125   PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropA(hwndDlg,
1126                                                        PropSheetInfoStr);
1127   COMCTL32_Free(psInfo->proppage);
1128   COMCTL32_Free(psInfo->strPropertiesFor);
1129
1130   GlobalFree((HGLOBAL)psInfo);
1131 }
1132
1133 /******************************************************************************
1134  *            PropertySheetA   (COMCTL32.84)(COMCTL32.83)
1135  */
1136 INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
1137 {
1138   int bRet = 0;
1139   PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
1140                                                        sizeof(PropSheetInfo));
1141   LPCPROPSHEETPAGEA lppsp;
1142   int i;
1143
1144   PROPSHEET_CollectSheetInfo(lppsh, psInfo);
1145
1146   psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
1147                                                     lppsh->nPages);
1148
1149   for (i = 0; i < lppsh->nPages; i++)
1150   {
1151     psInfo->proppage[i].index = i;
1152     if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
1153       psInfo->proppage[i].hpage = psInfo->ppshheader->u3.phpage[i];
1154     lppsp = PROPSHEET_GetPSPPage(psInfo, i);
1155     PROPSHEET_CollectPageInfo(lppsp, psInfo, i);
1156   }
1157
1158   bRet = PROPSHEET_CreateDialog(psInfo);
1159
1160   return bRet;
1161 }
1162
1163 /******************************************************************************
1164  *            PropertySheet32W   (COMCTL32.85)
1165  */
1166 INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW propertySheetHeader)
1167 {
1168     FIXME(propsheet, "(%p): stub\n", propertySheetHeader);
1169
1170     return -1;
1171 }
1172
1173 /******************************************************************************
1174  *            CreatePropertySheetPageA   (COMCTL32.19)(COMCTL32.18)
1175  */
1176 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
1177                           LPCPROPSHEETPAGEA lpPropSheetPage)
1178 {
1179   PROPSHEETPAGEA* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEA));
1180
1181   *ppsp = *lpPropSheetPage;
1182
1183   return (HPROPSHEETPAGE)ppsp;
1184 }
1185
1186 /******************************************************************************
1187  *            CreatePropertySheetPageW   (COMCTL32.20)
1188  */
1189 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
1190 {
1191     FIXME(propsheet, "(%p): stub\n", lpPropSheetPage);
1192
1193     return 0;
1194 }
1195
1196 /******************************************************************************
1197  *            DestroyPropertySheetPage   (COMCTL32.24)
1198  */
1199 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
1200 {
1201   COMCTL32_Free(hPropPage);
1202
1203   return TRUE;
1204 }
1205
1206 /******************************************************************************
1207  *            PROPSHEET_DialogProc
1208  */
1209 BOOL WINAPI
1210 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1211 {
1212   switch (uMsg)
1213   {
1214     case WM_INITDIALOG:
1215     {
1216       PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
1217       char* strCaption = (char*)COMCTL32_Alloc(MAX_CAPTION_LENGTH);
1218       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
1219       LPCPROPSHEETPAGEA ppshpage;
1220
1221       psInfo->strPropertiesFor = strCaption;
1222
1223       GetWindowTextA(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
1224
1225       PROPSHEET_CreateTabControl(hwnd, psInfo);
1226
1227       if (PROPSHEET_IsTooSmall(hwnd, psInfo))
1228       {
1229         PROPSHEET_AdjustSize(hwnd, psInfo);
1230         PROPSHEET_AdjustButtons(hwnd, psInfo);
1231       }
1232
1233       ppshpage = PROPSHEET_GetPSPPage(psInfo, psInfo->active_page);      
1234       PROPSHEET_CreatePage(hwnd, psInfo->active_page, psInfo, ppshpage, TRUE);
1235       SendMessageA(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
1236
1237       SetPropA(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
1238
1239       PROPSHEET_SetTitleA(hwnd,
1240                           psInfo->ppshheader->dwFlags,
1241                           psInfo->ppshheader->pszCaption);
1242
1243       return TRUE;
1244     }
1245
1246     case WM_DESTROY:
1247       PROPSHEET_CleanUp(hwnd);
1248       return TRUE;
1249
1250     case WM_CLOSE:
1251       PROPSHEET_Cancel(hwnd);
1252       return TRUE;
1253
1254     case WM_COMMAND:
1255     {
1256       WORD wID = LOWORD(wParam);
1257
1258       switch (wID)
1259       {
1260         case IDOK:
1261         case IDC_APPLY_BUTTON:
1262         {
1263           HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
1264
1265           if (PROPSHEET_Apply(hwnd) == FALSE)
1266             break;
1267
1268           EnableWindow(hwndApplyBtn, FALSE);
1269
1270           if (wID == IDOK)
1271           {
1272             PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1273                                                             PropSheetInfoStr);
1274             int result = TRUE;
1275
1276             if (psInfo->restartWindows)
1277               result = ID_PSRESTARTWINDOWS;
1278
1279             /* reboot system takes precedence over restart windows */
1280             if (psInfo->rebootSystem)
1281               result = ID_PSREBOOTSYSTEM;
1282
1283             if (psInfo->isModeless)
1284               psInfo->active_page = -1;
1285             else
1286               EndDialog(hwnd, result);
1287           }
1288
1289           break;
1290         }
1291
1292         case IDCANCEL:
1293           PROPSHEET_Cancel(hwnd);
1294           break;
1295
1296         case IDHELP:
1297           PROPSHEET_Help(hwnd);
1298           break;
1299       }
1300
1301       return TRUE;
1302     }
1303
1304     case WM_NOTIFY:
1305     {
1306       NMHDR* pnmh = (LPNMHDR) lParam;
1307
1308       if (pnmh->code == TCN_SELCHANGE)
1309       {
1310         PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1311                                                           PropSheetInfoStr);
1312         int index = SendMessageA(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
1313         HWND hwndHelp  = GetDlgItem(hwnd, IDHELP);
1314
1315         PROPSHEET_ShowPage(hwnd, index, psInfo);
1316
1317         if (psInfo->proppage[index].hasHelp)
1318           EnableWindow(hwndHelp, TRUE);
1319         else
1320           EnableWindow(hwndHelp, FALSE);
1321       }
1322
1323       return 0;
1324     }
1325
1326     case PSM_GETCURRENTPAGEHWND:
1327     {
1328       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1329                                                         PropSheetInfoStr);
1330       HWND hwndPage = 0;
1331
1332       if (psInfo->active_page != -1)
1333         hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1334
1335       SetWindowLongA(hwnd, DWL_MSGRESULT, hwndPage);
1336
1337       return TRUE;
1338     }
1339
1340     case PSM_CHANGED:
1341       PROPSHEET_Changed(hwnd, (HWND)wParam);
1342       return TRUE;
1343
1344     case PSM_UNCHANGED:
1345       PROPSHEET_UnChanged(hwnd, (HWND)wParam);
1346       return TRUE;
1347
1348     case PSM_GETTABCONTROL:
1349     {
1350       HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
1351
1352       SetWindowLongA(hwnd, DWL_MSGRESULT, hwndTabCtrl);
1353
1354       return TRUE;
1355     }
1356
1357     case PSM_SETCURSEL:
1358     {
1359       BOOL msgResult;
1360
1361       msgResult = PROPSHEET_SetCurSel(hwnd,
1362                                       (int)wParam,
1363                                       (HPROPSHEETPAGE)lParam);
1364
1365       SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1366
1367       return TRUE;
1368     }
1369
1370     case PSM_CANCELTOCLOSE:
1371     {
1372       HWND hwndOK = GetDlgItem(hwnd, IDOK);
1373       HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
1374
1375       EnableWindow(hwndCancel, FALSE);
1376       SetWindowTextA(hwndOK, "Close"); /* FIXME: hardcoded string */
1377
1378       return TRUE;
1379     }
1380
1381     case PSM_RESTARTWINDOWS:
1382     {
1383       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
1384                                                         PropSheetInfoStr);
1385
1386       psInfo->restartWindows = TRUE;
1387       return TRUE;
1388     }
1389
1390     case PSM_REBOOTSYSTEM:
1391     {
1392       PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, 
1393                                                         PropSheetInfoStr);
1394
1395       psInfo->rebootSystem = TRUE;
1396       return TRUE;
1397     }
1398
1399     case PSM_SETTITLEA:
1400       PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
1401       return TRUE;
1402
1403     case PSM_APPLY:
1404     {
1405       BOOL msgResult = PROPSHEET_Apply(hwnd);
1406
1407       SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1408
1409       return TRUE;
1410     }
1411
1412     case PSM_QUERYSIBLINGS:
1413     {
1414       LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
1415
1416       SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
1417
1418       return TRUE;
1419     }
1420
1421     case PSM_ADDPAGE:
1422       PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
1423       return TRUE;
1424
1425     case PSM_REMOVEPAGE:
1426       PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
1427       return TRUE;
1428
1429     case PSM_ISDIALOGMESSAGE:
1430     {
1431       FIXME (propsheet, "Unimplemented msg PSM_ISDIALOGMESSAGE\n");
1432       return 0;
1433     }
1434
1435     case PSM_PRESSBUTTON:
1436       PROPSHEET_PressButton(hwnd, (int)wParam);
1437       return TRUE;
1438
1439     case PSM_SETTITLEW:
1440         FIXME (propsheet, "Unimplemented msg PSM_SETTITLE32W\n");
1441         return 0;
1442     case PSM_SETWIZBUTTONS:
1443         FIXME (propsheet, "Unimplemented msg PSM_SETWIZBUTTONS\n");
1444         return 0;
1445     case PSM_SETCURSELID:
1446         FIXME (propsheet, "Unimplemented msg PSM_SETCURSELID\n");
1447         return 0;
1448     case PSM_SETFINISHTEXTA:
1449         FIXME (propsheet, "Unimplemented msg PSM_SETFINISHTEXT32A\n");
1450         return 0;
1451     case PSM_SETFINISHTEXTW:
1452         FIXME (propsheet, "Unimplemented msg PSM_SETFINISHTEXT32W\n");
1453         return 0;
1454
1455     default:
1456       return FALSE;
1457   }
1458 }
1459