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