4 * Copyright 1998 Francis Beaudet
5 * Copyright 1999 Thuy Nguyen
9 * - Unicode property sheets
20 #include "debugtools.h"
24 /******************************************************************************
36 typedef struct tagPropPageInfo
38 HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */
47 typedef struct tagPropSheetInfo
50 PROPSHEETHEADERA ppshheader;
51 LPSTR strPropertiesFor;
61 PropPageInfo* proppage;
66 HIMAGELIST hImageList;
75 /******************************************************************************
76 * Defines and global variables
79 const char * PropSheetInfoStr = "PropertySheetInfo";
81 #define MAX_CAPTION_LENGTH 255
82 #define MAX_TABTEXT_LENGTH 255
83 #define MAX_BUTTONTEXT_LENGTH 64
85 /******************************************************************************
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,
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,
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);
124 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
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);
135 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
137 DEFAULT_DEBUG_CHANNEL(propsheet)
139 /******************************************************************************
140 * PROPSHEET_CollectSheetInfo
142 * Collect relevant data.
144 static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh,
145 PropSheetInfo * psInfo)
147 DWORD dwSize = min(lppsh->dwSize,sizeof(PROPSHEETHEADERA));
148 DWORD dwFlags = lppsh->dwFlags;
150 psInfo->hasHelp = dwFlags & PSH_HASHELP;
151 psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW);
152 psInfo->useCallback = dwFlags & PSH_USECALLBACK;
153 psInfo->isModeless = dwFlags & PSH_MODELESS;
155 memcpy(&psInfo->ppshheader,lppsh,dwSize);
157 if (HIWORD(lppsh->pszCaption))
158 psInfo->ppshheader.pszCaption = HEAP_strdupA( GetProcessHeap(),
159 0, lppsh->pszCaption );
161 psInfo->nPages = lppsh->nPages;
163 if (dwFlags & PSH_USEPSTARTPAGE)
165 TRACE("PSH_USEPSTARTPAGE is on");
166 psInfo->active_page = 0;
169 psInfo->active_page = lppsh->u2.nStartPage;
171 if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages)
172 psInfo->active_page = 0;
174 psInfo->restartWindows = FALSE;
175 psInfo->rebootSystem = FALSE;
176 psInfo->hImageList = 0;
177 psInfo->activeValid = FALSE;
182 /******************************************************************************
183 * PROPSHEET_CollectPageInfo
185 * Collect property sheet data.
186 * With code taken from DIALOG_ParseTemplate32.
188 BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp,
189 PropSheetInfo * psInfo,
192 DLGTEMPLATE* pTemplate;
197 psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp;
198 psInfo->proppage[index].hwndPage = 0;
199 psInfo->proppage[index].isDirty = FALSE;
202 * Process property page flags.
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);
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;
214 * Process page template.
216 if (dwFlags & PSP_DLGINDIRECT)
217 pTemplate = (DLGTEMPLATE*)lppsp->u.pResource;
220 HRSRC hResource = FindResourceA(lppsp->hInstance,
221 lppsp->u.pszTemplate,
223 HGLOBAL hTemplate = LoadResource(lppsp->hInstance,
225 pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
229 * Extract the size of the page and the caption.
234 p = (const WORD *)pTemplate;
236 if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
238 /* DIALOGEX template */
242 p += 2; /* help ID */
243 p += 2; /* ext style */
248 /* DIALOG template */
251 p += 2; /* ext style */
257 width = (WORD)*p; p++;
258 height = (WORD)*p; p++;
260 /* remember the largest width and height */
261 if (width > psInfo->width)
262 psInfo->width = width;
264 if (height > psInfo->height)
265 psInfo->height = height;
277 p += lstrlenW( (LPCWSTR)p ) + 1;
291 p += lstrlenW( (LPCWSTR)p ) + 1;
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;
300 if (dwFlags & PSP_USETITLE)
302 if ( !HIWORD( lppsp->pszTitle ) )
306 if ( !LoadStringA( lppsp->hInstance, (UINT) lppsp->pszTitle, szTitle, 256 ) )
309 psInfo->proppage[index].pszText = HEAP_strdupAtoW( GetProcessHeap(),
313 psInfo->proppage[index].pszText = HEAP_strdupAtoW(GetProcessHeap(),
319 * Build the image list for icons
321 if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID))
324 int icon_cx = GetSystemMetrics(SM_CXSMICON);
325 int icon_cy = GetSystemMetrics(SM_CYSMICON);
327 if (dwFlags & PSP_USEICONID)
328 hIcon = LoadImageA(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON,
329 icon_cx, icon_cy, LR_DEFAULTCOLOR);
331 hIcon = lppsp->u2.hIcon;
335 if (psInfo->hImageList == 0 )
336 psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1);
338 ImageList_AddIcon(psInfo->hImageList, hIcon);
346 /******************************************************************************
347 * PROPSHEET_DoDialogBox
349 * Copied from windows/dialog.c:DIALOG_DoDialogBox
351 static INT PROPSHEET_DoDialogBox( HWND hwnd, HWND owner)
354 DIALOGINFO * dlgInfo;
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;
363 if (!(dlgInfo->flags & DF_END)) /* was EndDialog called in WM_INITDIALOG ? */
365 EnableWindow( owner, FALSE );
366 ShowWindow( hwnd, SW_SHOW );
367 while (GetMessageA(&msg, 0, 0, 0))
369 if (!PROPSHEET_IsDialogMessage( hwnd, &msg))
371 TranslateMessage( &msg );
372 DispatchMessageA( &msg );
374 if (dlgInfo->flags & DF_END) break;
376 EnableWindow( owner, TRUE );
378 retval = dlgInfo->idResult;
379 WIN_ReleaseWndPtr(wndPtr);
380 DestroyWindow( hwnd );
385 /******************************************************************************
386 * PROPSHEET_CreateDialog
388 * Creates the actual property sheet.
390 BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo)
397 WORD resID = IDD_PROPSHEET;
399 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
402 if(!(hRes = FindResourceA(COMCTL32_hModule,
403 MAKEINTRESOURCEA(resID),
407 if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes)))
411 * Make a copy of the dialog template.
413 resSize = SizeofResource(COMCTL32_hModule, hRes);
415 temp = COMCTL32_Alloc(resSize);
420 memcpy(temp, template, resSize);
422 if (psInfo->useCallback)
423 (*(psInfo->ppshheader.pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp);
425 ret = CreateDialogIndirectParamA(psInfo->ppshheader.hInstance,
426 (LPDLGTEMPLATEA) temp,
427 psInfo->ppshheader.hwndParent,
428 (DLGPROC) PROPSHEET_DialogProc,
431 if (!(psInfo->ppshheader.dwFlags & PSH_MODELESS))
432 ret = PROPSHEET_DoDialogBox((HWND)ret, psInfo->ppshheader.hwndParent);
439 /******************************************************************************
440 * PROPSHEET_SizeMismatch
442 * Verify that the tab control and the "largest" property sheet page dlg. template
445 static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo)
447 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
448 RECT rcOrigTab, rcPage;
453 GetClientRect(hwndTabCtrl, &rcOrigTab);
454 TRACE("orig tab %d %d %d %d\n", rcOrigTab.left, rcOrigTab.top,
455 rcOrigTab.right, rcOrigTab.bottom);
460 rcPage.left = psInfo->x;
461 rcPage.top = psInfo->y;
462 rcPage.right = psInfo->width;
463 rcPage.bottom = psInfo->height;
465 MapDialogRect(hwndDlg, &rcPage);
466 TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
467 rcPage.right, rcPage.bottom);
469 if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) )
471 if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) )
477 /******************************************************************************
478 * PROPSHEET_IsTooSmallWizard
480 * Verify that the default property sheet is big enough.
482 static BOOL PROPSHEET_IsTooSmallWizard(HWND hwndDlg, PropSheetInfo* psInfo)
484 RECT rcSheetRect, rcPage, rcLine, rcSheetClient;
485 HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
486 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
488 GetClientRect(hwndDlg, &rcSheetClient);
489 GetWindowRect(hwndDlg, &rcSheetRect);
490 GetWindowRect(hwndLine, &rcLine);
492 /* Remove the space below the sunken line */
493 rcSheetClient.bottom -= (rcSheetRect.bottom - rcLine.top);
495 /* Remove the buffer zone all around the edge */
496 rcSheetClient.bottom -= (padding.y * 2);
497 rcSheetClient.right -= (padding.x * 2);
502 rcPage.left = psInfo->x;
503 rcPage.top = psInfo->y;
504 rcPage.right = psInfo->width;
505 rcPage.bottom = psInfo->height;
507 MapDialogRect(hwndDlg, &rcPage);
508 TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top,
509 rcPage.right, rcPage.bottom);
511 if (rcPage.right > rcSheetClient.right)
514 if (rcPage.bottom > rcSheetClient.bottom)
520 /******************************************************************************
521 * PROPSHEET_AdjustSize
523 * Resizes the property sheet and the tab control to fit the largest page.
525 static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo)
527 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
528 HWND hwndButton = GetDlgItem(hwndDlg, IDOK);
530 int tabOffsetX, tabOffsetY, buttonHeight;
531 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg);
532 WND * wndPtr = WIN_FindWndPtr( hwndDlg );
533 DIALOGINFO * dlgInfo = (DIALOGINFO *)wndPtr->wExtra;
535 /* Get the height of buttons */
536 GetClientRect(hwndButton, &rc);
537 buttonHeight = rc.bottom;
544 rc.right = psInfo->width;
545 rc.bottom = psInfo->height;
547 MapDialogRect(hwndDlg, &rc);
550 * Resize the tab control.
552 GetClientRect(hwndTabCtrl,&tabRect);
554 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);
556 if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
558 rc.bottom = rc.top + tabRect.bottom - tabRect.top;
559 psInfo->height = MulDiv((rc.bottom - rc.top),8,dlgInfo->yBaseUnit);
562 if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
564 rc.right = rc.left + tabRect.right - tabRect.left;
565 psInfo->width = MulDiv((rc.right - rc.left),4,dlgInfo->xBaseUnit);
568 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc);
570 tabOffsetX = -(rc.left);
571 tabOffsetY = -(rc.top);
575 SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom,
576 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
578 GetClientRect(hwndTabCtrl, &rc);
580 TRACE("tab client rc %d %d %d %d\n",
581 rc.left, rc.top, rc.right, rc.bottom);
583 rc.right += ((padding.x * 2) + tabOffsetX);
584 rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY);
587 * Resize the property sheet.
589 SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
590 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
592 WIN_ReleaseWndPtr(wndPtr);
596 /******************************************************************************
597 * PROPSHEET_AdjustSizeWizard
599 * Resizes the property sheet to fit the largest page.
601 static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, PropSheetInfo* psInfo)
603 HWND hwndButton = GetDlgItem(hwndDlg, IDCANCEL);
604 HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
605 HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
607 int buttonHeight, lineHeight;
608 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg, psInfo);
609 WND * wndPtr = WIN_FindWndPtr( hwndDlg );
610 DIALOGINFO * dlgInfo = (DIALOGINFO *)wndPtr->wExtra;
612 /* Get the height of buttons */
613 GetClientRect(hwndButton, &rc);
614 buttonHeight = rc.bottom;
616 GetClientRect(hwndLine, &rc);
617 lineHeight = rc.bottom;
624 rc.right = psInfo->width;
625 rc.bottom = psInfo->height;
627 MapDialogRect(hwndDlg, &rc);
629 GetClientRect(hwndTabCtrl,&tabRect);
631 if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top))
633 rc.bottom = rc.top + tabRect.bottom - tabRect.top;
634 psInfo->height = MulDiv((rc.bottom - rc.top), 8, dlgInfo->yBaseUnit);
637 if ((rc.right - rc.left) < (tabRect.right - tabRect.left))
639 rc.right = rc.left + tabRect.right - tabRect.left;
640 psInfo->width = MulDiv((rc.right - rc.left), 4, dlgInfo->xBaseUnit);
643 TRACE("Biggest page %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom);
646 rc.right += (padding.x * 2);
647 rc.bottom += (buttonHeight + (5 * padding.y) + lineHeight);
650 * Resize the property sheet.
652 SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom,
653 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
655 WIN_ReleaseWndPtr(wndPtr);
659 /******************************************************************************
660 * PROPSHEET_AdjustButtons
662 * Adjusts the buttons' positions.
664 static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo)
666 HWND hwndButton = GetDlgItem(hwndParent, IDOK);
670 int buttonWidth, buttonHeight;
671 PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent);
673 if (psInfo->hasApply)
680 * Obtain the size of the buttons.
682 GetClientRect(hwndButton, &rcSheet);
683 buttonWidth = rcSheet.right;
684 buttonHeight = rcSheet.bottom;
687 * Get the size of the property sheet.
689 GetClientRect(hwndParent, &rcSheet);
692 * All buttons will be at this y coordinate.
694 y = rcSheet.bottom - (padding.y + buttonHeight);
697 * Position OK button.
699 hwndButton = GetDlgItem(hwndParent, IDOK);
701 x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons);
703 SetWindowPos(hwndButton, 0, x, y, 0, 0,
704 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
707 * Position Cancel button.
709 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
711 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
713 SetWindowPos(hwndButton, 0, x, y, 0, 0,
714 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
717 * Position Apply button.
719 hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON);
721 if (psInfo->hasApply)
724 x = rcSheet.right - ((padding.x + buttonWidth) * 2);
726 x = rcSheet.right - (padding.x + buttonWidth);
728 SetWindowPos(hwndButton, 0, x, y, 0, 0,
729 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
731 EnableWindow(hwndButton, FALSE);
734 ShowWindow(hwndButton, SW_HIDE);
737 * Position Help button.
739 hwndButton = GetDlgItem(hwndParent, IDHELP);
743 x = rcSheet.right - (padding.x + buttonWidth);
745 SetWindowPos(hwndButton, 0, x, y, 0, 0,
746 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
749 ShowWindow(hwndButton, SW_HIDE);
754 /******************************************************************************
755 * PROPSHEET_AdjustButtonsWizard
757 * Adjusts the buttons' positions.
759 static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent,
760 PropSheetInfo* psInfo)
762 HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL);
763 HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE);
767 int buttonWidth, buttonHeight, lineHeight, lineWidth;
768 PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
774 * Obtain the size of the buttons.
776 GetClientRect(hwndButton, &rcSheet);
777 buttonWidth = rcSheet.right;
778 buttonHeight = rcSheet.bottom;
780 GetClientRect(hwndLine, &rcSheet);
781 lineHeight = rcSheet.bottom;
784 * Get the size of the property sheet.
786 GetClientRect(hwndParent, &rcSheet);
789 * All buttons will be at this y coordinate.
791 y = rcSheet.bottom - (padding.y + buttonHeight);
794 * Position the Next and the Finish buttons.
796 hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON);
798 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1));
800 SetWindowPos(hwndButton, 0, x, y, 0, 0,
801 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
803 hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON);
805 SetWindowPos(hwndButton, 0, x, y, 0, 0,
806 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
808 ShowWindow(hwndButton, SW_HIDE);
811 * Position the Back button.
813 hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON);
817 SetWindowPos(hwndButton, 0, x, y, 0, 0,
818 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
821 * Position the Cancel button.
823 hwndButton = GetDlgItem(hwndParent, IDCANCEL);
825 x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 2));
827 SetWindowPos(hwndButton, 0, x, y, 0, 0,
828 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
831 * Position Help button.
833 hwndButton = GetDlgItem(hwndParent, IDHELP);
837 x = rcSheet.right - (padding.x + buttonWidth);
839 SetWindowPos(hwndButton, 0, x, y, 0, 0,
840 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
843 ShowWindow(hwndButton, SW_HIDE);
846 * Position and resize the sunken line.
849 y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight);
851 GetClientRect(hwndParent, &rcSheet);
852 lineWidth = rcSheet.right - (padding.x * 2);
854 SetWindowPos(hwndLine, 0, x, y, lineWidth, 2,
855 SWP_NOZORDER | SWP_NOACTIVATE);
860 /******************************************************************************
861 * PROPSHEET_GetPaddingInfo
863 * Returns the layout information.
865 static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg)
867 HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL);
870 PADDING_INFO padding;
872 GetWindowRect(hwndTab, &rcTab);
877 ScreenToClient(hwndDlg, &tl);
885 /******************************************************************************
886 * PROPSHEET_GetPaddingInfoWizard
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.
897 static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg, const PropSheetInfo*
900 PADDING_INFO padding;
904 POINT ptButton, ptLine;
912 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
914 idButton = IDC_NEXT_BUTTON;
918 /* hopefully this is ok */
923 hwndControl = GetDlgItem(hwndDlg, idButton);
924 GetWindowRect(hwndControl, &rc);
926 ptButton.x = rc.left;
929 ScreenToClient(hwndDlg, &ptButton);
932 hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE);
933 GetWindowRect(hwndControl, &rc);
936 ptLine.y = rc.bottom;
938 ScreenToClient(hwndDlg, &ptLine);
940 padding.y = ptButton.y - ptLine.y;
943 ERR("padding negative ! Please report this !\n");
945 /* this is most probably not correct, but the best we have now */
946 padding.x = padding.y;
950 /******************************************************************************
951 * PROPSHEET_CreateTabControl
953 * Insert the tabs in the tab control.
955 static BOOL PROPSHEET_CreateTabControl(HWND hwndParent,
956 PropSheetInfo * psInfo)
958 HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
962 char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
964 item.mask = TCIF_TEXT;
965 item.pszText = tabtext;
966 item.cchTextMax = MAX_TABTEXT_LENGTH;
968 nTabs = psInfo->nPages;
971 * Set the image list for icons.
973 if (psInfo->hImageList)
975 SendMessageA(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList);
978 for (i = 0; i < nTabs; i++)
980 if ( psInfo->proppage[i].hasIcon )
982 item.mask |= TCIF_IMAGE;
983 item.iImage = iImage++;
987 item.mask &= ~TCIF_IMAGE;
990 WideCharToMultiByte(CP_ACP, 0,
991 (LPCWSTR)psInfo->proppage[i].pszText,
992 -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
994 SendMessageA(hwndTabCtrl, TCM_INSERTITEMA, (WPARAM)i, (LPARAM)&item);
1000 /******************************************************************************
1001 * PROPSHEET_CreatePage
1005 static int PROPSHEET_CreatePage(HWND hwndParent,
1007 const PropSheetInfo * psInfo,
1008 LPCPROPSHEETPAGEA ppshpage)
1010 DLGTEMPLATE* pTemplate;
1013 PropPageInfo* ppInfo = psInfo->proppage;
1014 PADDING_INFO padding;
1015 UINT pageWidth,pageHeight;
1017 TRACE("index %d\n", index);
1019 if (ppshpage->dwFlags & PSP_DLGINDIRECT)
1020 pTemplate = (DLGTEMPLATE*)ppshpage->u.pResource;
1023 HRSRC hResource = FindResourceA(ppshpage->hInstance,
1024 ppshpage->u.pszTemplate,
1026 HGLOBAL hTemplate = LoadResource(ppshpage->hInstance, hResource);
1027 pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
1030 if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
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;
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;
1051 if (psInfo->proppage[index].useCallback)
1052 (*(ppshpage->pfnCallback))(hwndParent,
1054 (LPPROPSHEETPAGEA)ppshpage);
1056 hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance,
1059 ppshpage->pfnDlgProc,
1062 ppInfo[index].hwndPage = hwndPage;
1064 rc.left = psInfo->x;
1066 rc.right = psInfo->width;
1067 rc.bottom = psInfo->height;
1069 MapDialogRect(hwndParent, &rc);
1071 pageWidth = rc.right - rc.left;
1072 pageHeight = rc.bottom - rc.top;
1074 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
1075 padding = PROPSHEET_GetPaddingInfoWizard(hwndParent, psInfo);
1079 * Ask the Tab control to fit this page in.
1082 HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL);
1083 SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&rc);
1084 padding = PROPSHEET_GetPaddingInfo(hwndParent);
1087 SetWindowPos(hwndPage, HWND_TOP,
1088 rc.left + padding.x,
1090 pageWidth, pageHeight, 0);
1095 /******************************************************************************
1096 * PROPSHEET_ShowPage
1098 * Displays or creates the specified page.
1100 static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo)
1102 if (index == psInfo->active_page)
1104 if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage)
1105 SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1109 if (psInfo->proppage[index].hwndPage == 0)
1111 LPCPROPSHEETPAGEA ppshpage;
1114 ppshpage = (LPCPROPSHEETPAGEA)psInfo->proppage[index].hpage;
1115 PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage);
1117 psn.hdr.hwndFrom = hwndDlg;
1118 psn.hdr.code = PSN_SETACTIVE;
1122 /* Send the notification before showing the page. */
1123 SendMessageA(psInfo->proppage[index].hwndPage,
1124 WM_NOTIFY, 0, (LPARAM) &psn);
1127 * TODO: check return value.
1131 if (psInfo->active_page != -1)
1132 ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE);
1134 ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW);
1136 if (!(psInfo->ppshheader.dwFlags & PSH_WIZARD))
1140 /* Synchronize current selection with tab control */
1141 hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1142 SendMessageA(hwndTabCtrl, TCM_SETCURSEL, index, 0);
1145 psInfo->active_page = index;
1146 psInfo->activeValid = TRUE;
1151 /******************************************************************************
1154 static BOOL PROPSHEET_Back(HWND hwndDlg)
1159 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1162 if (psInfo->active_page < 0)
1165 psn.hdr.code = PSN_WIZBACK;
1166 psn.hdr.hwndFrom = hwndDlg;
1170 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1172 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) == -1)
1175 if (psInfo->active_page > 0)
1177 res = PROPSHEET_CanSetCurSel(hwndDlg);
1180 res = PROPSHEET_SetCurSel(hwndDlg, psInfo->active_page - 1, 0);
1187 /******************************************************************************
1190 static BOOL PROPSHEET_Next(HWND hwndDlg)
1194 LRESULT msgResult = 0;
1195 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1198 if (psInfo->active_page < 0)
1201 psn.hdr.code = PSN_WIZNEXT;
1202 psn.hdr.hwndFrom = hwndDlg;
1206 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1208 msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1210 TRACE("msg result %ld\n", msgResult);
1212 if (msgResult == -1)
1215 if(PROPSHEET_CanSetCurSel(hwndDlg) != FALSE)
1217 PROPSHEET_SetCurSel(hwndDlg, psInfo->active_page + 1, 0);
1223 /******************************************************************************
1226 static BOOL PROPSHEET_Finish(HWND hwndDlg)
1230 LRESULT msgResult = 0;
1231 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1234 if (psInfo->active_page < 0)
1237 psn.hdr.code = PSN_WIZFINISH;
1238 psn.hdr.hwndFrom = hwndDlg;
1242 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1244 msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1246 TRACE("msg result %ld\n", msgResult);
1251 if (psInfo->isModeless)
1252 psInfo->activeValid = FALSE;
1254 EndDialog(hwndDlg, TRUE);
1259 /******************************************************************************
1262 static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam)
1268 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1271 if (psInfo->active_page < 0)
1274 psn.hdr.hwndFrom = hwndDlg;
1280 * Send PSN_KILLACTIVE to the current page.
1282 psn.hdr.code = PSN_KILLACTIVE;
1284 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1286 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE)
1290 * Send PSN_APPLY to all pages.
1292 psn.hdr.code = PSN_APPLY;
1293 psn.lParam = lParam;
1295 for (i = 0; i < psInfo->nPages; i++)
1297 hwndPage = psInfo->proppage[i].hwndPage;
1300 msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1301 if (msgResult == PSNRET_INVALID_NOCHANGEPAGE)
1308 psInfo->activeValid = FALSE;
1310 else if(psInfo->active_page >= 0)
1312 psn.hdr.code = PSN_SETACTIVE;
1314 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1315 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1321 /******************************************************************************
1324 static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam)
1326 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1332 if (psInfo->active_page < 0)
1335 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1336 psn.hdr.code = PSN_QUERYCANCEL;
1337 psn.hdr.hwndFrom = hwndDlg;
1341 if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn))
1344 psn.hdr.code = PSN_RESET;
1345 psn.lParam = lParam;
1347 for (i = 0; i < psInfo->nPages; i++)
1349 hwndPage = psInfo->proppage[i].hwndPage;
1352 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1355 if (psInfo->isModeless)
1357 /* makes PSM_GETCURRENTPAGEHWND return NULL */
1358 psInfo->activeValid = FALSE;
1361 EndDialog(hwndDlg, FALSE);
1364 /******************************************************************************
1367 static void PROPSHEET_Help(HWND hwndDlg)
1369 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1374 if (psInfo->active_page < 0)
1377 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1378 psn.hdr.code = PSN_HELP;
1379 psn.hdr.hwndFrom = hwndDlg;
1383 SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1386 /******************************************************************************
1389 static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage)
1392 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1395 if (!psInfo) return;
1397 * Set the dirty flag of this page.
1399 for (i = 0; i < psInfo->nPages; i++)
1401 if (psInfo->proppage[i].hwndPage == hwndDirtyPage)
1402 psInfo->proppage[i].isDirty = TRUE;
1406 * Enable the Apply button.
1408 if (psInfo->hasApply)
1410 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1412 EnableWindow(hwndApplyBtn, TRUE);
1416 /******************************************************************************
1417 * PROPSHEET_UnChanged
1419 static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage)
1422 BOOL noPageDirty = TRUE;
1423 HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON);
1424 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1427 if ( !psInfo ) return;
1428 for (i = 0; i < psInfo->nPages; i++)
1430 /* set the specified page as clean */
1431 if (psInfo->proppage[i].hwndPage == hwndCleanPage)
1432 psInfo->proppage[i].isDirty = FALSE;
1434 /* look to see if there's any dirty pages */
1435 if (psInfo->proppage[i].isDirty)
1436 noPageDirty = FALSE;
1440 * Disable Apply button.
1443 EnableWindow(hwndApplyBtn, FALSE);
1446 /******************************************************************************
1447 * PROPSHEET_PressButton
1449 static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID)
1453 case PSBTN_APPLYNOW:
1454 SendMessageA(hwndDlg, WM_COMMAND, IDC_APPLY_BUTTON, 0);
1457 PROPSHEET_Back(hwndDlg);
1460 SendMessageA(hwndDlg, WM_COMMAND, IDCANCEL, 0);
1463 PROPSHEET_Finish(hwndDlg);
1466 SendMessageA(hwndDlg, WM_COMMAND, IDHELP, 0);
1469 PROPSHEET_Next(hwndDlg);
1472 SendMessageA(hwndDlg, WM_COMMAND, IDOK, 0);
1475 FIXME("Invalid button index %d\n", buttonID);
1480 /*************************************************************************
1481 * BOOL PROPSHEET_CanSetCurSel [Internal]
1483 * Test weither the current page can be changed by sending a PSN_KILLACTIVE
1486 * hwndDlg [I] handle to a Dialog hWnd
1489 * TRUE if Current Selection can change
1493 static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg)
1495 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1503 if (psInfo->active_page < 0)
1507 * Notify the current page.
1509 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
1510 psn.hdr.code = PSN_KILLACTIVE;
1511 psn.hdr.hwndFrom = hwndDlg;
1515 return !SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1518 /******************************************************************************
1519 * PROPSHEET_SetCurSel
1521 static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
1523 HPROPSHEETPAGE hpage)
1525 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1528 HWND hwndHelp = GetDlgItem(hwndDlg, IDHELP);
1530 /* hpage takes precedence over index */
1532 index = PROPSHEET_GetPageIndex(hpage, psInfo);
1534 if (index < 0 || index >= psInfo->nPages)
1536 TRACE("Could not find page to select!\n");
1540 hwndPage = psInfo->proppage[index].hwndPage;
1543 * Notify the new page if it's already created.
1544 * If not it will get created and notified in PROPSHEET_ShowPage.
1551 psn.hdr.code = PSN_SETACTIVE;
1552 psn.hdr.hwndFrom = hwndDlg;
1556 result = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn);
1559 * TODO: check return value.
1564 * Display the new page.
1566 PROPSHEET_ShowPage(hwndDlg, index, psInfo);
1568 if (psInfo->proppage[index].hasHelp)
1569 EnableWindow(hwndHelp, TRUE);
1571 EnableWindow(hwndHelp, FALSE);
1576 /******************************************************************************
1577 * PROPSHEET_SetTitleA
1579 static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText)
1581 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr);
1584 if (HIWORD(lpszText) == 0) {
1585 if (!LoadStringA(psInfo->ppshheader.hInstance,
1586 LOWORD(lpszText), szTitle, sizeof(szTitle)-1))
1590 if (dwStyle & PSH_PROPTITLE)
1593 int lentitle = strlen(lpszText);
1594 int lenprop = strlen(psInfo->strPropertiesFor);
1596 dest = COMCTL32_Alloc(lentitle + lenprop + 1);
1597 strcpy(dest, psInfo->strPropertiesFor);
1598 strcat(dest, lpszText);
1600 SetWindowTextA(hwndDlg, dest);
1601 COMCTL32_Free(dest);
1604 SetWindowTextA(hwndDlg, lpszText);
1607 /******************************************************************************
1608 * PROPSHEET_SetFinishTextA
1610 static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText)
1612 HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
1614 /* Set text, show and enable the Finish button */
1615 SetWindowTextA(hwndButton, lpszText);
1616 ShowWindow(hwndButton, SW_SHOW);
1617 EnableWindow(hwndButton, TRUE);
1619 /* Make it default pushbutton */
1620 SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
1622 /* Hide Back button */
1623 hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
1624 ShowWindow(hwndButton, SW_HIDE);
1626 /* Hide Next button */
1627 hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
1628 ShowWindow(hwndButton, SW_HIDE);
1631 /******************************************************************************
1632 * PROPSHEET_QuerySiblings
1634 static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg,
1635 WPARAM wParam, LPARAM lParam)
1639 LRESULT msgResult = 0;
1640 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1643 while ((i < psInfo->nPages) && (msgResult == 0))
1645 hwndPage = psInfo->proppage[i].hwndPage;
1646 msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam);
1654 /******************************************************************************
1657 static BOOL PROPSHEET_AddPage(HWND hwndDlg,
1658 HPROPSHEETPAGE hpage)
1660 PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1662 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1664 char tabtext[MAX_TABTEXT_LENGTH] = "Tab text";
1665 LPCPROPSHEETPAGEA ppsp = (LPCPROPSHEETPAGEA)hpage;
1668 * Allocate and fill in a new PropPageInfo entry.
1670 psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage,
1671 sizeof(PropPageInfo) *
1672 (psInfo->nPages + 1));
1673 if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages))
1676 psInfo->proppage[psInfo->nPages].hpage = hpage;
1678 if (ppsp->dwFlags & PSP_PREMATURE)
1680 /* Create the page but don't show it */
1681 PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp);
1685 * Add a new tab to the tab control.
1687 item.mask = TCIF_TEXT;
1688 item.pszText = tabtext;
1689 item.cchTextMax = MAX_TABTEXT_LENGTH;
1691 WideCharToMultiByte(CP_ACP, 0,
1692 (LPCWSTR)psInfo->proppage[psInfo->nPages].pszText,
1693 -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL);
1695 SendMessageA(hwndTabControl, TCM_INSERTITEMA, psInfo->nPages + 1,
1700 /* If it is the only page - show it */
1701 if(psInfo->nPages == 1)
1702 PROPSHEET_ShowPage(hwndDlg, 0, psInfo);
1707 /******************************************************************************
1708 * PROPSHEET_RemovePage
1710 static BOOL PROPSHEET_RemovePage(HWND hwndDlg,
1712 HPROPSHEETPAGE hpage)
1714 PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg,
1716 HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL);
1717 PropPageInfo* oldPages;
1722 oldPages = psInfo->proppage;
1724 * hpage takes precedence over index.
1728 index = PROPSHEET_GetPageIndex(hpage, psInfo);
1731 /* Make sure that index is within range */
1732 if (index < 0 || index >= psInfo->nPages)
1734 TRACE("Could not find page to remove!\n");
1738 TRACE("total pages %d removing page %d active page %d\n",
1739 psInfo->nPages, index, psInfo->active_page);
1741 * Check if we're removing the active page.
1743 if (index == psInfo->active_page)
1745 if (psInfo->nPages > 1)
1749 /* activate previous page */
1750 PROPSHEET_ShowPage(hwndDlg, index - 1, psInfo);
1754 /* activate the next page */
1755 PROPSHEET_ShowPage(hwndDlg, index + 1, psInfo);
1756 psInfo->active_page = index;
1761 psInfo->active_page = -1;
1762 if (!psInfo->isModeless)
1764 EndDialog(hwndDlg, FALSE);
1769 else if (index < psInfo->active_page)
1770 psInfo->active_page--;
1772 /* Destroy page dialog window */
1773 DestroyWindow(psInfo->proppage[index].hwndPage);
1775 /* Free page resources */
1776 if(psInfo->proppage[index].hpage)
1778 PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[index].hpage;
1780 if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[index].pszText)
1781 HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[index].pszText);
1783 DestroyPropertySheetPage(psInfo->proppage[index].hpage);
1786 /* Remove the tab */
1787 SendMessageA(hwndTabControl, TCM_DELETEITEM, index, 0);
1790 psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages);
1793 memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo));
1795 if (index < psInfo->nPages)
1796 memcpy(&psInfo->proppage[index], &oldPages[index + 1],
1797 (psInfo->nPages - index) * sizeof(PropPageInfo));
1799 COMCTL32_Free(oldPages);
1804 /******************************************************************************
1805 * PROPSHEET_SetWizButtons
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.
1812 static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags)
1814 HWND hwndBack = GetDlgItem(hwndDlg, IDC_BACK_BUTTON);
1815 HWND hwndNext = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON);
1816 HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON);
1818 TRACE("%ld\n", dwFlags);
1820 EnableWindow(hwndBack, FALSE);
1821 EnableWindow(hwndNext, FALSE);
1822 EnableWindow(hwndFinish, FALSE);
1824 if (dwFlags & PSWIZB_BACK)
1825 EnableWindow(hwndBack, TRUE);
1827 if (dwFlags & PSWIZB_NEXT)
1829 /* Hide the Finish button */
1830 ShowWindow(hwndFinish, SW_HIDE);
1832 /* Show and enable the Next button */
1833 ShowWindow(hwndNext, SW_SHOW);
1834 EnableWindow(hwndNext, TRUE);
1836 /* Set the Next button as the default pushbutton */
1837 SendMessageA(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0);
1840 if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH))
1842 /* Hide the Next button */
1843 ShowWindow(hwndNext, SW_HIDE);
1845 /* Show the Finish button */
1846 ShowWindow(hwndFinish, SW_SHOW);
1848 if (dwFlags & PSWIZB_FINISH)
1849 EnableWindow(hwndFinish, TRUE);
1851 /* Set the Finish button as the default pushbutton */
1852 SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0);
1856 /******************************************************************************
1857 * PROPSHEET_GetPageIndex
1859 * Given a HPROPSHEETPAGE, returns the index of the corresponding page from
1860 * the array of PropPageInfo.
1862 static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo)
1867 while ((index < psInfo->nPages) && (found == FALSE))
1869 if (psInfo->proppage[index].hpage == hpage)
1881 /******************************************************************************
1884 static void PROPSHEET_CleanUp(HWND hwndDlg)
1887 PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropA(hwndDlg,
1891 if (HIWORD(psInfo->ppshheader.pszCaption))
1892 HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->ppshheader.pszCaption);
1894 for (i = 0; i < psInfo->nPages; i++)
1896 PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage;
1898 if(psInfo->proppage[i].hwndPage)
1899 DestroyWindow(psInfo->proppage[i].hwndPage);
1903 if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[i].pszText)
1904 HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[i].pszText);
1906 DestroyPropertySheetPage(psInfo->proppage[i].hpage);
1910 COMCTL32_Free(psInfo->proppage);
1911 COMCTL32_Free(psInfo->strPropertiesFor);
1912 ImageList_Destroy(psInfo->hImageList);
1914 GlobalFree((HGLOBAL)psInfo);
1917 /******************************************************************************
1918 * PropertySheetA (COMCTL32.84)(COMCTL32.83)
1920 INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh)
1923 PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR,
1924 sizeof(PropSheetInfo));
1928 PROPSHEET_CollectSheetInfo(lppsh, psInfo);
1930 psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) *
1932 pByte = (BYTE*) psInfo->ppshheader.u3.ppsp;
1934 for (n = i = 0; i < lppsh->nPages; i++, n++)
1936 if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE))
1937 psInfo->proppage[n].hpage = psInfo->ppshheader.u3.phpage[i];
1940 psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte);
1941 pByte += ((LPPROPSHEETPAGEA)pByte)->dwSize;
1944 if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEA)psInfo->proppage[n].hpage,
1947 if (lppsh->dwFlags & PSH_PROPSHEETPAGE)
1948 DestroyPropertySheetPage(psInfo->proppage[n].hpage);
1954 bRet = PROPSHEET_CreateDialog(psInfo);
1959 /******************************************************************************
1960 * PropertySheetW (COMCTL32.85)
1962 INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW propertySheetHeader)
1964 FIXME("(%p): stub\n", propertySheetHeader);
1969 /******************************************************************************
1970 * CreatePropertySheetPageA (COMCTL32.19)(COMCTL32.18)
1972 HPROPSHEETPAGE WINAPI CreatePropertySheetPageA(
1973 LPCPROPSHEETPAGEA lpPropSheetPage)
1975 PROPSHEETPAGEA* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEA));
1977 *ppsp = *lpPropSheetPage;
1979 if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u.pszTemplate ) )
1980 ppsp->u.pszTemplate = HEAP_strdupA( GetProcessHeap(), 0, lpPropSheetPage->u.pszTemplate );
1982 if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) )
1983 ppsp->u2.pszIcon = HEAP_strdupA( GetProcessHeap(), 0, lpPropSheetPage->u2.pszIcon );
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;
1991 return (HPROPSHEETPAGE)ppsp;
1994 /******************************************************************************
1995 * CreatePropertySheetPageW (COMCTL32.20)
1997 HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage)
1999 FIXME("(%p): stub\n", lpPropSheetPage);
2004 /******************************************************************************
2005 * DestroyPropertySheetPage (COMCTL32.24)
2007 BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage)
2009 PROPSHEETPAGEA *psp = (PROPSHEETPAGEA *)hPropPage;
2014 if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u.pszTemplate ) )
2015 HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u.pszTemplate);
2017 if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) )
2018 HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u2.pszIcon);
2020 if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle ))
2021 HeapFree(GetProcessHeap(), 0, (LPVOID)psp->pszTitle);
2023 COMCTL32_Free(hPropPage);
2028 /******************************************************************************
2029 * PROPSHEET_IsDialogMessage
2031 static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg)
2033 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr);
2035 if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd)))
2038 if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000))
2041 INT dlgCode = SendMessageA(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg);
2043 if (!(dlgCode & DLGC_WANTMESSAGE))
2045 switch (lpMsg->wParam)
2048 if (GetKeyState(VK_SHIFT) & 0x8000)
2054 case VK_NEXT: new_page = 1; break;
2055 case VK_PRIOR: new_page = -1; break;
2061 if (PROPSHEET_CanSetCurSel(hwnd) != FALSE)
2063 new_page += psInfo->active_page;
2066 new_page = psInfo->nPages - 1;
2067 else if (new_page >= psInfo->nPages)
2070 PROPSHEET_SetCurSel(hwnd, new_page, 0);
2077 return IsDialogMessageA(hwnd, lpMsg);
2080 /******************************************************************************
2081 * PROPSHEET_DialogProc
2084 PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2090 PropSheetInfo* psInfo = (PropSheetInfo*) lParam;
2091 char* strCaption = (char*)COMCTL32_Alloc(MAX_CAPTION_LENGTH);
2092 HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2093 LPCPROPSHEETPAGEA ppshpage;
2096 SetPropA(hwnd, PropSheetInfoStr, (HANDLE)psInfo);
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
2104 psInfo->hwnd = hwnd;
2105 SetWindowLongA(hwnd,DWL_USER,(LONG)psInfo);
2108 * Small icon in the title bar.
2110 if ((psInfo->ppshheader.dwFlags & PSH_USEICONID) ||
2111 (psInfo->ppshheader.dwFlags & PSH_USEHICON))
2114 int icon_cx = GetSystemMetrics(SM_CXSMICON);
2115 int icon_cy = GetSystemMetrics(SM_CYSMICON);
2117 if (psInfo->ppshheader.dwFlags & PSH_USEICONID)
2118 hIcon = LoadImageA(psInfo->ppshheader.hInstance,
2119 psInfo->ppshheader.u.pszIcon,
2124 hIcon = psInfo->ppshheader.u.hIcon;
2126 SendMessageA(hwnd, WM_SETICON, 0, hIcon);
2129 if (psInfo->ppshheader.dwFlags & PSH_USEHICON)
2130 SendMessageA(hwnd, WM_SETICON, 0, psInfo->ppshheader.u.hIcon);
2132 psInfo->strPropertiesFor = strCaption;
2134 GetWindowTextA(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH);
2136 PROPSHEET_CreateTabControl(hwnd, psInfo);
2138 if (psInfo->ppshheader.dwFlags & PSH_WIZARD)
2140 if (PROPSHEET_IsTooSmallWizard(hwnd, psInfo))
2142 PROPSHEET_AdjustSizeWizard(hwnd, psInfo);
2143 PROPSHEET_AdjustButtonsWizard(hwnd, psInfo);
2148 if (PROPSHEET_SizeMismatch(hwnd, psInfo))
2150 PROPSHEET_AdjustSize(hwnd, psInfo);
2151 PROPSHEET_AdjustButtons(hwnd, psInfo);
2155 if (psInfo->useCallback)
2156 (*(psInfo->ppshheader.pfnCallback))(hwnd,
2157 PSCB_INITIALIZED, (LPARAM)0);
2159 idx = psInfo->active_page;
2160 ppshpage = (LPCPROPSHEETPAGEA)psInfo->proppage[idx].hpage;
2161 psInfo->active_page = -1;
2163 PROPSHEET_SetCurSel(hwnd, idx, psInfo->proppage[idx].hpage);
2165 if (!(psInfo->ppshheader.dwFlags & PSH_WIZARD))
2166 SendMessageA(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0);
2168 if (!HIWORD(psInfo->ppshheader.pszCaption) &&
2169 psInfo->ppshheader.hInstance)
2173 if (LoadStringA(psInfo->ppshheader.hInstance,
2174 (UINT)psInfo->ppshheader.pszCaption, szText, 255))
2175 PROPSHEET_SetTitleA(hwnd, psInfo->ppshheader.dwFlags, szText);
2179 PROPSHEET_SetTitleA(hwnd, psInfo->ppshheader.dwFlags,
2180 psInfo->ppshheader.pszCaption);
2187 PROPSHEET_CleanUp(hwnd);
2191 PROPSHEET_Cancel(hwnd, 1);
2196 WORD wID = LOWORD(wParam);
2201 case IDC_APPLY_BUTTON:
2203 HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON);
2205 if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE)
2210 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2214 if (psInfo->restartWindows)
2215 result = ID_PSRESTARTWINDOWS;
2217 /* reboot system takes precedence over restart windows */
2218 if (psInfo->rebootSystem)
2219 result = ID_PSREBOOTSYSTEM;
2221 if (psInfo->isModeless)
2222 psInfo->activeValid = FALSE;
2224 EndDialog(hwnd, result);
2227 EnableWindow(hwndApplyBtn, FALSE);
2232 case IDC_BACK_BUTTON:
2233 PROPSHEET_Back(hwnd);
2236 case IDC_NEXT_BUTTON:
2237 PROPSHEET_Next(hwnd);
2240 case IDC_FINISH_BUTTON:
2241 PROPSHEET_Finish(hwnd);
2245 PROPSHEET_Cancel(hwnd, 0);
2249 PROPSHEET_Help(hwnd);
2258 NMHDR* pnmh = (LPNMHDR) lParam;
2260 if (pnmh->code == TCN_SELCHANGE)
2262 int index = SendMessageA(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
2263 PROPSHEET_SetCurSel(hwnd, index, 0);
2266 if(pnmh->code == TCN_SELCHANGING)
2268 BOOL bRet = PROPSHEET_CanSetCurSel(hwnd);
2269 SetWindowLongA(hwnd, DWL_MSGRESULT, !bRet);
2277 case PSM_GETCURRENTPAGEHWND:
2279 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2283 if (psInfo->activeValid && psInfo->active_page != -1)
2284 hwndPage = psInfo->proppage[psInfo->active_page].hwndPage;
2286 SetWindowLongA(hwnd, DWL_MSGRESULT, hwndPage);
2292 PROPSHEET_Changed(hwnd, (HWND)wParam);
2296 PROPSHEET_UnChanged(hwnd, (HWND)wParam);
2299 case PSM_GETTABCONTROL:
2301 HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL);
2303 SetWindowLongA(hwnd, DWL_MSGRESULT, hwndTabCtrl);
2312 msgResult = PROPSHEET_CanSetCurSel(hwnd);
2313 if(msgResult != FALSE)
2315 msgResult = PROPSHEET_SetCurSel(hwnd,
2317 (HPROPSHEETPAGE)lParam);
2320 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2325 case PSM_CANCELTOCLOSE:
2327 char buf[MAX_BUTTONTEXT_LENGTH];
2328 HWND hwndOK = GetDlgItem(hwnd, IDOK);
2329 HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL);
2331 EnableWindow(hwndCancel, FALSE);
2332 if (LoadStringA(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)))
2333 SetWindowTextA(hwndOK, buf);
2338 case PSM_RESTARTWINDOWS:
2340 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2343 psInfo->restartWindows = TRUE;
2347 case PSM_REBOOTSYSTEM:
2349 PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd,
2352 psInfo->rebootSystem = TRUE;
2357 PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam);
2362 BOOL msgResult = PROPSHEET_Apply(hwnd, 0);
2364 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2369 case PSM_QUERYSIBLINGS:
2371 LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam);
2373 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
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).
2388 BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam);
2390 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2395 case PSM_REMOVEPAGE:
2396 PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam);
2399 case PSM_ISDIALOGMESSAGE:
2401 BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam);
2402 SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult);
2406 case PSM_PRESSBUTTON:
2407 PROPSHEET_PressButton(hwnd, (int)wParam);
2410 case PSM_SETFINISHTEXTA:
2411 PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam);
2414 case PSM_SETWIZBUTTONS:
2415 PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam);
2419 FIXME("Unimplemented msg PSM_SETTITLE32W\n");
2421 case PSM_SETCURSELID:
2422 FIXME("Unimplemented msg PSM_SETCURSELID\n");
2424 case PSM_SETFINISHTEXTW:
2425 FIXME("Unimplemented msg PSM_SETFINISHTEXT32W\n");