/* * Property Sheets * * Copyright 1998 Francis Beaudet * Copyright 1999 Thuy Nguyen * * TODO: * - Modeless mode * - Wizard mode * - Unicode property sheets */ #include #include "winbase.h" #include "commctrl.h" #include "prsht.h" #include "winnls.h" #include "propsheet.h" #include "debug.h" /****************************************************************************** * Data structures */ typedef struct { WORD dlgVer; WORD signature; DWORD helpID; DWORD exStyle; DWORD style; } MyDLGTEMPLATEEX; typedef struct tagPropPageInfo { int index; /* corresponds to the index in ppshheader->ppsp */ HPROPSHEETPAGE hpage; /* to keep track of pages not passed to PropertySheet */ HWND hwndPage; BOOL isDirty; LPCWSTR pszText; BOOL hasHelp; BOOL useCallback; } PropPageInfo; typedef struct tagPropSheetInfo { LPSTR strPropertiesFor; int nPages; int active_page; LPCPROPSHEETHEADERA ppshheader; BOOL isModeless; BOOL hasHelp; BOOL hasApply; BOOL useCallback; BOOL restartWindows; BOOL rebootSystem; PropPageInfo* proppage; int x; int y; int width; int height; } 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 /****************************************************************************** * Prototypes */ static BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo); static BOOL PROPSHEET_IsTooSmall(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, BOOL showPage); static BOOL PROPSHEET_ShowPage(HWND hwndDlg, int index, PropSheetInfo * psInfo); static PADDING_INFO PROPSHEET_GetPaddingInfo(HWND hwndDlg); static BOOL PROPSHEET_Apply(HWND hwndDlg); static void PROPSHEET_Cancel(HWND hwndDlg); 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_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText); static BOOL PROPSHEET_SetCurSel(HWND hwndDlg, int index, HPROPSHEETPAGE hpage); static LRESULT PROPSHEET_QuerySiblings(HWND hwndDlg, WPARAM wParam, LPARAM lParam); static LPCPROPSHEETPAGEA PROPSHEET_GetPSPPage(const PropSheetInfo * psInfo, int index); 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); 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 = lppsh; psInfo->nPages = lppsh->nPages; if (dwFlags & PSH_USEPSTARTPAGE) { TRACE(propsheet, "PSH_USEPSTARTPAGE is on"); psInfo->active_page = 0; } else psInfo->active_page = lppsh->u2.nStartPage; psInfo->restartWindows = FALSE; psInfo->rebootSystem = 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; if (psInfo->ppshheader->dwFlags & PSH_PROPSHEETPAGE) psInfo->proppage[index].hpage = 0; 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; /* 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. */ 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(propsheet, "Tab %d %s\n",index,debugstr_w((LPCWSTR)p)); p += lstrlenW((LPCWSTR)p) + 1; return TRUE; } /****************************************************************************** * PROPSHEET_CreateDialog * * Creates the actual property sheet. */ BOOL PROPSHEET_CreateDialog(PropSheetInfo* psInfo) { LRESULT ret; LPCVOID template; HRSRC hRes; if(!(hRes = FindResourceA(COMCTL32_hModule, MAKEINTRESOURCEA(IDD_PROPSHEET), RT_DIALOGA))) return FALSE; if(!(template = (LPVOID)LoadResource(COMCTL32_hModule, hRes))) return FALSE; if (psInfo->useCallback) (*(psInfo->ppshheader->pfnCallback))(0, PSCB_PRECREATE, (LPARAM)template); if (psInfo->ppshheader->dwFlags & PSH_MODELESS) ret = CreateDialogIndirectParamA(psInfo->ppshheader->hInstance, (LPDLGTEMPLATEA) template, psInfo->ppshheader->hwndParent, (DLGPROC) PROPSHEET_DialogProc, (LPARAM)psInfo); else ret = DialogBoxIndirectParamA(psInfo->ppshheader->hInstance, (LPDLGTEMPLATEA) template, psInfo->ppshheader->hwndParent, (DLGPROC) PROPSHEET_DialogProc, (LPARAM)psInfo); return ret; } /****************************************************************************** * PROPSHEET_IsTooSmall * * Verify that the resource property sheet is big enough. */ static BOOL PROPSHEET_IsTooSmall(HWND hwndDlg, PropSheetInfo* psInfo) { HWND hwndTabCtrl = GetDlgItem(hwndDlg, IDC_TABCONTROL); RECT rcOrigTab, rcPage; /* * Original tab size. */ GetClientRect(hwndTabCtrl, &rcOrigTab); TRACE(propsheet, "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(propsheet, "biggest page %d %d %d %d\n", rcPage.left, rcPage.top, rcPage.right, rcPage.bottom); if (rcPage.right > rcOrigTab.right) return TRUE; if (rcPage.bottom > rcOrigTab.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; int tabOffsetX, tabOffsetY, buttonHeight; PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndDlg); /* 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. */ 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(propsheet, "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); 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_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_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; char tabtext[MAX_TABTEXT_LENGTH] = "Tab text"; item.mask = TCIF_TEXT; item.pszText = tabtext; item.cchTextMax = MAX_TABTEXT_LENGTH; nTabs = psInfo->ppshheader->nPages; for (i = 0; i < nTabs; i++) { 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, BOOL showPage) { DLGTEMPLATE* pTemplate; HWND hwndPage; RECT rc; PropPageInfo* ppInfo = psInfo->proppage; PADDING_INFO padding = PROPSHEET_GetPaddingInfo(hwndParent); HWND hwndTabCtrl = GetDlgItem(hwndParent, IDC_TABCONTROL); TRACE(propsheet, "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; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~DS_MODALFRAME; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_CAPTION; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_SYSMENU; ((MyDLGTEMPLATEEX*)pTemplate)->style &= ~WS_POPUP; } else { pTemplate->style |= WS_CHILD; pTemplate->style &= ~DS_MODALFRAME; pTemplate->style &= ~WS_CAPTION; pTemplate->style &= ~WS_SYSMENU; pTemplate->style &= ~WS_POPUP; } if (psInfo->proppage[index].useCallback) (*(ppshpage->pfnCallback))(hwndParent, PSPCB_CREATE, (LPPROPSHEETPAGEA)ppshpage); hwndPage = CreateDialogIndirectA(ppshpage->hInstance, pTemplate, hwndParent, ppshpage->pfnDlgProc); ppInfo[index].hwndPage = hwndPage; rc.left = psInfo->x; rc.top = psInfo->y; rc.right = psInfo->width; rc.bottom = psInfo->height; MapDialogRect(hwndParent, &rc); /* * Ask the Tab control to fit this page in. */ SendMessageA(hwndTabCtrl, TCM_ADJUSTRECT, FALSE, (LPARAM)&rc); SetWindowPos(hwndPage, HWND_TOP, rc.left + padding.x, rc.top + padding.y, 0, 0, SWP_NOSIZE); if (showPage) ShowWindow(hwndPage, SW_SHOW); else ShowWindow(hwndPage, SW_HIDE); 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) return TRUE; ShowWindow(psInfo->proppage[psInfo->active_page].hwndPage, SW_HIDE); if (psInfo->proppage[index].hwndPage != 0) ShowWindow(psInfo->proppage[index].hwndPage, SW_SHOW); else { LPCPROPSHEETPAGEA ppshpage = PROPSHEET_GetPSPPage(psInfo, index); PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage, TRUE); } psInfo->active_page = index; return TRUE; } /****************************************************************************** * PROPSHEET_Apply */ static BOOL PROPSHEET_Apply(HWND hwndDlg) { int i; NMHDR hdr; HWND hwndPage; LRESULT msgResult; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); hdr.hwndFrom = hwndDlg; /* * Send PSN_KILLACTIVE to the current page. */ hdr.code = PSN_KILLACTIVE; hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr) != FALSE) return FALSE; /* * Send PSN_APPLY to all pages. */ hdr.code = PSN_APPLY; for (i = 0; i < psInfo->nPages; i++) { hwndPage = psInfo->proppage[i].hwndPage; msgResult = SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr); if (msgResult == PSNRET_INVALID_NOCHANGEPAGE) return FALSE; } return TRUE; } /****************************************************************************** * PROPSHEET_Cancel */ static void PROPSHEET_Cancel(HWND hwndDlg) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; NMHDR hdr; hdr.hwndFrom = hwndDlg; hdr.code = PSN_QUERYCANCEL; if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr)) return; hdr.code = PSN_RESET; SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr); if (psInfo->isModeless) psInfo->active_page = -1; /* makes PSM_GETCURRENTPAGEHWND return NULL */ else EndDialog(hwndDlg, FALSE); } /****************************************************************************** * PROPSHEET_Help */ static void PROPSHEET_Help(HWND hwndDlg) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); HWND hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; NMHDR hdr; hdr.hwndFrom = hwndDlg; hdr.code = PSN_HELP; SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr); } /****************************************************************************** * PROPSHEET_Changed */ static void PROPSHEET_Changed(HWND hwndDlg, HWND hwndDirtyPage) { int i; PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); /* * 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); 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: FIXME(propsheet, "Wizard mode not implemented.\n"); break; case PSBTN_CANCEL: SendMessageA(hwndDlg, WM_COMMAND, IDCANCEL, 0); break; case PSBTN_FINISH: FIXME(propsheet, "Wizard mode not implemented.\n"); break; case PSBTN_HELP: SendMessageA(hwndDlg, WM_COMMAND, IDHELP, 0); break; case PSBTN_NEXT: FIXME(propsheet, "Wizard mode not implemented.\n"); break; case PSBTN_OK: SendMessageA(hwndDlg, WM_COMMAND, IDOK, 0); break; default: FIXME(propsheet, "Invalid button index %d\n", buttonID); } } /****************************************************************************** * 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); NMHDR hdr; /* * Notify the current page. */ hwndPage = psInfo->proppage[psInfo->active_page].hwndPage; hdr.hwndFrom = hwndDlg; hdr.code = PSN_KILLACTIVE; if (SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr)) return FALSE; if (hpage != NULL) FIXME(propsheet, "Implement HPROPSHEETPAGE!\n"); else hwndPage = psInfo->proppage[index].hwndPage; /* * Notify the new page. */ hdr.code = PSN_SETACTIVE; SendMessageA(hwndPage, WM_NOTIFY, 0, (LPARAM) &hdr); /* * 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) { if (dwStyle & PSH_PROPTITLE) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwndDlg, PropSheetInfoStr); 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_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_GetPSPPage */ static LPCPROPSHEETPAGEA PROPSHEET_GetPSPPage(const PropSheetInfo * psInfo, int index) { BOOL usePSP = psInfo->ppshheader->dwFlags & PSH_PROPSHEETPAGE; LPCPROPSHEETPAGEA lppsp; int realIndex = psInfo->proppage[index].index; if (usePSP) { BYTE* pByte; lppsp = psInfo->ppshheader->u3.ppsp; pByte = (BYTE*) lppsp; pByte += (lppsp->dwSize * realIndex); lppsp = (LPCPROPSHEETPAGEA)pByte; } else lppsp = (LPCPROPSHEETPAGEA) psInfo->ppshheader->u3.phpage[realIndex]; return lppsp; } /****************************************************************************** * 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)); PROPSHEET_CollectPageInfo(ppsp, psInfo, psInfo->nPages); psInfo->proppage[psInfo->nPages].index = -1; psInfo->proppage[psInfo->nPages].hpage = hpage; /* * Create the page but don't show it. */ PROPSHEET_CreatePage(hwndDlg, psInfo->nPages, psInfo, ppsp, FALSE); /* * 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++; return FALSE; } /****************************************************************************** * 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 = psInfo->proppage; /* * hpage takes precedence over index. */ if (hpage != 0) { index = PROPSHEET_GetPageIndex(hpage, psInfo); if (index == -1) { TRACE(propsheet, "Could not find page to remove!\n"); return FALSE; } } TRACE(propsheet, "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 { TRACE(propsheet, "Removing the only page, close the dialog!\n"); if (psInfo->isModeless) psInfo->active_page = -1; else EndDialog(hwndDlg, FALSE); return TRUE; } } if (index < psInfo->active_page) psInfo->active_page--; /* 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_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) { PropSheetInfo* psInfo = (PropSheetInfo*) RemovePropA(hwndDlg, PropSheetInfoStr); COMCTL32_Free(psInfo->proppage); COMCTL32_Free(psInfo->strPropertiesFor); GlobalFree((HGLOBAL)psInfo); } /****************************************************************************** * PropertySheetA (COMCTL32.84)(COMCTL32.83) */ INT WINAPI PropertySheetA(LPCPROPSHEETHEADERA lppsh) { int bRet = 0; PropSheetInfo* psInfo = (PropSheetInfo*) GlobalAlloc(GPTR, sizeof(PropSheetInfo)); LPCPROPSHEETPAGEA lppsp; int i; PROPSHEET_CollectSheetInfo(lppsh, psInfo); psInfo->proppage = (PropPageInfo*) COMCTL32_Alloc(sizeof(PropPageInfo) * lppsh->nPages); for (i = 0; i < lppsh->nPages; i++) { psInfo->proppage[i].index = i; if (!(lppsh->dwFlags & PSH_PROPSHEETPAGE)) psInfo->proppage[i].hpage = psInfo->ppshheader->u3.phpage[i]; lppsp = PROPSHEET_GetPSPPage(psInfo, i); PROPSHEET_CollectPageInfo(lppsp, psInfo, i); } bRet = PROPSHEET_CreateDialog(psInfo); return bRet; } /****************************************************************************** * PropertySheet32W (COMCTL32.85) */ INT WINAPI PropertySheetW(LPCPROPSHEETHEADERW propertySheetHeader) { FIXME(propsheet, "(%p): stub\n", propertySheetHeader); return -1; } /****************************************************************************** * CreatePropertySheetPageA (COMCTL32.19)(COMCTL32.18) */ HPROPSHEETPAGE WINAPI CreatePropertySheetPageA( LPCPROPSHEETPAGEA lpPropSheetPage) { PROPSHEETPAGEA* ppsp = COMCTL32_Alloc(sizeof(PROPSHEETPAGEA)); *ppsp = *lpPropSheetPage; return (HPROPSHEETPAGE)ppsp; } /****************************************************************************** * CreatePropertySheetPageW (COMCTL32.20) */ HPROPSHEETPAGE WINAPI CreatePropertySheetPageW(LPCPROPSHEETPAGEW lpPropSheetPage) { FIXME(propsheet, "(%p): stub\n", lpPropSheetPage); return 0; } /****************************************************************************** * DestroyPropertySheetPage (COMCTL32.24) */ BOOL WINAPI DestroyPropertySheetPage(HPROPSHEETPAGE hPropPage) { COMCTL32_Free(hPropPage); return TRUE; } /****************************************************************************** * 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; psInfo->strPropertiesFor = strCaption; GetWindowTextA(hwnd, psInfo->strPropertiesFor, MAX_CAPTION_LENGTH); PROPSHEET_CreateTabControl(hwnd, psInfo); if (PROPSHEET_IsTooSmall(hwnd, psInfo)) { PROPSHEET_AdjustSize(hwnd, psInfo); PROPSHEET_AdjustButtons(hwnd, psInfo); } ppshpage = PROPSHEET_GetPSPPage(psInfo, psInfo->active_page); PROPSHEET_CreatePage(hwnd, psInfo->active_page, psInfo, ppshpage, TRUE); SendMessageA(hwndTabCtrl, TCM_SETCURSEL, psInfo->active_page, 0); SetPropA(hwnd, PropSheetInfoStr, (HANDLE)psInfo); 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); 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) == FALSE) break; EnableWindow(hwndApplyBtn, FALSE); 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->active_page = -1; else EndDialog(hwnd, result); } break; } case IDCANCEL: PROPSHEET_Cancel(hwnd); break; case IDHELP: PROPSHEET_Help(hwnd); break; } return TRUE; } case WM_NOTIFY: { NMHDR* pnmh = (LPNMHDR) lParam; if (pnmh->code == TCN_SELCHANGE) { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr); int index = SendMessageA(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0); HWND hwndHelp = GetDlgItem(hwnd, IDHELP); PROPSHEET_ShowPage(hwnd, index, psInfo); if (psInfo->proppage[index].hasHelp) EnableWindow(hwndHelp, TRUE); else EnableWindow(hwndHelp, FALSE); } return 0; } case PSM_GETCURRENTPAGEHWND: { PropSheetInfo* psInfo = (PropSheetInfo*) GetPropA(hwnd, PropSheetInfoStr); HWND hwndPage = 0; if (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_SetCurSel(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam); SetWindowLongA(hwnd, DWL_MSGRESULT, msgResult); return TRUE; } case PSM_CANCELTOCLOSE: { HWND hwndOK = GetDlgItem(hwnd, IDOK); HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL); EnableWindow(hwndCancel, FALSE); SetWindowTextA(hwndOK, "Close"); /* FIXME: hardcoded string */ return TRUE; } 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); 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: PROPSHEET_AddPage(hwnd, (HPROPSHEETPAGE)lParam); return TRUE; case PSM_REMOVEPAGE: PROPSHEET_RemovePage(hwnd, (int)wParam, (HPROPSHEETPAGE)lParam); return TRUE; case PSM_ISDIALOGMESSAGE: { FIXME (propsheet, "Unimplemented msg PSM_ISDIALOGMESSAGE\n"); return 0; } case PSM_PRESSBUTTON: PROPSHEET_PressButton(hwnd, (int)wParam); return TRUE; case PSM_SETTITLEW: FIXME (propsheet, "Unimplemented msg PSM_SETTITLE32W\n"); return 0; case PSM_SETWIZBUTTONS: FIXME (propsheet, "Unimplemented msg PSM_SETWIZBUTTONS\n"); return 0; case PSM_SETCURSELID: FIXME (propsheet, "Unimplemented msg PSM_SETCURSELID\n"); return 0; case PSM_SETFINISHTEXTA: FIXME (propsheet, "Unimplemented msg PSM_SETFINISHTEXT32A\n"); return 0; case PSM_SETFINISHTEXTW: FIXME (propsheet, "Unimplemented msg PSM_SETFINISHTEXT32W\n"); return 0; default: return FALSE; } }