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