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