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