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