/* * Property Sheets * * Copyright 1998 Francis Beaudet * Copyright 1999 Thuy Nguyen * * TODO: * - Tab order * - Unicode property sheets */ #include #include "winbase.h" #include "commctrl.h" #include "prsht.h" #include "dialog.h" #include "win.h" #include "winnls.h" #include "comctl32.h" #include "debugtools.h" #include "heap.h" /****************************************************************************** * Data structures */ typedef struct { WORD dlgVer; WORD signature; DWORD helpID; DWORD exStyle; DWORD style; } MyDLGTEMPLATEEX; typedef struct tagPropPageInfo { HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */ HWND hwndPage; BOOL isDirty; LPCWSTR pszText; BOOL hasHelp; BOOL useCallback; BOOL hasIcon; } PropPageInfo; typedef struct tagPropSheetInfo { LPSTR strPropertiesFor; int nPages; int active_page; LPPROPSHEETHEADERA ppshheader; BOOL isModeless; BOOL hasHelp; BOOL hasApply; BOOL useCallback; BOOL restartWindows; BOOL rebootSystem; BOOL activeValid; PropPageInfo* proppage; int x; int y; int width; int height; HIMAGELIST hImageList; } PropSheetInfo; typedef struct { int x; int y; } PADDING_INFO; /****************************************************************************** * Defines and global variables */ const char * PropSheetInfoStr = "PropertySheetInfo"; #define MAX_CAPTION_LENGTH 255 #define MAX_TABTEXT_LENGTH 255 #define MAX_BUTTONTEXT_LENGTH 64 /****************************************************************************** * Prototypes */ static BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo); static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo); static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo); static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo); static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh, PropSheetInfo * psInfo); static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp, PropSheetInfo * psInfo, int index); static BOOL PROPSHEET_CreateTabControl(HWND hwndParent, PropSheetInfo * psInfo); static int PROPSHEET_CreatePage(HWND hwndParent, int index, const PropSheetInfo * psInfo, LPCPROPSHEETPAGEA ppshpage); static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo); static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg); static BOOL PROPSHEET_Back(HWND hwndDlg); static BOOL PROPSHEET_Next(HWND hwndDlg); static BOOL PROPSHEET_Finish(HWND hwndDlg); static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam); static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam); static void PROPSHEET_Help(HWND hwndDlg); static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage); static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage); static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID); static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText); static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText); static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg); static BOOL PROPSHEET_SetCurSel(HWND hwndDlg, int index, HPROPSHEETPAGE hpage); static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg, WPARAM wParam, LPARAM lParam); static BOOL PROPSHEET_AddPage(HWND hwndDlg, HPROPSHEETPAGE hpage); static BOOL PROPSHEET_RemovePage(HWND hwndDlg, int index, HPROPSHEETPAGE hpage); static void PROPSHEET_CleanUp(); static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo); static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags); static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg); static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg); static INT PROPSHEET_DoDialogBox( HWND hwnd, HWND owner); BOOL WINAPI PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); DEFAULT_DEBUG_CHANNEL(propsheet) /****************************************************************************** * PROPSHEET_CollectSheetInfo * * Collect relevant data. */ static BOOL PROPSHEET_CollectSheetInfo(LPCPROPSHEETHEADERA lppsh, PropSheetInfo * psInfo) { DWORD dwFlags = lppsh->dwFlags; psInfo->hasHelp = dwFlags & PSH_HASHELP; psInfo->hasApply = !(dwFlags & PSH_NOAPPLYNOW); psInfo->useCallback = dwFlags & PSH_USECALLBACK; psInfo->isModeless = dwFlags & PSH_MODELESS; psInfo->ppshheader = COMCTL32_Alloc(sizeof(PROPSHEETHEADERA)); *psInfo->ppshheader = *lppsh; if (HIWORD(lppsh->pszCaption)) psInfo->ppshheader->pszCaption = HEAP_strdupA( GetProcessHeap(), 0, lppsh->pszCaption ); psInfo->nPages = lppsh->nPages; if (dwFlags & PSH_USEPSTARTPAGE) { TRACE("PSH_USEPSTARTPAGE is on"); psInfo->active_page = 0; } else psInfo->active_page = lppsh->u2.nStartPage; if (psInfo->active_page < 0 || psInfo->active_page >= psInfo->nPages) psInfo->active_page = 0; psInfo->restartWindows = FALSE; psInfo->rebootSystem = FALSE; psInfo->hImageList = 0; psInfo->activeValid = FALSE; return TRUE; } /****************************************************************************** * PROPSHEET_CollectPageInfo * * Collect property sheet data. * With code taken from DIALOG_ParseTemplate32. */ BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEA lppsp, PropSheetInfo * psInfo, int index) { DLGTEMPLATE* pTemplate; const WORD* p; DWORD dwFlags; int width, height; psInfo->proppage[index].hpage = (HPROPSHEETPAGE)lppsp; psInfo->proppage[index].hwndPage = 0; psInfo->proppage[index].isDirty = FALSE; /* * Process property page flags. */ dwFlags = lppsp->dwFlags; psInfo->proppage[index].useCallback = dwFlags & PSP_USECALLBACK; psInfo->proppage[index].hasHelp = dwFlags & PSP_HASHELP; psInfo->proppage[index].hasIcon = dwFlags & (PSP_USEHICON | PSP_USEICONID); /* as soon as we have a page with the help flag, set the sheet flag on */ if (psInfo->proppage[index].hasHelp) psInfo->hasHelp = TRUE; /* * Process page template. */ if (dwFlags & PSP_DLGINDIRECT) pTemplate = (DLGTEMPLATE*)lppsp->u1.pResource; else { HRSRC hResource = FindResourceA(lppsp->hInstance, lppsp->u1.pszTemplate, RT_DIALOGA); HGLOBAL hTemplate = LoadResource(lppsp->hInstance, hResource); pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate); } /* * Extract the size of the page and the caption. */ if (!pTemplate) return FALSE; p = (const WORD *)pTemplate; if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF) { /* DIALOGEX template */ p++; /* dlgVer */ p++; /* signature */ p += 2; /* help ID */ p += 2; /* ext style */ p += 2; /* style */ } else { /* DIALOG template */ p += 2; /* style */ p += 2; /* ext style */ } p++; /* nb items */ p++; /* x */ p++; /* y */ width = (WORD)*p; p++; height = (WORD)*p; p++; /* remember the largest width and height */ if (width > psInfo->width) psInfo->width = width; if (height > psInfo->height) psInfo->height = height; /* menu */ switch ((WORD)*p) { case 0x0000: p++; break; case 0xffff: p += 2; break; default: p += lstrlenW( (LPCWSTR)p ) + 1; break; } /* class */ switch ((WORD)*p) { case 0x0000: p++; break; case 0xffff: p += 2; break; default: p += lstrlenW( (LPCWSTR)p ) + 1; break; } /* Extract the caption */ psInfo->proppage[index].pszText = (LPCWSTR)p; TRACE("Tab %d %s\n",index,debugstr_w((LPCWSTR)p)); p += lstrlenW((LPCWSTR)p) + 1; if (dwFlags & PSP_USETITLE) { if ( !HIWORD( lppsp->pszTitle ) ) { char szTitle[256]; if ( !LoadStringA( lppsp->hInstance, (UINT) lppsp->pszTitle, szTitle, 256 ) ) return FALSE; psInfo->proppage[index].pszText = HEAP_strdupAtoW( GetProcessHeap(), 0, szTitle ); } else psInfo->proppage[index].pszText = HEAP_strdupAtoW(GetProcessHeap(), 0, lppsp->pszTitle); } /* * Build the image list for icons */ if ((dwFlags & PSP_USEHICON) || (dwFlags & PSP_USEICONID)) { HICON hIcon; int icon_cx = GetSystemMetrics(SM_CXSMICON); int icon_cy = GetSystemMetrics(SM_CYSMICON); if (dwFlags & PSP_USEICONID) hIcon = LoadImageA(lppsp->hInstance, lppsp->u2.pszIcon, IMAGE_ICON, icon_cx, icon_cy, LR_DEFAULTCOLOR); else hIcon = lppsp->u2.hIcon; if ( hIcon ) { if (psInfo->hImageList == 0 ) psInfo->hImageList = ImageList_Create(icon_cx, icon_cy, ILC_COLOR, 1, 1); ImageList_AddIcon(psInfo->hImageList, hIcon); } } return TRUE; } /****************************************************************************** * PROPSHEET_DoDialogBox * * Copied from windows/dialog.c:DIALOG_DoDialogBox */ static INT PROPSHEET_DoDialogBox( HWND hwnd, HWND owner) { WND * wndPtr; DIALOGINFO * dlgInfo; MSG msg; INT retval; /* Owner must be a top-level window */ owner = WIN_GetTopParent( owner ); if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return -1; dlgInfo = (DIALOGINFO *)wndPtr->wExtra; if (!dlgInfo->flags & DF_END) /* was EndDialog called in WM_INITDIALOG ? */ { EnableWindow( owner, FALSE ); ShowWindow( hwnd, SW_SHOW ); while (GetMessageA(&msg, 0, 0, 0)) { if (!PROPSHEET_IsDialogMessage( hwnd, &msg)) { TranslateMessage( &msg ); DispatchMessageA( &msg ); } if (dlgInfo->flags & DF_END) break; } EnableWindow( owner, TRUE ); } retval = dlgInfo->idResult; WIN_ReleaseWndPtr(wndPtr); DestroyWindow( hwnd ); return retval; } /****************************************************************************** * PROPSHEET_CreateDialog * * Creates the actual property sheet. */ BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo) { LRESULT ret; LPCVOID template; LPVOID temp = 0; HRSRC hRes; DWORD resSize; WORD resID = IDD_PROPSHEET; if (psInfo->ppshheader->dwFlags & PSH_WIZARD) resID = IDD_WIZARD; if(!(hRes = FindResourceA(COMCTL32_hModule, MAKEINTRESOURCEA(resID), RT_DIALOGA))) return FALSE; if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes))) return FALSE; /* * Make a copy of the dialog template. */ resSize = SizeofResource(COMCTL32_hModule, hRes); temp = COMCTL32_Alloc(resSize); if (!temp) return FALSE; memcpy(temp, template, resSize); if (psInfo->useCallback) (*(psInfo->ppshheader->pfnCallback))(0, PSCB_PRECREATE, (LPARAM)temp); ret = CreateDialogIndirectParamA(psInfo->ppshheader->hInstance, (LPDLGTEMPLATEA) temp, psInfo->ppshheader->hwndParent, (DLGPROC) PROPSHEET_DialogProc, (LPARAM)psInfo); if (!(psInfo->ppshheader->dwFlags & PSH_MODELESS)) ret = PROPSHEET_DoDialogBox((HWND)ret, psInfo->ppshheader->hwndParent); COMCTL32_Free(temp); return ret; } /****************************************************************************** * PROPSHEET_SizeMismatch * * Verify that the tab control and the "largest" property sheet page dlg. template * match in size. */ static BOOL PROPSHEET_SizeMismatch(HWND hwndDlg, PropSheetInfo* psInfo) { HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL); RECT rcOrigTab, rcPage; /* * Original tab size. */ GetClientRect(hwndTabCtrl, &rcOrigTab); TRACE("orig tab %d %d %d %d\n", rcOrigTab.left, rcOrigTab.top, rcOrigTab.right, rcOrigTab.bottom); /* * Biggest page size. */ rcPage.left = psInfo->x; rcPage.top = psInfo->y; rcPage.right = psInfo->width; rcPage.bottom = psInfo->height; MapDialogRect(hwndDlg, &rcPage); TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top, rcPage.right, rcPage.bottom); if ( (rcPage.right - rcPage.left) != (rcOrigTab.right - rcOrigTab.left) ) return TRUE; if ( (rcPage.bottom - rcPage.top) != (rcOrigTab.bottom - rcOrigTab.top) ) return TRUE; return FALSE; } /****************************************************************************** * PROPSHEET_IsTooSmallWizard * * Verify that the default property sheet is big enough. */ static BOOL PROPSHEET_IsTooSmallWizard(HWND hwndDlg, PropSheetInfo* psInfo) { RECT rcSheetRect, rcPage, rcLine, rcSheetClient; HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE); PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg); GetClientRect(hwndDlg, &rcSheetClient); GetWindowRect(hwndDlg, &rcSheetRect); GetWindowRect(hwndLine, &rcLine); /* Remove the space below the sunken line */ rcSheetClient.bottom -= (rcSheetRect.bottom - rcLine.top); /* Remove the buffer zone all around the edge */ rcSheetClient.bottom -= (padding.y * 2); rcSheetClient.right -= (padding.x * 2); /* * Biggest page size. */ rcPage.left = psInfo->x; rcPage.top = psInfo->y; rcPage.right = psInfo->width; rcPage.bottom = psInfo->height; MapDialogRect(hwndDlg, &rcPage); TRACE("biggest page %d %d %d %d\n", rcPage.left, rcPage.top, rcPage.right, rcPage.bottom); if (rcPage.right > rcSheetClient.right) return TRUE; if (rcPage.bottom > rcSheetClient.bottom) return TRUE; return FALSE; } /****************************************************************************** * PROPSHEET_AdjustSize * * Resizes the property sheet and the tab control to fit the largest page. */ static BOOL PROPSHEET_AdjustSize(HWND hwndDlg, PropSheetInfo* psInfo) { HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL); HWND hwndButton = GetDlgItem(hwndDlg, IDOK); RECT rc,tabRect; int tabOffsetX, tabOffsetY, buttonHeight; PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg); WND * wndPtr = WIN_FindWndPtr( hwndDlg ); DIALOGINFO * dlgInfo = (DIALOGINFO *)wndPtr->wExtra; /* Get the height of buttons */ GetClientRect(hwndButton, &rc); buttonHeight = rc.bottom; /* * Biggest page size. */ rc.left = psInfo->x; rc.top = psInfo->y; rc.right = psInfo->width; rc.bottom = psInfo->height; MapDialogRect(hwndDlg, &rc); /* * Resize the tab control. */ GetClientRect(hwndTabCtrl,&tabRect); SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect); if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top)) { rc.bottom = rc.top + tabRect.bottom - tabRect.top; psInfo->height = MulDiv((rc.bottom - rc.top),8,dlgInfo->yBaseUnit); } if ((rc.right - rc.left) < (tabRect.right - tabRect.left)) { rc.right = rc.left + tabRect.right - tabRect.left; psInfo->width = MulDiv((rc.right - rc.left),4,dlgInfo->xBaseUnit); } SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, TRUE, (LPARAM)&rc); tabOffsetX = -(rc.left); tabOffsetY = -(rc.top); rc.right -= rc.left; rc.bottom -= rc.top; SetWindowPos(hwndTabCtrl, 0, 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); GetClientRect(hwndTabCtrl, &rc); TRACE("tab client rc %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom); rc.right += ((padding.x * 2) + tabOffsetX); rc.bottom += (buttonHeight + (3 * padding.y) + tabOffsetY); /* * Resize the property sheet. */ SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); WIN_ReleaseWndPtr(wndPtr); return TRUE; } /****************************************************************************** * PROPSHEET_AdjustSizeWizard * * Resizes the property sheet to fit the largest page. */ static BOOL PROPSHEET_AdjustSizeWizard(HWND hwndDlg, PropSheetInfo* psInfo) { HWND hwndButton = GetDlgItem(hwndDlg, IDCANCEL); HWND hwndLine = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE); HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL); RECT rc,tabRect; int buttonHeight, lineHeight; PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndDlg); WND * wndPtr = WIN_FindWndPtr( hwndDlg ); DIALOGINFO * dlgInfo = (DIALOGINFO *)wndPtr->wExtra; /* Get the height of buttons */ GetClientRect(hwndButton, &rc); buttonHeight = rc.bottom; GetClientRect(hwndLine, &rc); lineHeight = rc.bottom; /* * Biggest page size. */ rc.left = psInfo->x; rc.top = psInfo->y; rc.right = psInfo->width; rc.bottom = psInfo->height; MapDialogRect(hwndDlg, &rc); GetClientRect(hwndTabCtrl,&tabRect); if ((rc.bottom - rc.top) < (tabRect.bottom - tabRect.top)) { rc.bottom = rc.top + tabRect.bottom - tabRect.top; psInfo->height = MulDiv((rc.bottom - rc.top), 8, dlgInfo->yBaseUnit); } if ((rc.right - rc.left) < (tabRect.right - tabRect.left)) { rc.right = rc.left + tabRect.right - tabRect.left; psInfo->width = MulDiv((rc.right - rc.left), 4, dlgInfo->xBaseUnit); } TRACE("Biggest page %d %d %d %d\n", rc.left, rc.top, rc.right, rc.bottom); /* Make room */ rc.right += (padding.x * 2); rc.bottom += (buttonHeight + (5 * padding.y) + lineHeight); /* * Resize the property sheet. */ SetWindowPos(hwndDlg, 0, 0, 0, rc.right, rc.bottom, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); WIN_ReleaseWndPtr(wndPtr); return TRUE; } /****************************************************************************** * PROPSHEET_AdjustButtons * * Adjusts the buttons' positions. */ static BOOL PROPSHEET_AdjustButtons(HWND hwndParent, PropSheetInfo* psInfo) { HWND hwndButton = GetDlgItem(hwndParent, IDOK); RECT rcSheet; int x, y; int num_buttons = 2; int buttonWidth, buttonHeight; PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent); if (psInfo->hasApply) num_buttons++; if (psInfo->hasHelp) num_buttons++; /* * Obtain the size of the buttons. */ GetClientRect(hwndButton, &rcSheet); buttonWidth = rcSheet.right; buttonHeight = rcSheet.bottom; /* * Get the size of the property sheet. */ GetClientRect(hwndParent, &rcSheet); /* * All buttons will be at this y coordinate. */ y = rcSheet.bottom - (padding.y + buttonHeight); /* * Position OK button. */ hwndButton = GetDlgItem(hwndParent, IDOK); x = rcSheet.right - ((padding.x + buttonWidth) * num_buttons); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); /* * Position Cancel button. */ hwndButton = GetDlgItem(hwndParent, IDCANCEL); x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1)); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); /* * Position Apply button. */ hwndButton = GetDlgItem(hwndParent, IDC_APPLY_BUTTON); if (psInfo->hasApply) { if (psInfo->hasHelp) x = rcSheet.right - ((padding.x + buttonWidth) * 2); else x = rcSheet.right - (padding.x + buttonWidth); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); EnableWindow(hwndButton, FALSE); } else ShowWindow(hwndButton, SW_HIDE); /* * Position Help button. */ hwndButton = GetDlgItem(hwndParent, IDHELP); if (psInfo->hasHelp) { x = rcSheet.right - (padding.x + buttonWidth); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } else ShowWindow(hwndButton, SW_HIDE); return TRUE; } /****************************************************************************** * PROPSHEET_AdjustButtonsWizard * * Adjusts the buttons' positions. */ static BOOL PROPSHEET_AdjustButtonsWizard(HWND hwndParent, PropSheetInfo* psInfo) { HWND hwndButton = GetDlgItem(hwndParent, IDCANCEL); HWND hwndLine = GetDlgItem(hwndParent, IDC_SUNKEN_LINE); RECT rcSheet; int x, y; int num_buttons = 3; int buttonWidth, buttonHeight, lineHeight, lineWidth; PADDING_INFO padding = PROPSHEET_GetPaddingInfoWizard(hwndParent); if (psInfo->hasHelp) num_buttons++; /* * Obtain the size of the buttons. */ GetClientRect(hwndButton, &rcSheet); buttonWidth = rcSheet.right; buttonHeight = rcSheet.bottom; GetClientRect(hwndLine, &rcSheet); lineHeight = rcSheet.bottom; /* * Get the size of the property sheet. */ GetClientRect(hwndParent, &rcSheet); /* * All buttons will be at this y coordinate. */ y = rcSheet.bottom - (padding.y + buttonHeight); /* * Position the Next and the Finish buttons. */ hwndButton = GetDlgItem(hwndParent, IDC_NEXT_BUTTON); x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 1)); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); hwndButton = GetDlgItem(hwndParent, IDC_FINISH_BUTTON); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); ShowWindow(hwndButton, SW_HIDE); /* * Position the Back button. */ hwndButton = GetDlgItem(hwndParent, IDC_BACK_BUTTON); x -= buttonWidth; SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); /* * Position the Cancel button. */ hwndButton = GetDlgItem(hwndParent, IDCANCEL); x = rcSheet.right - ((padding.x + buttonWidth) * (num_buttons - 2)); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); /* * Position Help button. */ hwndButton = GetDlgItem(hwndParent, IDHELP); if (psInfo->hasHelp) { x = rcSheet.right - (padding.x + buttonWidth); SetWindowPos(hwndButton, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } else ShowWindow(hwndButton, SW_HIDE); /* * Position and resize the sunken line. */ x = padding.x; y = rcSheet.bottom - ((padding.y * 2) + buttonHeight + lineHeight); GetClientRect(hwndParent, &rcSheet); lineWidth = rcSheet.right - (padding.x * 2); SetWindowPos(hwndLine, 0, x, y, lineWidth, 2, SWP_NOZORDER | SWP_NOACTIVATE); return TRUE; } /****************************************************************************** * PROPSHEET_GetPaddingInfo * * Returns the layout information. */ static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg) { HWND hwndTab = GetDlgItem(hwndDlg, IDC_TABCONTROL); RECT rcTab; POINT tl; PADDING_INFO padding; GetWindowRect(hwndTab, &rcTab); tl.x = rcTab.left; tl.y = rcTab.top; ScreenToClient(hwndDlg, &tl); padding.x = tl.x; padding.y = tl.y; return padding; } /****************************************************************************** * PROPSHEET_GetPaddingInfoWizard * * Returns the layout information. * Horizontal spacing is the distance between the Cancel and Help buttons. * Vertical spacing is the distance between the line and the buttons. */ static PADDING_INFO PROPSHEET_GetPaddingInfoWizard(HWND hwndDlg) { PADDING_INFO padding; RECT rc; HWND hwndControl; POINT ptHelp, ptCancel, ptLine; /* Help button */ hwndControl = GetDlgItem(hwndDlg, IDHELP); GetWindowRect(hwndControl, &rc); ptHelp.x = rc.left; ptHelp.y = rc.top; ScreenToClient(hwndDlg, &ptHelp); /* Cancel button */ hwndControl = GetDlgItem(hwndDlg, IDCANCEL); GetWindowRect(hwndControl, &rc); ptCancel.x = rc.right; ptCancel.y = rc.top; ScreenToClient(hwndDlg, &ptCancel); /* Line */ hwndControl = GetDlgItem(hwndDlg, IDC_SUNKEN_LINE); GetWindowRect(hwndControl, &rc); ptLine.x = 0; ptLine.y = rc.bottom; ScreenToClient(hwndDlg, &ptLine); padding.x = ptHelp.x - ptCancel.x; padding.y = ptHelp.y - ptLine.y; return padding; } /****************************************************************************** * PROPSHEET_CreateTabControl * * Insert the tabs in the tab control. */ static BOOL PROPSHEET_CreateTabControl(HWND hwndParent, PropSheetInfo * psInfo) { HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL); TCITEMA item; int i, nTabs; int iImage = 0; char tabtext[MAX_TABTEXT_LENGTH] = "Tab text"; item.mask = TCIF_TEXT; item.pszText = tabtext; item.cchTextMax = MAX_TABTEXT_LENGTH; nTabs = psInfo->nPages; /* * Set the image list for icons. */ if (psInfo->hImageList) { SendMessageA(hwndTabCtrl, TCM_SETIMAGELIST, 0, (LPARAM)psInfo->hImageList); } for (i = 0; i < nTabs; i++) { if ( psInfo->proppage[i].hasIcon ) { item.mask |= TCIF_IMAGE; item.iImage = iImage++; } else { item.mask &= ~TCIF_IMAGE; } WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)psInfo->proppage[i].pszText, -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL); SendMessageA(hwndTabCtrl, TCM_INSERTITEMA, (WPARAM)i, (LPARAM)&item); } return TRUE; } /****************************************************************************** * PROPSHEET_CreatePage * * Creates a page. */ static int PROPSHEET_CreatePage(HWND hwndParent, int index, const PropSheetInfo * psInfo, LPCPROPSHEETPAGEA ppshpage) { DLGTEMPLATE* pTemplate; HWND hwndPage; RECT rc; PropPageInfo* ppInfo = psInfo->proppage; PADDING_INFO padding; UINT pageWidth,pageHeight; TRACE("index %d\n", index); if (ppshpage->dwFlags & PSP_DLGINDIRECT) pTemplate = (DLGTEMPLATE*)ppshpage->u1.pResource; else { HRSRC hResource = FindResourceA(ppshpage->hInstance, ppshpage->u1.pszTemplate, RT_DIALOGA); HGLOBAL hTemplate = LoadResource(ppshpage->hInstance, hResource); pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate); } if (((MyDLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF) { ((MyDLGTEMPLATEEX*)pTemplate)->style |= WS_CHILD | DS_CONTROL; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_DISABLED; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_VISIBLE; } else { pTemplate->style |= WS_CHILD | DS_CONTROL; pTemplate->style &= ~DS_MODALFRAME; pTemplate->style &= ~WS_CAPTION; pTemplate->style &= ~WS_SYSMENU; pTemplate->style &= ~WS_POPUP; pTemplate->style &= ~WS_DISABLED; pTemplate->style &= ~WS_VISIBLE; } if (psInfo->proppage[index].useCallback) (*(ppshpage->pfnCallback))(hwndParent, PSPCB_CREATE, (LPPROPSHEETPAGEA)ppshpage); hwndPage = CreateDialogIndirectParamA(ppshpage->hInstance, pTemplate, hwndParent, ppshpage->pfnDlgProc, (LPARAM)ppshpage); ppInfo[index].hwndPage = hwndPage; rc.left = psInfo->x; rc.top = psInfo->y; rc.right = psInfo->width; rc.bottom = psInfo->height; MapDialogRect(hwndParent, &rc); pageWidth = rc.right - rc.left; pageHeight = rc.bottom - rc.top; if (psInfo->ppshheader->dwFlags & PSH_WIZARD) padding = PROPSHEET_GetPaddingInfoWizard(hwndParent); else { /* * Ask the Tab control to fit this page in. */ HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL); SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&rc); padding = PROPSHEET_GetPaddingInfo(hwndParent); } SetWindowPos(hwndPage, HWND_TOP, rc.left + padding.x, rc.top + padding.y, pageWidth, pageHeight, 0); return TRUE; } /****************************************************************************** * PROPSHEET_ShowPage * * Displays or creates the specified page. */ static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo) { if (index == psInfo->active_page) { if (GetTopWindow(hwndDlg) != psInfo->proppage[index].hwndPage) SetWindowPos(psInfo->proppage[index].hwndPage, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); return TRUE; } if (psInfo->proppage[index].hwndPage == 0) { LPCPROPSHEETPAGEA ppshpage; PSHNOTIFY psn; ppshpage = (LPCPROPSHEETPAGEA)psInfo->proppage[index].hpage; PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage); psn.hdr.hwndFrom = hwndDlg; psn.hdr.code = PSN_SETACTIVE; psn.hdr.idFrom = 0; psn.lParam = 0; /* Send the notification before showing the page. */ SendMessageA(psInfo->proppage[index].hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); /* * TODO: check return value. */ } if (psInfo->active_page != -1) ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE); ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW); if (!(psInfo->ppshheader->dwFlags & PSH_WIZARD)) { HWND hwndTabCtrl; /* Synchronize current selection with tab control */ hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL); SendMessageA(hwndTabCtrl, TCM_SETCURSEL, index, 0); } psInfo->active_page = index; psInfo->activeValid = TRUE; return TRUE; } /****************************************************************************** * PROPSHEET_Back */ static BOOL PROPSHEET_Back(HWND hwndDlg) { BOOL res; PSHNOTIFY psn; HWND hwndPage; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); if (psInfo->active_page < 0) return FALSE; psn.hdr.code = PSN_WIZBACK; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) == -1) return FALSE; if (psInfo->active_page > 0) { res = PROPSHEET_CanSetCurSel(hwndDlg); if(res != FALSE) { res = PROPSHEET_SetCurSel(hwndDlg, psInfo->active_page - 1, 0); } } return TRUE; } /****************************************************************************** * PROPSHEET_Next */ static BOOL PROPSHEET_Next(HWND hwndDlg) { PSHNOTIFY psn; HWND hwndPage; LRESULT msgResult = 0; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); if (psInfo->active_page < 0) return FALSE; psn.hdr.code = PSN_WIZNEXT; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); TRACE("msg result %ld\n", msgResult); if (msgResult == -1) return FALSE; if(PROPSHEET_CanSetCurSel(hwndDlg) != FALSE) { PROPSHEET_SetCurSel(hwndDlg, psInfo->active_page + 1, 0); } return TRUE; } /****************************************************************************** * PROPSHEET_Finish */ static BOOL PROPSHEET_Finish(HWND hwndDlg) { PSHNOTIFY psn; HWND hwndPage; LRESULT msgResult = 0; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); if (psInfo->active_page < 0) return FALSE; psn.hdr.code = PSN_WIZFINISH; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); TRACE("msg result %ld\n", msgResult); if (msgResult != 0) return FALSE; if (psInfo->isModeless) psInfo->activeValid = FALSE; else EndDialog(hwndDlg, TRUE); return TRUE; } /****************************************************************************** * PROPSHEET_Apply */ static BOOL PROPSHEET_Apply(HWND hwndDlg, LPARAM lParam) { int i; HWND hwndPage; PSHNOTIFY psn; LRESULT msgResult; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); if (psInfo->active_page < 0) return FALSE; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; /* * Send PSN_KILLACTIVE to the current page. */ psn.hdr.code = PSN_KILLACTIVE; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn) != FALSE) return FALSE; /* * Send PSN_APPLY to all pages. */ psn.hdr.code = PSN_APPLY; psn.lParam = lParam; for (i = 0; i < psInfo->nPages; i++) { hwndPage = psInfo->proppage[i].hwndPage; if (hwndPage) { msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); if (msgResult == PSNRET_INVALID_NOCHANGEPAGE) return FALSE; } } if(lParam) { psInfo->activeValid = FALSE; } else if(psInfo->active_page >= 0) { psn.hdr.code = PSN_SETACTIVE; psn.lParam = 0; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); } return TRUE; } /****************************************************************************** * PROPSHEET_Cancel */ static void PROPSHEET_Cancel(HWND hwndDlg, LPARAM lParam) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndPage; PSHNOTIFY psn; int i; if (psInfo->active_page < 0) return; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; psn.hdr.code = PSN_QUERYCANCEL; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn)) return; psn.hdr.code = PSN_RESET; psn.lParam = lParam; for (i = 0; i < psInfo->nPages; i++) { hwndPage = psInfo->proppage[i].hwndPage; if (hwndPage) SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); } if (psInfo->isModeless) { /* makes PSM_GETCURRENTPAGEHWND return NULL */ psInfo->activeValid = FALSE; } else EndDialog(hwndDlg, FALSE); } /****************************************************************************** * PROPSHEET_Help */ static void PROPSHEET_Help(HWND hwndDlg) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndPage; PSHNOTIFY psn; if (psInfo->active_page < 0) return; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; psn.hdr.code = PSN_HELP; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); } /****************************************************************************** * PROPSHEET_Changed */ static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage) { int i; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); if (!psInfo) return; /* * Set the dirty flag of this page. */ for (i = 0; i < psInfo->nPages; i++) { if (psInfo->proppage[i].hwndPage == hwndDirtyPage) psInfo->proppage[i].isDirty = TRUE; } /* * Enable the Apply button. */ if (psInfo->hasApply) { HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON); EnableWindow(hwndApplyBtn, TRUE); } } /****************************************************************************** * PROPSHEET_UnChanged */ static void PROPSHEET_UnChanged(HWND hwndDlg, HWND hwndCleanPage) { int i; BOOL noPageDirty = TRUE; HWND hwndApplyBtn = GetDlgItem(hwndDlg, IDC_APPLY_BUTTON); PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); if ( !psInfo ) return; for (i = 0; i < psInfo->nPages; i++) { /* set the specified page as clean */ if (psInfo->proppage[i].hwndPage == hwndCleanPage) psInfo->proppage[i].isDirty = FALSE; /* look to see if there's any dirty pages */ if (psInfo->proppage[i].isDirty) noPageDirty = FALSE; } /* * Disable Apply button. */ if (noPageDirty) EnableWindow(hwndApplyBtn, FALSE); } /****************************************************************************** * PROPSHEET_PressButton */ static void PROPSHEET_PressButton(HWND hwndDlg, int buttonID) { switch (buttonID) { case PSBTN_APPLYNOW: SendMessageA(hwndDlg, WM_COMMAND, IDC_APPLY_BUTTON, 0); break; case PSBTN_BACK: PROPSHEET_Back(hwndDlg); break; case PSBTN_CANCEL: SendMessageA(hwndDlg, WM_COMMAND, IDCANCEL, 0); break; case PSBTN_FINISH: PROPSHEET_Finish(hwndDlg); break; case PSBTN_HELP: SendMessageA(hwndDlg, WM_COMMAND, IDHELP, 0); break; case PSBTN_NEXT: PROPSHEET_Next(hwndDlg); break; case PSBTN_OK: SendMessageA(hwndDlg, WM_COMMAND, IDOK, 0); break; default: FIXME("Invalid button index %d\n", buttonID); } } /************************************************************************* * BOOL PROPSHEET_CanSetCurSel [Internal] * * Test weither the current page can be changed by sending a PSN_KILLACTIVE * * PARAMS * hwndDlg [I] handle to a Dialog hWnd * * RETURNS * TRUE if Current Selection can change * * NOTES */ static BOOL PROPSHEET_CanSetCurSel(HWND hwndDlg) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndPage; PSHNOTIFY psn; if (!psInfo) return FALSE; if (psInfo->active_page < 0) return TRUE; /* * Notify the current page. */ hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; psn.hdr.code = PSN_KILLACTIVE; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; return !SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); } /****************************************************************************** * PROPSHEET_SetCurSel */ static BOOL PROPSHEET_SetCurSel(HWND hwndDlg, int index, HPROPSHEETPAGE hpage) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndPage; HWND hwndHelp = GetDlgItem(hwndDlg, IDHELP); /* hpage takes precedence over index */ if (hpage != NULL) index = PROPSHEET_GetPageIndex(hpage, psInfo); if (index < 0 || index >= psInfo->nPages) { TRACE("Could not find page to select!\n"); return FALSE; } hwndPage = psInfo->proppage[index].hwndPage; /* * Notify the new page if it's already created. * If not it will get created and notified in PROPSHEET_ShowPage. */ if (hwndPage) { int result; PSHNOTIFY psn; psn.hdr.code = PSN_SETACTIVE; psn.hdr.hwndFrom = hwndDlg; psn.hdr.idFrom = 0; psn.lParam = 0; result = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &psn); /* * TODO: check return value. */ } /* * Display the new page. */ PROPSHEET_ShowPage(hwndDlg, index, psInfo); if (psInfo->proppage[index].hasHelp) EnableWindow(hwndHelp, TRUE); else EnableWindow(hwndHelp, FALSE); return TRUE; } /****************************************************************************** * PROPSHEET_SetTitleA */ static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); char szTitle[256]; if (HIWORD(lpszText) == 0) { if (!LoadStringA(psInfo->ppshheader->hInstance, LOWORD(lpszText), szTitle, sizeof(szTitle)-1)) return; lpszText = szTitle; } if (dwStyle & PSH_PROPTITLE) { char* dest; int lentitle = strlen(lpszText); int lenprop = strlen(psInfo->strPropertiesFor); dest = COMCTL32_Alloc(lentitle + lenprop + 1); strcpy(dest, psInfo->strPropertiesFor); strcat(dest, lpszText); SetWindowTextA(hwndDlg, dest); COMCTL32_Free(dest); } else SetWindowTextA(hwndDlg, lpszText); } /****************************************************************************** * PROPSHEET_SetFinishTextA */ static void PROPSHEET_SetFinishTextA(HWND hwndDlg, LPCSTR lpszText) { HWND hwndButton = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON); /* Set text, show and enable the Finish button */ SetWindowTextA(hwndButton, lpszText); ShowWindow(hwndButton, SW_SHOW); EnableWindow(hwndButton, TRUE); /* Make it default pushbutton */ SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0); /* Hide Back button */ hwndButton = GetDlgItem(hwndDlg, IDC_BACK_BUTTON); ShowWindow(hwndButton, SW_HIDE); /* Hide Next button */ hwndButton = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON); ShowWindow(hwndButton, SW_HIDE); } /****************************************************************************** * PROPSHEET_QuerySiblings */ static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg, WPARAM wParam, LPARAM lParam) { int i = 0; HWND hwndPage; LRESULT msgResult = 0; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); while ((i < psInfo->nPages) && (msgResult == 0)) { hwndPage = psInfo->proppage[i].hwndPage; msgResult = SendMessageA(hwndPage, PSM_QUERYSIBLINGS, wParam, lParam); i++; } return msgResult; } /****************************************************************************** * PROPSHEET_AddPage */ static BOOL PROPSHEET_AddPage(HWND hwndDlg, HPROPSHEETPAGE hpage) { PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL); TCITEMA item; char tabtext[MAX_TABTEXT_LENGTH] = "Tab text"; LPCPROPSHEETPAGEA ppsp = (LPCPROPSHEETPAGEA)hpage; /* * Allocate and fill in a new PropPageInfo entry. */ psInfo->proppage = (PropPageInfo*) COMCTL32_ReAlloc(psInfo->proppage, sizeof(PropPageInfo) * (psInfo->nPages + 1)); if (!PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages)) return FALSE; psInfo->proppage[psInfo->nPages].hpage = hpage; if (ppsp->dwFlags & PSP_PREMATURE) { /* Create the page but don't show it */ PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp); } /* * Add a new tab to the tab control. */ item.mask = TCIF_TEXT; item.pszText = tabtext; item.cchTextMax = MAX_TABTEXT_LENGTH; WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)psInfo->proppage[psInfo->nPages].pszText, -1, tabtext, MAX_TABTEXT_LENGTH, NULL, NULL); SendMessageA(hwndTabControl, TCM_INSERTITEMA, psInfo->nPages + 1, (LPARAM)&item); psInfo->nPages++; /* If it is the only page - show it */ if(psInfo->nPages == 1) PROPSHEET_ShowPage(hwndDlg, 0, psInfo); return TRUE; } /****************************************************************************** * PROPSHEET_RemovePage */ static BOOL PROPSHEET_RemovePage(HWND hwndDlg, int index, HPROPSHEETPAGE hpage) { PropSheetInfo * psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndTabControl = GetDlgItem(hwndDlg, IDC_TABCONTROL); PropPageInfo* oldPages; if (!psInfo) { return FALSE; } oldPages = psInfo->proppage; /* * hpage takes precedence over index. */ if (hpage != 0) { index = PROPSHEET_GetPageIndex(hpage, psInfo); } /* Make shure that index is within range */ if (index < 0 || index >= psInfo->nPages) { TRACE("Could not find page to remove!\n"); return FALSE; } TRACE("total pages %d removing page %d active page %d\n", psInfo->nPages, index, psInfo->active_page); /* * Check if we're removing the active page. */ if (index == psInfo->active_page) { if (psInfo->nPages > 1) { if (index > 0) { /* activate previous page */ PROPSHEET_ShowPage(hwndDlg, index - 1, psInfo); } else { /* activate the next page */ PROPSHEET_ShowPage(hwndDlg, index + 1, psInfo); } } else { psInfo->active_page = -1; if (!psInfo->isModeless) { EndDialog(hwndDlg, FALSE); return TRUE; } } } else if (index < psInfo->active_page) psInfo->active_page--; /* Destroy page dialog window */ DestroyWindow(psInfo->proppage[index].hwndPage); /* Free page resources */ if(psInfo->proppage[index].hpage) { PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[index].hpage; if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[index].pszText) HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[index].pszText); DestroyPropertySheetPage(psInfo->proppage[index].hpage); } /* Remove the tab */ SendMessageA(hwndTabControl, TCM_DELETEITEM, index, 0); psInfo->nPages--; psInfo->proppage = COMCTL32_Alloc(sizeof(PropPageInfo) * psInfo->nPages); if (index > 0) memcpy(&psInfo->proppage[0], &oldPages[0], index * sizeof(PropPageInfo)); if (index < psInfo->nPages) memcpy(&psInfo->proppage[index], &oldPages[index + 1], (psInfo->nPages - index) * sizeof(PropPageInfo)); COMCTL32_Free(oldPages); return FALSE; } /****************************************************************************** * PROPSHEET_SetWizButtons * * This code will work if (and assumes that) the Next button is on top of the * Finish button. ie. Finish comes after Next in the Z order. * This means make sure the dialog template reflects this. * */ static void PROPSHEET_SetWizButtons(HWND hwndDlg, DWORD dwFlags) { HWND hwndBack = GetDlgItem(hwndDlg, IDC_BACK_BUTTON); HWND hwndNext = GetDlgItem(hwndDlg, IDC_NEXT_BUTTON); HWND hwndFinish = GetDlgItem(hwndDlg, IDC_FINISH_BUTTON); TRACE("%ld\n", dwFlags); EnableWindow(hwndBack, FALSE); EnableWindow(hwndNext, FALSE); EnableWindow(hwndFinish, FALSE); if (dwFlags & PSWIZB_BACK) EnableWindow(hwndBack, TRUE); if (dwFlags & PSWIZB_NEXT) { /* Hide the Finish button */ ShowWindow(hwndFinish, SW_HIDE); /* Show and enable the Next button */ ShowWindow(hwndNext, SW_SHOW); EnableWindow(hwndNext, TRUE); /* Set the Next button as the default pushbutton */ SendMessageA(hwndDlg, DM_SETDEFID, IDC_NEXT_BUTTON, 0); } if ((dwFlags & PSWIZB_FINISH) || (dwFlags & PSWIZB_DISABLEDFINISH)) { /* Hide the Next button */ ShowWindow(hwndNext, SW_HIDE); /* Show the Finish button */ ShowWindow(hwndFinish, SW_SHOW); if (dwFlags & PSWIZB_FINISH) EnableWindow(hwndFinish, TRUE); /* Set the Finish button as the default pushbutton */ SendMessageA(hwndDlg, DM_SETDEFID, IDC_FINISH_BUTTON, 0); } } /****************************************************************************** * PROPSHEET_GetPageIndex * * Given a HPROPSHEETPAGE, returns the index of the corresponding page from * the array of PropPageInfo. */ static int PROPSHEET_GetPageIndex(HPROPSHEETPAGE hpage, PropSheetInfo* psInfo) { BOOL found = FALSE; int index = 0; while ((index < psInfo->nPages) && (found == FALSE)) { if (psInfo->proppage[index].hpage == hpage) found = TRUE; else index++; } if (found == FALSE) index = -1; return index; } /****************************************************************************** * PROPSHEET_CleanUp */ static void PROPSHEET_CleanUp(HWND hwndDlg) { int i; PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropA(hwndDlg, PropSheetInfoStr); TRACE("\n"); if (HIWORD(psInfo->ppshheader->pszCaption)) HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->ppshheader->pszCaption); COMCTL32_Free((LPVOID)psInfo->ppshheader); for (i = 0; i < psInfo->nPages; i++) { PROPSHEETPAGEA* psp = (PROPSHEETPAGEA*)psInfo->proppage[i].hpage; if(psInfo->proppage[i].hwndPage) DestroyWindow(psInfo->proppage[i].hwndPage); if(psp) { if ((psp->dwFlags & PSP_USETITLE) && psInfo->proppage[i].pszText) HeapFree(GetProcessHeap(), 0, (LPVOID)psInfo->proppage[i].pszText); DestroyPropertySheetPage(psInfo->proppage[i].hpage); } } COMCTL32_Free(psInfo->proppage); COMCTL32_Free(psInfo->strPropertiesFor); ImageList_Destroy(psInfo->hImageList); GlobalFree((HGLOBAL)psInfo); } /****************************************************************************** * PropertySheetA (COMCTL32.84)(COMCTL32.83) */ INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh) { int bRet = 0; PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR, sizeof(PropSheetInfo)); int i, n; BYTE* pByte; PROPSHEET_CollectSheetInfo(lppsh, psInfo); psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) * lppsh->nPages); pByte = (BYTE*) psInfo->ppshheader->u3.ppsp; for (n = i = 0; i < lppsh->nPages; i++, n++) { if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE)) psInfo->proppage[n].hpage = psInfo->ppshheader->u3.phpage[i]; else { psInfo->proppage[n].hpage = CreatePropertySheetPageA((LPCPROPSHEETPAGEA)pByte); pByte += ((LPPROPSHEETPAGEA)pByte)->dwSize; } if (!PROPSHEET_CollectPageInfo((LPCPROPSHEETPAGEA)psInfo->proppage[n].hpage, psInfo, n)) { if (lppsh->dwFlags & PSH_PROPSHEETPAGE) DestroyPropertySheetPage(psInfo->proppage[n].hpage); n--; psInfo->nPages--; } } bRet = PROPSHEET_CreateDialog(psInfo); return bRet; } /****************************************************************************** * PropertySheet32W (COMCTL32.85) */ INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW propertySheetHeader) { FIXME("(%p): stub\n", propertySheetHeader); return -1; } /****************************************************************************** * CreatePropertySheetPageA (COMCTL32.19)(COMCTL32.18) */ HPROPSHEETPAGE WINAPI CreatePropertySheetPageA( LPCPROPSHEETPAGEA lpPropSheetPage) { PROPSHEETPAGEA* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEA)); *ppsp = *lpPropSheetPage; if ( !(ppsp->dwFlags & PSP_DLGINDIRECT) && HIWORD( ppsp->u1.pszTemplate ) ) ppsp->u1.pszTemplate = HEAP_strdupA( GetProcessHeap(), 0, lpPropSheetPage->u1.pszTemplate ); if ( (ppsp->dwFlags & PSP_USEICONID) && HIWORD( ppsp->u2.pszIcon ) ) ppsp->u2.pszIcon = HEAP_strdupA( GetProcessHeap(), 0, lpPropSheetPage->u2.pszIcon ); if ((ppsp->dwFlags & PSP_USETITLE) && HIWORD( ppsp->pszTitle )) ppsp->pszTitle = HEAP_strdupA( GetProcessHeap(), 0, lpPropSheetPage->pszTitle ); else if ( !(ppsp->dwFlags & PSP_USETITLE) ) ppsp->pszTitle = NULL; return (HPROPSHEETPAGE)ppsp; } /****************************************************************************** * CreatePropertySheetPageW (COMCTL32.20) */ HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage) { FIXME("(%p): stub\n", lpPropSheetPage); return 0; } /****************************************************************************** * DestroyPropertySheetPage (COMCTL32.24) */ BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage) { PROPSHEETPAGEA *psp = (PROPSHEETPAGEA *)hPropPage; if (!psp) return FALSE; if ( !(psp->dwFlags & PSP_DLGINDIRECT) && HIWORD( psp->u1.pszTemplate ) ) HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u1.pszTemplate); if ( (psp->dwFlags & PSP_USEICONID) && HIWORD( psp->u2.pszIcon ) ) HeapFree(GetProcessHeap(), 0, (LPVOID)psp->u2.pszIcon); if ((psp->dwFlags & PSP_USETITLE) && HIWORD( psp->pszTitle )) HeapFree(GetProcessHeap(), 0, (LPVOID)psp->pszTitle); COMCTL32_Free(hPropPage); return TRUE; } /****************************************************************************** * PROPSHEET_IsDialogMessage */ static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr); if (!psInfo || (hwnd != lpMsg->hwnd && !IsChild(hwnd, lpMsg->hwnd))) return FALSE; if (lpMsg->message == WM_KEYDOWN && (GetKeyState(VK_CONTROL) & 0x8000)) { int new_page = 0; INT dlgCode = SendMessageA(lpMsg->hwnd, WM_GETDLGCODE, 0, (LPARAM)lpMsg); if (!(dlgCode & DLGC_WANTMESSAGE)) { switch (lpMsg->wParam) { case VK_TAB: if (GetKeyState(VK_SHIFT) & 0x8000) new_page = -1; else new_page = 1; break; case VK_NEXT: new_page = 1; break; case VK_PRIOR: new_page = -1; break; } } if (new_page) { if (PROPSHEET_CanSetCurSel(hwnd) != FALSE) { new_page += psInfo->active_page; if (new_page < 0) new_page = psInfo->nPages - 1; else if (new_page >= psInfo->nPages) new_page = 0; PROPSHEET_SetCurSel(hwnd, new_page, 0); } return TRUE; } } return IsDialogMessageA(hwnd, lpMsg); } /****************************************************************************** * PROPSHEET_DialogProc */ BOOL WINAPI PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { PropSheetInfo* psInfo = (PropSheetInfo*) lParam; char* strCaption = (char*)COMCTL32_Alloc(MAX_CAPTION_LENGTH); HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL); LPCPROPSHEETPAGEA ppshpage; int idx; SetPropA(hwnd, PropSheetInfoStr, (HANDLE)psInfo); /* * Small icon in the title bar. */ if ((psInfo->ppshheader->dwFlags & PSH_USEICONID) || (psInfo->ppshheader->dwFlags & PSH_USEHICON)) { HICON hIcon; int icon_cx = GetSystemMetrics(SM_CXSMICON); int icon_cy = GetSystemMetrics(SM_CYSMICON); if (psInfo->ppshheader->dwFlags & PSH_USEICONID) hIcon = LoadImageA(psInfo->ppshheader->hInstance, psInfo->ppshheader->u1.pszIcon, IMAGE_ICON, icon_cx, icon_cy, LR_DEFAULTCOLOR); else hIcon = psInfo->ppshheader->u1.hIcon; SendMessageA(hwnd, WM_SETICON, 0, hIcon); } if (psInfo->ppshheader->dwFlags & PSH_USEHICON) SendMessageA(hwnd, WM_SETICON, 0, psInfo->ppshheader->u1.hIcon); psInfo->strPropertiesFor = strCaption; GetWindowTextA(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH); PROPSHEET_CreateTabControl(hwnd, psInfo); if (psInfo->ppshheader->dwFlags & PSH_WIZARD) { if (PROPSHEET_IsTooSmallWizard(hwnd, psInfo)) { PROPSHEET_AdjustSizeWizard(hwnd, psInfo); PROPSHEET_AdjustButtonsWizard(hwnd, psInfo); } } else { if (PROPSHEET_SizeMismatch(hwnd, psInfo)) { PROPSHEET_AdjustSize(hwnd, psInfo); PROPSHEET_AdjustButtons(hwnd, psInfo); } } if (psInfo->useCallback) (*(psInfo->ppshheader->pfnCallback))(hwnd, PSCB_INITIALIZED, (LPARAM)0); idx = psInfo->active_page; ppshpage = (LPCPROPSHEETPAGEA)psInfo->proppage[idx].hpage; psInfo->active_page = -1; PROPSHEET_SetCurSel(hwnd, idx, psInfo->proppage[idx].hpage); if (!(psInfo->ppshheader->dwFlags & PSH_WIZARD)) SendMessageA(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0); if (!HIWORD(psInfo->ppshheader->pszCaption) && psInfo->ppshheader->hInstance) { char szText[256]; if (LoadStringA(psInfo->ppshheader->hInstance, (UINT)psInfo->ppshheader->pszCaption, szText, 255)) PROPSHEET_SetTitleA(hwnd, psInfo->ppshheader->dwFlags, szText); } else { PROPSHEET_SetTitleA(hwnd, psInfo->ppshheader->dwFlags, psInfo->ppshheader->pszCaption); } return TRUE; } case WM_DESTROY: PROPSHEET_CleanUp(hwnd); return TRUE; case WM_CLOSE: PROPSHEET_Cancel(hwnd, 1); return TRUE; case WM_COMMAND: { WORD wID = LOWORD(wParam); switch (wID) { case IDOK: case IDC_APPLY_BUTTON: { HWND hwndApplyBtn = GetDlgItem(hwnd, IDC_APPLY_BUTTON); if (PROPSHEET_Apply(hwnd, wID == IDOK ? 1: 0) == FALSE) break; if (wID == IDOK) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr); int result = TRUE; if (psInfo->restartWindows) result = ID_PSRESTARTWINDOWS; /* reboot system takes precedence over restart windows */ if (psInfo->rebootSystem) result = ID_PSREBOOTSYSTEM; if (psInfo->isModeless) psInfo->activeValid = FALSE; else EndDialog(hwnd, result); } else EnableWindow(hwndApplyBtn, FALSE); break; } case IDC_BACK_BUTTON: PROPSHEET_Back(hwnd); break; case IDC_NEXT_BUTTON: PROPSHEET_Next(hwnd); break; case IDC_FINISH_BUTTON: PROPSHEET_Finish(hwnd); break; case IDCANCEL: PROPSHEET_Cancel(hwnd, 0); break; case IDHELP: PROPSHEET_Help(hwnd); break; } return TRUE; } case WM_NOTIFY: { NMHDR* pnmh = (LPNMHDR) lParam; if (pnmh->code == TCN_SELCHANGE) { int index = SendMessageA(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0); PROPSHEET_SetCurSel(hwnd, index, 0); } if(pnmh->code == TCN_SELCHANGING) { BOOL bRet = PROPSHEET_CanSetCurSel(hwnd); SetWindowLongA(hwnd, DWL_MSGRESULT, !bRet); return TRUE; } return 0; } case PSM_GETCURRENTPAGEHWND: { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr); HWND hwndPage = 0; if (psInfo->activeValid && psInfo->active_page != -1) hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; SetWindowLongA(hwnd, DWL_MSGRESULT, hwndPage); return TRUE; } case PSM_CHANGED: PROPSHEET_Changed(hwnd, (HWND)wParam); return TRUE; case PSM_UNCHANGED: PROPSHEET_UnChanged(hwnd, (HWND)wParam); return TRUE; case PSM_GETTABCONTROL: { HWND hwndTabCtrl = GetDlgItem(hwnd, IDC_TABCONTROL); SetWindowLongA(hwnd, DWL_MSGRESULT, hwndTabCtrl); return TRUE; } case PSM_SETCURSEL: { BOOL msgResult; msgResult = PROPSHEET_CanSetCurSel(hwnd); if(msgResult != FALSE) { msgResult = PROPSHEET_SetCurSel(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam); } SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult); return TRUE; } case PSM_CANCELTOCLOSE: { char buf[MAX_BUTTONTEXT_LENGTH]; HWND hwndOK = GetDlgItem(hwnd, IDOK); HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL); EnableWindow(hwndCancel, FALSE); if (LoadStringA(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf))) SetWindowTextA(hwndOK, buf); return FALSE; } case PSM_RESTARTWINDOWS: { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr); psInfo->restartWindows = TRUE; return TRUE; } case PSM_REBOOTSYSTEM: { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr); psInfo->rebootSystem = TRUE; return TRUE; } case PSM_SETTITLEA: PROPSHEET_SetTitleA(hwnd, (DWORD) wParam, (LPCSTR) lParam); return TRUE; case PSM_APPLY: { BOOL msgResult = PROPSHEET_Apply(hwnd, 0); SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult); return TRUE; } case PSM_QUERYSIBLINGS: { LRESULT msgResult = PROPSHEET_QuerySiblings(hwnd, wParam, lParam); SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult); return TRUE; } case PSM_ADDPAGE: { /* * Note: MSVC++ 6.0 documentation says that PSM_ADDPAGE does not have * a return value. This is not true. PSM_ADDPAGE returns TRUE * on success or FALSE otherwise, as specified on MSDN Online. * Also see the MFC code for * CPropertySheet::AddPage(CPropertyPage* pPage). */ BOOL msgResult = PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam); SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult); return TRUE; } case PSM_REMOVEPAGE: PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam); return TRUE; case PSM_ISDIALOGMESSAGE: { BOOL msgResult = PROPSHEET_IsDialogMessage(hwnd, (LPMSG)lParam); SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult); return TRUE; } case PSM_PRESSBUTTON: PROPSHEET_PressButton(hwnd, (int)wParam); return TRUE; case PSM_SETFINISHTEXTA: PROPSHEET_SetFinishTextA(hwnd, (LPCSTR) lParam); return TRUE; case PSM_SETWIZBUTTONS: PROPSHEET_SetWizButtons(hwnd, (DWORD)lParam); return TRUE; case PSM_SETTITLEW: FIXME("Unimplemented msg PSM_SETTITLE32W\n"); return 0; case PSM_SETCURSELID: FIXME("Unimplemented msg PSM_SETCURSELID\n"); return 0; case PSM_SETFINISHTEXTW: FIXME("Unimplemented msg PSM_SETFINISHTEXT32W\n"); return 0; default: return FALSE; } }