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