// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include "occimpl.h" #ifdef AFX_CORE4_SEG #pragma code_seg(AFX_CORE4_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW //////////////////////////////////////////////////////////////////////////// // CPropertyPage -- one page of a tabbed dialog UINT CALLBACK AfxPropPageCallback(HWND, UINT message, LPPROPSHEETPAGE pPropPage) { switch (message) { case PSPCB_CREATE: { ASSERT(AfxIsValidAddress(pPropPage, sizeof(AFX_OLDPROPSHEETPAGE))); ASSERT(AfxIsValidAddress(pPropPage, pPropPage->dwSize)); CPropertyPage* pPage = STATIC_DOWNCAST(CPropertyPage, (CObject*)pPropPage->lParam); ASSERT_VALID(pPage); TRY { AfxHookWindowCreate(pPage); } CATCH_ALL(e) { // Note: DELETE_EXCEPTION(e) not necessary return FALSE; } END_CATCH_ALL } return TRUE; case PSPCB_RELEASE: AfxUnhookWindowCreate(); break; } return 0; } BEGIN_MESSAGE_MAP(CPropertyPage, CDialog) //{{AFX_MSG_MAP(CPropertyPage) ON_WM_CTLCOLOR() //}}AFX_MSG_MAP #ifndef _AFX_NO_CTL3D_SUPPORT ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls) #endif END_MESSAGE_MAP() CPropertyPage::CPropertyPage(UINT nIDTemplate, UINT nIDCaption) { ASSERT(nIDTemplate != 0); CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption); } CPropertyPage::CPropertyPage(LPCTSTR lpszTemplateName, UINT nIDCaption) { ASSERT(AfxIsValidString(lpszTemplateName)); CommonConstruct(lpszTemplateName, nIDCaption); } void CPropertyPage::Construct(UINT nIDTemplate, UINT nIDCaption) { ASSERT(nIDTemplate != 0); CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption); } void CPropertyPage::Construct(LPCTSTR lpszTemplateName, UINT nIDCaption) { ASSERT(HIWORD(lpszTemplateName) == 0 || AfxIsValidString(lpszTemplateName)); CommonConstruct(lpszTemplateName, nIDCaption); } CPropertyPage::CPropertyPage() { CommonConstruct(NULL, 0); } void CPropertyPage::CommonConstruct(LPCTSTR lpszTemplateName, UINT nIDCaption) { memset(&m_psp, 0, sizeof(m_psp)); m_psp.dwSize = sizeof(m_psp); m_psp.dwFlags = PSP_USECALLBACK; if (lpszTemplateName != NULL) m_psp.hInstance = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG); m_psp.pszTemplate = lpszTemplateName; m_psp.pfnDlgProc = AfxDlgProc; m_psp.lParam = (LPARAM)this; m_psp.pfnCallback = AfxPropPageCallback; if (nIDCaption != 0) { VERIFY(m_strCaption.LoadString(nIDCaption)); m_psp.pszTitle = m_strCaption; m_psp.dwFlags |= PSP_USETITLE; } if (AfxHelpEnabled()) m_psp.dwFlags |= PSP_HASHELP; if (HIWORD(lpszTemplateName) == 0) m_nIDHelp = LOWORD((DWORD)lpszTemplateName); m_lpszTemplateName = m_psp.pszTemplate; m_bFirstSetActive = TRUE; } CPropertyPage::~CPropertyPage() { #ifndef _AFX_NO_OCC_SUPPORT Cleanup(); #endif if (m_hDialogTemplate != NULL) GlobalFree(m_hDialogTemplate); } #ifndef _AFX_NO_OCC_SUPPORT void CPropertyPage::Cleanup() { COccManager* pOccManager = afxOccManager; if ((pOccManager != NULL) && (m_pOccDialogInfo != NULL)) { pOccManager->PostCreateDialog(m_pOccDialogInfo); free(m_pOccDialogInfo); m_pOccDialogInfo = NULL; } } AFX_STATIC DLGTEMPLATE* AFXAPI _AfxChangePropPageFont(const DLGTEMPLATE* pTemplate, BOOL bWizard) { CString strFaceDefault; WORD wSizeDefault; if (!AfxGetPropSheetFont(strFaceDefault, wSizeDefault, bWizard)) return NULL; // set font of property page to same font used by property sheet CString strFace; WORD wSize; if ((!CDialogTemplate::GetFont(pTemplate, strFace, wSize)) || (strFace != strFaceDefault) || (wSize != wSizeDefault)) { CDialogTemplate dlgTemplate(pTemplate); dlgTemplate.SetFont(strFaceDefault, wSizeDefault); return (DLGTEMPLATE*)dlgTemplate.Detach(); } return NULL; } const DLGTEMPLATE* CPropertyPage::InitDialogInfo(const DLGTEMPLATE* pTemplate) { // cleanup from previous run, if any Cleanup(); m_pOccDialogInfo = (_AFX_OCC_DIALOG_INFO*)malloc( sizeof(_AFX_OCC_DIALOG_INFO)); return afxOccManager->PreCreateDialog(m_pOccDialogInfo, pTemplate); } #endif void CPropertyPage::PreProcessPageTemplate(PROPSHEETPAGE& psp, BOOL bWizard) { const DLGTEMPLATE* pTemplate; if (psp.dwFlags & PSP_DLGINDIRECT) { pTemplate = psp.pResource; } else { HRSRC hResource = ::FindResource(psp.hInstance, psp.pszTemplate, RT_DIALOG); HGLOBAL hTemplate = LoadResource(psp.hInstance, hResource); pTemplate = (LPCDLGTEMPLATE)LockResource(hTemplate); } ASSERT(pTemplate != NULL); #ifdef _DEBUG // WINBUG: Windows currently does not support DIALOGEX resources! // Assert that the template is *not* a DIALOGEX template. // DIALOGEX templates are not supported by the PropertySheet API. // To change a DIALOGEX template back to a DIALOG template, // remove the following: // 1. Extended styles on the dialog // 2. Help IDs on any control in the dialog // 3. Control IDs that are DWORDs // 4. Weight, italic, or charset attributes on the dialog's font if (((DLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF) { // it's a DIALOGEX -- we'd better check DWORD dwVersion = ::GetVersion(); if (dwVersion & 0x80000000) { // it's Win95 -- versions of COMCTL32.DLL that export // a function called DllGetVersion are okay HINSTANCE hInst = LoadLibrary(_T("COMCTL32.DLL")); ASSERT(hInst != NULL); if (hInst != NULL) { FARPROC proc = GetProcAddress(hInst, "DllGetVersion"); if (proc == NULL) ASSERT(FALSE); FreeLibrary(hInst); } } else if (LOBYTE(LOWORD(dwVersion)) == 3) { // it's Windows NT 3.x; we have no hope of this working ASSERT(FALSE); } } #endif // _DEBUG #ifndef _AFX_NO_OCC_SUPPORT // if the dialog could contain OLE controls, deal with them now if (afxOccManager != NULL) pTemplate = InitDialogInfo(pTemplate); #endif // set font of property page to same font used by property sheet HGLOBAL hTemplate = _AfxChangePropPageFont(pTemplate, bWizard); if (m_hDialogTemplate != NULL) { GlobalFree(m_hDialogTemplate); m_hDialogTemplate = NULL; } if (hTemplate != NULL) { pTemplate = (LPCDLGTEMPLATE)hTemplate; m_hDialogTemplate = hTemplate; } psp.pResource = pTemplate; psp.dwFlags |= PSP_DLGINDIRECT; } void CPropertyPage::CancelToClose() { ASSERT(::IsWindow(m_hWnd)); ASSERT(GetParent() != NULL); GetParent()->SendMessage(PSM_CANCELTOCLOSE); } void CPropertyPage::SetModified(BOOL bChanged) { if (m_hWnd == NULL) // allowed for backward compatibility return; ASSERT(::IsWindow(m_hWnd)); ASSERT(GetParent() != NULL); CWnd* pParentWnd = GetParent(); if (bChanged) pParentWnd->SendMessage(PSM_CHANGED, (WPARAM)m_hWnd); else pParentWnd->SendMessage(PSM_UNCHANGED, (WPARAM)m_hWnd); } LRESULT CPropertyPage::QuerySiblings(WPARAM wParam, LPARAM lParam) { ASSERT(::IsWindow(m_hWnd)); ASSERT(GetParent() != NULL); return GetParent()->SendMessage(PSM_QUERYSIBLINGS, wParam, lParam); } BOOL CPropertyPage::OnApply() { ASSERT_VALID(this); OnOK(); return TRUE; } void CPropertyPage::OnReset() { ASSERT_VALID(this); OnCancel(); } void CPropertyPage::OnOK() { ASSERT_VALID(this); } void CPropertyPage::OnCancel() { ASSERT_VALID(this); } BOOL CPropertyPage::OnSetActive() { ASSERT_VALID(this); if (m_bFirstSetActive) m_bFirstSetActive = FALSE; else UpdateData(FALSE); return TRUE; } BOOL CPropertyPage::OnKillActive() { ASSERT_VALID(this); if (!UpdateData()) { TRACE0("UpdateData failed during page deactivation\n"); return FALSE; } return TRUE; } BOOL CPropertyPage::OnQueryCancel() { return TRUE; // ok to cancel } LRESULT CPropertyPage::OnWizardBack() { return 0; } LRESULT CPropertyPage::OnWizardNext() { return 0; } BOOL CPropertyPage::OnWizardFinish() { return TRUE; } LRESULT CPropertyPage::MapWizardResult(LRESULT lToMap) { // -1 and 0 are special if (lToMap == -1 || lToMap == 0) return lToMap; // only do special stuff if MFC owns the property sheet CWnd* pParent = GetParent(); CPropertySheet* pSheet = DYNAMIC_DOWNCAST(CPropertySheet, pParent); if (pSheet != NULL) { // the structures are laid out different for CPropertySheeEx CPropertySheetEx* pSheetEx = DYNAMIC_DOWNCAST(CPropertySheetEx, pParent); const PROPSHEETPAGE* ppsp; if (pSheetEx != NULL) ppsp = pSheetEx->m_psh.ppsp; else ppsp = pSheet->m_psh.ppsp; // search the pages for a matching ID for (int i = 0; i < pSheet->m_pages.GetSize(); i++) { // check page[i] for a match CPropertyPage* pPage = pSheet->GetPage(i); if ((LRESULT)pPage->m_psp.pszTemplate == lToMap) return (LRESULT)ppsp->pResource; // jump to next page (BYTE*&)ppsp += ppsp->dwSize; } } // otherwise, just use the original value return lToMap; } BOOL CPropertyPage::IsButtonEnabled(int iButton) { HWND hWnd = ::GetDlgItem(::GetParent(m_hWnd), iButton); if (hWnd == NULL) return FALSE; return ::IsWindowEnabled(hWnd); } BOOL CPropertyPage::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) { ASSERT(pResult != NULL); NMHDR* pNMHDR = (NMHDR*)lParam; // allow message map to override if (CDialog::OnNotify(wParam, lParam, pResult)) return TRUE; // don't handle messages not from the page/sheet itself if (pNMHDR->hwndFrom != m_hWnd && pNMHDR->hwndFrom != ::GetParent(m_hWnd)) return FALSE; // handle default switch (pNMHDR->code) { case PSN_SETACTIVE: { CPropertySheet* pSheet = DYNAMIC_DOWNCAST(CPropertySheet, GetParent()); if (pSheet != NULL && !(pSheet->m_nFlags & WF_CONTINUEMODAL) && !(pSheet->m_bModeless)) *pResult = -1; else *pResult = OnSetActive() ? 0 : -1; } break; case PSN_KILLACTIVE: *pResult = !OnKillActive(); break; case PSN_APPLY: *pResult = OnApply() ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE; break; case PSN_RESET: OnReset(); break; case PSN_QUERYCANCEL: *pResult = !OnQueryCancel(); break; case PSN_WIZNEXT: //WINBUG: Win32 will send a PSN_WIZBACK even if the button is disabled. if (IsButtonEnabled(ID_WIZNEXT)) *pResult = MapWizardResult(OnWizardNext()); break; case PSN_WIZBACK: //WINBUG: Win32 will send a PSN_WIZBACK even if the button is disabled. if (IsButtonEnabled(ID_WIZBACK)) *pResult = MapWizardResult(OnWizardBack()); break; case PSN_WIZFINISH: *pResult = !OnWizardFinish(); break; case PSN_HELP: SendMessage(WM_COMMAND, ID_HELP); break; default: return FALSE; // not handled } return TRUE; // handled } ///////////////////////////////////////////////////////////////////////////// // CPropertyPage message Handlers BOOL CPropertyPage::PreTranslateMessage(MSG* pMsg) { VERIFY(!CWnd::PreTranslateMessage(pMsg)); return FALSE; // handled by CPropertySheet::PreTranslateMessage } HBRUSH CPropertyPage::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { LRESULT lResult; if (pWnd->SendChildNotifyLastMsg(&lResult)) return (HBRUSH)lResult; if (afxData.bWin4) return CDialog::OnCtlColor(pDC, pWnd, nCtlColor); if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor, afxData.hbrBtnFace, afxData.clrBtnText)) return (HBRUSH)Default(); return afxData.hbrBtnFace; } ///////////////////////////////////////////////////////////////////////////// // CPropertyPage Diagnostics #ifdef _DEBUG void CPropertyPage::AssertValid() const { CDialog::AssertValid(); ASSERT(m_psp.dwSize == sizeof(m_psp)); ASSERT(m_psp.dwFlags & PSP_USECALLBACK); ASSERT(m_psp.pfnDlgProc == AfxDlgProc); ASSERT(m_psp.lParam == (LPARAM)this); } void CPropertyPage::Dump(CDumpContext& dc) const { CDialog::Dump(dc); dc << "m_strCaption = " << m_strCaption << "\n"; dc << "m_psp.dwFlags = " << m_psp.dwFlags << "\n"; } #endif //_DEBUG void CPropertyPage::EndDialog(int nID) { // Normally you shouldn't call EndDialog from a page. But in case it does // happen during error situations, call CPropertySheet::EndDialog instead. CPropertySheet* pParent = DYNAMIC_DOWNCAST(CPropertySheet, GetParent()); if (pParent != NULL) pParent->EndDialog(nID); } ///////////////////////////////////////////////////////////////////////////// // CPropertySheet -- a tabbed "dialog" (really a popup-window) BEGIN_MESSAGE_MAP(CPropertySheet, CWnd) //{{AFX_MSG_MAP(CPropertySheet) ON_WM_CTLCOLOR() ON_WM_NCCREATE() ON_MESSAGE(WM_INITDIALOG, HandleInitDialog) ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp) ON_WM_CLOSE() ON_WM_SYSCOMMAND() ON_MESSAGE(DM_SETDEFID, OnSetDefID) //}}AFX_MSG_MAP END_MESSAGE_MAP() AFX_STATIC_DATA const int _afxPropSheetIDs[4] = { ID_WIZNEXT, ID_WIZFINISH, ID_WIZBACK, IDCANCEL }; AFX_OLDPROPSHEETHEADER* CPropertySheet::GetPropSheetHeader() { CPropertySheetEx* pSheetEx = DYNAMIC_DOWNCAST(CPropertySheetEx, this); if (pSheetEx != NULL) return (AFX_OLDPROPSHEETHEADER*)&pSheetEx->m_psh; else return &m_psh; } LRESULT CPropertySheet::OnSetDefID(WPARAM wParam, LPARAM lParam) { // WINBUG -- A wrong or invalid ID may be passed in here. If this is the // case, then look for a valid one. HWND hWnd; if (IsWizard() && ( ((hWnd = ::GetDlgItem(m_hWnd, wParam)) == NULL) || !(::GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) || !::IsWindowEnabled(hWnd) )) { for (int i = 0; i < 4; i++) { // find first button that is visible and enabled HWND hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetIDs[i]); if ((GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) && ::IsWindowEnabled(hWnd)) { //WINBUG -- focus could be incorrect as well in this case // so ... let's set it to the default button HWND hWndFocus = ::GetFocus(); if (!::IsWindowEnabled(hWndFocus)) ::SetFocus(hWnd); return DefWindowProc(DM_SETDEFID, _afxPropSheetIDs[i], lParam); } } } return Default(); } CPropertySheet::CPropertySheet() { CommonConstruct(NULL, 0); } CPropertySheet::CPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) { ASSERT(nIDCaption != 0); VERIFY(m_strCaption.LoadString(nIDCaption) != 0); CommonConstruct(pParentWnd, iSelectPage); } CPropertySheet::CPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage) { ASSERT(pszCaption != NULL); m_strCaption = pszCaption; CommonConstruct(pParentWnd, iSelectPage); } void CPropertySheet::Construct(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage) { ASSERT(nIDCaption != 0); VERIFY(m_strCaption.LoadString(nIDCaption) != 0); CommonConstruct(pParentWnd, iSelectPage); } void CPropertySheet::Construct(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage) { ASSERT(pszCaption != NULL); m_strCaption = pszCaption; CommonConstruct(pParentWnd, iSelectPage); } void CPropertySheet::CommonConstruct(CWnd* pParentWnd, UINT iSelectPage) { memset(&m_psh, 0, sizeof(m_psh)); m_psh.dwSize = sizeof(m_psh); m_psh.dwFlags = PSH_PROPSHEETPAGE; m_psh.pszCaption = m_strCaption; m_psh.nStartPage = iSelectPage; m_bStacked = TRUE; m_bModeless = FALSE; if (AfxHelpEnabled()) m_psh.dwFlags |= PSH_HASHELP; m_pParentWnd = pParentWnd; // m_psh.hwndParent set in DoModal/create } void CPropertySheet::EnableStackedTabs(BOOL bStacked) { m_bStacked = bStacked; } void CPropertySheet::SetTitle(LPCTSTR lpszText, UINT nStyle) { ASSERT((nStyle & ~PSH_PROPTITLE) == 0); // only PSH_PROPTITLE is valid ASSERT(lpszText == NULL || AfxIsValidString(lpszText)); if (m_hWnd == NULL) { AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader(); // set internal state m_strCaption = lpszText; psh->pszCaption = m_strCaption; psh->dwFlags &= ~PSH_PROPTITLE; psh->dwFlags |= nStyle; } else { // set external state SendMessage(PSM_SETTITLE, nStyle, (LPARAM)lpszText); } } CPropertySheet::~CPropertySheet() { delete[] (PROPSHEETPAGE*)m_psh.ppsp; } BOOL CPropertySheet::PreTranslateMessage(MSG* pMsg) { ASSERT_VALID(this); // allow tooltip messages to be filtered if (CWnd::PreTranslateMessage(pMsg)) return TRUE; // allow sheet to translate Ctrl+Tab, Shift+Ctrl+Tab, // Ctrl+PageUp, and Ctrl+PageDown if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT)) { if (SendMessage(PSM_ISDIALOGMESSAGE, 0, (LPARAM)pMsg)) return TRUE; } // handle rest with IsDialogMessage return PreTranslateInput(pMsg); } BOOL CPropertySheet::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) || !IS_COMMAND_ID(nID) || nID >= 0xf000) { // control notification or non-command button or system command return FALSE; // not routed any further } // if we have an owner window, give it second crack CWnd* pOwner = GetParent(); if (pOwner != NULL) { #ifdef _DEBUG if (afxTraceFlags & traceCmdRouting) TRACE1("Routing command id 0x%04X to owner window.\n", nID); #endif ASSERT(pOwner != this); if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } // last crack goes to the current CWinThread object CWinThread* pThread = AfxGetThread(); if (pThread != NULL) { #ifdef _DEBUG if (afxTraceFlags & traceCmdRouting) TRACE1("Routing command id 0x%04X to app.\n", nID); #endif if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } #ifdef _DEBUG if (afxTraceFlags & traceCmdRouting) { TRACE2("IGNORING command id 0x%04X sent to %hs dialog.\n", nID, GetRuntimeClass()->m_lpszClassName); } #endif return FALSE; } CPropertyPage* CPropertySheet::GetActivePage() const { ASSERT_VALID(this); CPropertyPage* pPage; if (m_hWnd != NULL) pPage = STATIC_DOWNCAST(CPropertyPage, CWnd::FromHandle((HWND)::SendMessage(m_hWnd, PSM_GETCURRENTPAGEHWND, 0, 0))); else pPage = GetPage(GetActiveIndex()); return pPage; } BOOL CPropertySheet::ContinueModal() { // allow CWnd::EndModalLoop to be used if (!CWnd::ContinueModal()) return FALSE; // when active page is NULL, the modal loop should end ASSERT(::IsWindow(m_hWnd)); BOOL bResult = SendMessage(PSM_GETCURRENTPAGEHWND); return bResult; } int CPropertySheet::DoModal() { ASSERT_VALID(this); ASSERT(m_hWnd == NULL); // register common controls VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG)); AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG); // finish building PROPSHEETHEADER structure BuildPropPageArray(); // allow OLE servers to disable themselves CWinApp* pApp = AfxGetApp(); if (pApp != NULL) pApp->EnableModeless(FALSE); // find parent HWND HWND hWndTop; HWND hWndParent = CWnd::GetSafeOwner_(m_pParentWnd->GetSafeHwnd(), &hWndTop); AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader(); psh->hwndParent = hWndParent; BOOL bEnableParent = FALSE; if (hWndParent != NULL && ::IsWindowEnabled(hWndParent)) { ::EnableWindow(hWndParent, FALSE); bEnableParent = TRUE; } HWND hWndCapture = ::GetCapture(); if (hWndCapture != NULL) ::SendMessage(hWndCapture, WM_CANCELMODE, 0, 0); // setup for modal loop and creation m_nModalResult = 0; m_nFlags |= WF_CONTINUEMODAL; // hook for creation of window AfxHookWindowCreate(this); psh->dwFlags |= PSH_MODELESS; m_nFlags |= WF_CONTINUEMODAL; HWND hWnd = (HWND)::PropertySheet((PROPSHEETHEADER*)psh); #ifdef _DEBUG DWORD dwError = ::GetLastError(); #endif psh->dwFlags &= ~PSH_MODELESS; AfxUnhookWindowCreate(); // handle error if (hWnd == NULL || hWnd == (HWND)-1) { TRACE1("PropertySheet() failed: GetLastError returned %d\n", dwError); m_nFlags &= ~WF_CONTINUEMODAL; } int nResult = m_nModalResult; if (ContinueModal()) { // enter modal loop DWORD dwFlags = MLF_SHOWONIDLE; if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG; nResult = RunModalLoop(dwFlags); } // hide the window before enabling parent window, etc. if (m_hWnd != NULL) { SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW| SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); } if (bEnableParent) ::EnableWindow(hWndParent, TRUE); if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd) ::SetActiveWindow(hWndParent); // cleanup DestroyWindow(); // allow OLE servers to enable themselves if (pApp != NULL) pApp->EnableModeless(TRUE); if (hWndTop != NULL) ::EnableWindow(hWndTop, TRUE); return nResult; } int CALLBACK AfxPropSheetCallback(HWND, UINT message, LPARAM lParam) { switch (message) { case PSCB_PRECREATE: { _AFX_THREAD_STATE* pState = AfxGetThreadState(); LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE)lParam; if (lpTemplate->style != pState->m_dwPropStyle || lpTemplate->dwExtendedStyle != pState->m_dwPropExStyle) { // Mark the dialog template as read-write. DWORD dwOldProtect; VirtualProtect(lpTemplate, sizeof(DLGTEMPLATE), PAGE_READWRITE, &dwOldProtect); // Ensure DS_SETFONT is set correctly. lpTemplate->style = lpTemplate->style & DS_SETFONT ? pState->m_dwPropStyle | DS_SETFONT : pState->m_dwPropStyle & ~DS_SETFONT; lpTemplate->dwExtendedStyle = pState->m_dwPropExStyle; return TRUE; } return FALSE; } } return 0; } BOOL CPropertySheet::Create(CWnd* pParentWnd, DWORD dwStyle, DWORD dwExStyle) { _AFX_THREAD_STATE* pState = AfxGetThreadState(); // Calculate the default window style. if (dwStyle == (DWORD)-1) { pState->m_dwPropStyle = DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION; // Wizards don't have WS_SYSMENU. if (!IsWizard()) pState->m_dwPropStyle |= WS_SYSMENU; } else { pState->m_dwPropStyle = dwStyle; } pState->m_dwPropExStyle = dwExStyle; ASSERT_VALID(this); ASSERT(m_hWnd == NULL); VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG)); AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG); // finish building PROPSHEETHEADER structure AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader(); BuildPropPageArray(); m_bModeless = TRUE; psh->dwFlags |= (PSH_MODELESS|PSH_USECALLBACK); psh->pfnCallback = AfxPropSheetCallback; psh->hwndParent = pParentWnd->GetSafeHwnd(); // hook the window creation process AfxHookWindowCreate(this); HWND hWnd = (HWND)PropertySheet((PROPSHEETHEADER*)psh); #ifdef _DEBUG DWORD dwError = ::GetLastError(); #endif // cleanup on failure, otherwise return TRUE if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if Create fails if (hWnd == NULL || hWnd == (HWND)-1) { TRACE1("PropertySheet() failed: GetLastError returned %d\n", dwError); return FALSE; } ASSERT(hWnd == m_hWnd); return TRUE; } void CPropertySheet::BuildPropPageArray() { // delete existing prop page array delete[] (PROPSHEETPAGE*)m_psh.ppsp; m_psh.ppsp = NULL; // build new prop page array AFX_OLDPROPSHEETPAGE* ppsp = new AFX_OLDPROPSHEETPAGE[m_pages.GetSize()]; m_psh.ppsp = (LPPROPSHEETPAGE)ppsp; BOOL bWizard = (m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97)); for (int i = 0; i < m_pages.GetSize(); i++) { CPropertyPage* pPage = GetPage(i); memcpy(&ppsp[i], &pPage->m_psp, sizeof(pPage->m_psp)); pPage->PreProcessPageTemplate((PROPSHEETPAGE&)ppsp[i], bWizard); } m_psh.nPages = m_pages.GetSize(); } //////////////////////////////////////////////////////////////////////////// int CPropertySheet::GetPageCount() const { ASSERT_VALID(this); if (m_hWnd == NULL) return m_pages.GetSize(); CTabCtrl* pTab = GetTabControl(); ASSERT_VALID(pTab); return pTab->GetItemCount(); } int CPropertySheet::GetActiveIndex() const { if (m_hWnd == NULL) return ((CPropertySheet*)this)->GetPropSheetHeader()->nStartPage; CTabCtrl* pTab = GetTabControl(); ASSERT_VALID(pTab); return pTab->GetCurSel(); } BOOL CPropertySheet::SetActivePage(int nPage) { if (m_hWnd == NULL) { GetPropSheetHeader()->nStartPage = nPage; return TRUE; } return (BOOL)SendMessage(PSM_SETCURSEL, nPage); } int CPropertySheet::GetPageIndex(CPropertyPage* pPage) { for (int i = 0; i < GetPageCount(); i++) { if (GetPage(i) == pPage) return i; } return -1; // pPage not found } BOOL CPropertySheet::SetActivePage(CPropertyPage* pPage) { ASSERT_VALID(this); ASSERT(pPage != NULL); ASSERT_KINDOF(CPropertyPage, pPage); int nPage = GetPageIndex(pPage); ASSERT(pPage >= 0); return SetActivePage(nPage); } void CPropertySheet::AddPage(CPropertyPage* pPage) { ASSERT_VALID(this); ASSERT(pPage != NULL); ASSERT_KINDOF(CPropertyPage, pPage); ASSERT_VALID(pPage); // add page to internal list m_pages.Add(pPage); // add page externally if (m_hWnd != NULL) { // build new prop page array AFX_OLDPROPSHEETPAGE *ppsp = new AFX_OLDPROPSHEETPAGE[m_pages.GetSize()]; memcpy(ppsp, m_psh.ppsp, sizeof(AFX_OLDPROPSHEETPAGE) * (m_pages.GetSize()-1)); delete[] (PROPSHEETPAGE*)m_psh.ppsp; m_psh.ppsp = (PROPSHEETPAGE*)ppsp; ppsp += m_pages.GetSize()-1; // copy processed PROPSHEETPAGE struct to end memcpy(ppsp, &pPage->m_psp, sizeof(pPage->m_psp)); pPage->PreProcessPageTemplate((PROPSHEETPAGE&)*ppsp, IsWizard()); HPROPSHEETPAGE hPSP = CreatePropertySheetPage((PROPSHEETPAGE*)ppsp); if (hPSP == NULL) AfxThrowMemoryException(); if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP)) { DestroyPropertySheetPage(hPSP); AfxThrowMemoryException(); } } } void CPropertySheet::RemovePage(CPropertyPage* pPage) { ASSERT_VALID(this); ASSERT(pPage != NULL); ASSERT_KINDOF(CPropertyPage, pPage); int nPage = GetPageIndex(pPage); ASSERT(nPage >= 0); RemovePage(nPage); } void CPropertySheet::RemovePage(int nPage) { ASSERT_VALID(this); // remove the page externally if (m_hWnd != NULL) SendMessage(PSM_REMOVEPAGE, nPage); // remove the page from internal list m_pages.RemoveAt(nPage); } void CPropertySheet::EndDialog(int nEndID) { ASSERT_VALID(this); CWnd::EndModalLoop(nEndID); if (m_bModeless) DestroyWindow(); else PostMessage(PSM_PRESSBUTTON, PSBTN_CANCEL); } void CPropertySheet::OnClose() { m_nModalResult = IDCANCEL; if (m_bModeless) DestroyWindow(); else Default(); } void CPropertySheet::OnSysCommand(UINT nID, LPARAM) { m_nModalResult = IDCANCEL; switch (nID & 0xFFF0) { case SC_CLOSE: if (m_bModeless) { SendMessage(WM_CLOSE); return; } } Default(); } ///////////////////////////////////////////////////////////////////////////// // CPropertySheet message handlers AFX_STATIC_DATA int _afxPropSheetButtons[] = { IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP }; BOOL CPropertySheet::OnInitDialog() { // change tab style if scrolling tabs desired (stacked tabs are default) if (!m_bStacked) { HWND hWndTab = (HWND)::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL); if (hWndTab != NULL) CWnd::ModifyStyle(hWndTab, TCS_MULTILINE, TCS_SINGLELINE, 0); } if (!IsWizard()) { // resize the tab control so the layout is less restrictive HWND hWnd = (HWND)::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL); ASSERT(hWnd != NULL); CRect rectOld; ::GetWindowRect(hWnd, &rectOld); ScreenToClient(rectOld); CRect rectNew(0, 0, 0, 32); ::MapDialogRect(m_hWnd, &rectNew); if (rectNew.bottom < rectOld.bottom) { // move tab control int cyDiff = rectOld.Height() - rectNew.bottom; ::SetWindowPos(hWnd, NULL, 0, 0, rectOld.Width(), rectNew.bottom, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); // move buttons by similar amount for (int i = 0; i < _countof(_afxPropSheetButtons); i++) { hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetButtons[i]); if (hWnd != NULL) { ::GetWindowRect(hWnd, &rectOld); ScreenToClient(&rectOld); ::SetWindowPos(hWnd, NULL, rectOld.left, rectOld.top - cyDiff, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } } // resize property sheet itself similarly GetWindowRect(&rectOld); SetWindowPos(NULL, 0, 0, rectOld.Width(), rectOld.Height() - cyDiff, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } } BOOL bResult = (BOOL)Default(); if (m_bModeless && !IsWizard()) { // layout property sheet so button area is not accounted for CRect rectWnd; GetWindowRect(rectWnd); CRect rectButton; HWND hWnd = ::GetDlgItem(m_hWnd, IDOK); ASSERT(hWnd != NULL); ::GetWindowRect(hWnd, rectButton); SetWindowPos(NULL, 0, 0, rectWnd.Width(), rectButton.top - rectWnd.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); // remove standard buttons for modeless dialogs for (int i = 0; i < _countof(_afxPropSheetButtons); i++) { HWND hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetButtons[i]); if (hWnd != NULL) { ::ShowWindow(hWnd, SW_HIDE); ::EnableWindow(hWnd, FALSE); } } } // center the property sheet relative to the parent window if (!(GetStyle() & WS_CHILD)) CenterWindow(); return bResult; } BOOL CPropertySheet::OnNcCreate(LPCREATESTRUCT) { // By default, MFC does not directly support the new style // help button in the caption, so it is turned off here. // It can be added back in and implemented by derived classes // from CPropertySheet. ModifyStyleEx(WS_EX_CONTEXTHELP, 0); return (BOOL)Default(); } LRESULT CPropertySheet::HandleInitDialog(WPARAM, LPARAM) { LRESULT lResult = OnInitDialog(); return lResult; } BOOL CPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam) { // allow message map override if (CWnd::OnCommand(wParam, lParam)) return TRUE; // crack message parameters UINT nID = LOWORD(wParam); HWND hWndCtrl = (HWND)lParam; int nCode = HIWORD(wParam); // set m_nModalResult to ID of button, whenever button is clicked if (hWndCtrl != NULL && nCode == BN_CLICKED) { if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0) & (DLGC_BUTTON|DLGC_DEFPUSHBUTTON)) { LONG lStyle = ::GetWindowLong(hWndCtrl, GWL_STYLE) & 0x0F; if (lStyle == BS_PUSHBUTTON || lStyle == BS_DEFPUSHBUTTON || lStyle == BS_USERBUTTON || lStyle == BS_OWNERDRAW) { m_nModalResult = nID; } } } return FALSE; } LRESULT CPropertySheet::OnCommandHelp(WPARAM wParam, LPARAM lParam) { ASSERT_VALID(this); CPropertyPage* pPage = GetActivePage(); ASSERT_VALID(pPage); return AfxCallWndProc(pPage, pPage->m_hWnd, WM_COMMANDHELP, wParam, lParam); } HBRUSH CPropertySheet::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { LRESULT lResult; if (pWnd->SendChildNotifyLastMsg(&lResult)) return (HBRUSH)lResult; if (afxData.bWin4) return CWnd::OnCtlColor(pDC, pWnd, nCtlColor); if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor, afxData.hbrBtnFace, afxData.clrBtnText)) return (HBRUSH)Default(); return afxData.hbrBtnFace; } ///////////////////////////////////////////////////////////////////////////// // CPropertySheet Diagnostics #ifdef _DEBUG void CPropertySheet::AssertValid() const { CWnd::AssertValid(); m_pages.AssertValid(); ASSERT(m_psh.dwSize == sizeof(m_psh)); ASSERT((m_psh.dwFlags & PSH_PROPSHEETPAGE) == PSH_PROPSHEETPAGE); } void CPropertySheet::Dump(CDumpContext& dc) const { CWnd::Dump(dc); dc << "m_strCaption = " << m_strCaption << "\n"; dc << "Number of Pages = " << m_pages.GetSize() << "\n"; dc << "Stacked = " << m_bStacked << "\n"; dc << "Modeless = " << m_bModeless << "\n"; } #endif //_DEBUG //////////////////////////////////////////////////////////////////////////// // CPropertyPageEx -- one page of a tabbed dialog, extended for IE4 features CPropertyPageEx::CPropertyPageEx(UINT nIDTemplate, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle) { ASSERT(nIDTemplate != 0); CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle); } CPropertyPageEx::CPropertyPageEx(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle) { ASSERT(AfxIsValidString(lpszTemplateName)); CommonConstruct(lpszTemplateName, nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle); } void CPropertyPageEx::Construct(UINT nIDTemplate, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle) { ASSERT(nIDTemplate != 0); CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle); } void CPropertyPageEx::Construct(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle) { ASSERT(HIWORD(lpszTemplateName) == 0 || AfxIsValidString(lpszTemplateName)); CommonConstruct(lpszTemplateName, nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle); } CPropertyPageEx::CPropertyPageEx() { CommonConstruct(NULL, 0, 0, 0); } void CPropertyPageEx::CommonConstruct(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle) { memset(&m_psp, 0, sizeof(m_psp)); m_psp.dwSize = sizeof(m_psp); m_psp.dwFlags = PSP_USECALLBACK; if (lpszTemplateName != NULL) m_psp.hInstance = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG); m_psp.pszTemplate = lpszTemplateName; m_psp.pfnDlgProc = AfxDlgProc; m_psp.lParam = (LPARAM)this; m_psp.pfnCallback = AfxPropPageCallback; if (nIDCaption != 0) { VERIFY(m_strCaption.LoadString(nIDCaption)); m_psp.pszTitle = m_strCaption; m_psp.dwFlags |= PSP_USETITLE; } if (nIDHeaderTitle != 0) VERIFY(m_strHeaderTitle.LoadString(nIDHeaderTitle)); if (nIDHeaderSubTitle != 0) VERIFY(m_strHeaderSubTitle.LoadString(nIDHeaderSubTitle)); if (AfxHelpEnabled()) m_psp.dwFlags |= PSP_HASHELP; if (HIWORD(lpszTemplateName) == 0) m_nIDHelp = LOWORD((DWORD)lpszTemplateName); m_lpszTemplateName = m_psp.pszTemplate; m_bFirstSetActive = TRUE; } #ifdef _DEBUG void CPropertyPageEx::AssertValid() const { CDialog::AssertValid(); ASSERT(m_psp.dwSize == sizeof(m_psp)); ASSERT(m_psp.dwFlags & PSP_USECALLBACK); ASSERT(m_psp.pfnDlgProc == AfxDlgProc); ASSERT(m_psp.lParam == (LPARAM)this); } void CPropertyPageEx::Dump(CDumpContext& dc) const { CDialog::Dump(dc); dc << "m_strCaption = " << m_strCaption << "\n"; dc << "m_psp.dwFlags = " << m_psp.dwFlags << "\n"; dc << "m_strHeaderTitle = " << m_strHeaderTitle << "\n"; dc << "m_strHeaderSubTitle = " << m_strHeaderSubTitle << "\n"; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CPropertySheetEx -- a tabbed "dialog" (really a popup-window), extended // for IE4 CPropertySheetEx::CPropertySheetEx() { CommonConstruct(NULL, 0, NULL, NULL, NULL); } CPropertySheetEx::CPropertySheetEx(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark, HBITMAP hbmHeader) { ASSERT(nIDCaption != 0); VERIFY(m_strCaption.LoadString(nIDCaption) != 0); CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader); } CPropertySheetEx::CPropertySheetEx(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark, HBITMAP hbmHeader) { ASSERT(pszCaption != NULL); m_strCaption = pszCaption; CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader); } void CPropertySheetEx::Construct(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark, HBITMAP hbmHeader) { ASSERT(nIDCaption != 0); VERIFY(m_strCaption.LoadString(nIDCaption) != 0); CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader); } void CPropertySheetEx::Construct(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark, HBITMAP hbmHeader) { ASSERT(pszCaption != NULL); m_strCaption = pszCaption; CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader); } void CPropertySheetEx::CommonConstruct(CWnd* pParentWnd, UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark, HBITMAP hbmHeader) { memset(&m_psh, 0, sizeof(m_psh)); m_psh.dwSize = sizeof(m_psh); m_psh.dwFlags = PSH_PROPSHEETPAGE; m_psh.pszCaption = m_strCaption; m_psh.nStartPage = iSelectPage; m_bStacked = TRUE; m_bModeless = FALSE; if (AfxHelpEnabled()) m_psh.dwFlags |= PSH_HASHELP; if (hbmWatermark != NULL) { m_psh.hbmWatermark = hbmWatermark; m_psh.dwFlags |= (PSH_USEHBMWATERMARK | PSH_WATERMARK); } if (hpalWatermark != NULL) { m_psh.hplWatermark = hpalWatermark; m_psh.dwFlags |= PSH_USEHPLWATERMARK; } if (hbmHeader != NULL) { m_psh.hbmHeader = hbmHeader; m_psh.dwFlags |= (PSH_USEHBMHEADER | PSH_HEADER); } m_pParentWnd = pParentWnd; // m_psh.hwndParent set in DoModal/create } void CPropertySheetEx::BuildPropPageArray() { // delete existing prop page array delete[] (PROPSHEETPAGE*)m_psh.ppsp; m_psh.ppsp = NULL; // build new prop page array PROPSHEETPAGE* ppsp = new PROPSHEETPAGE[m_pages.GetSize()]; m_psh.ppsp = (LPPROPSHEETPAGE)ppsp; BOOL bWizard = (m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97)); for (int i = 0; i < m_pages.GetSize(); i++) { CPropertyPage* pPage = GetPage(i); memcpy(&ppsp[i], &pPage->m_psp, sizeof(pPage->m_psp)); ppsp[i].dwSize = sizeof(PROPSHEETPAGE); if (pPage->IsKindOf(RUNTIME_CLASS(CPropertyPageEx))) { CPropertyPageEx* pPageEx = (CPropertyPageEx*)pPage; if (!pPageEx->m_strHeaderTitle.IsEmpty()) { ppsp[i].pszHeaderTitle = pPageEx->m_strHeaderTitle; ppsp[i].dwFlags |= PSP_USEHEADERTITLE; } if (!pPageEx->m_strHeaderSubTitle.IsEmpty()) { ppsp[i].pszHeaderSubTitle = pPageEx->m_strHeaderSubTitle; ppsp[i].dwFlags |= PSP_USEHEADERSUBTITLE; } } pPage->PreProcessPageTemplate(ppsp[i], bWizard); } m_psh.nPages = m_pages.GetSize(); } CPropertySheetEx::~CPropertySheetEx() { delete[] (PROPSHEETPAGE*)m_psh.ppsp; } void CPropertySheetEx::AddPage(CPropertyPageEx* pPage) { ASSERT_VALID(this); ASSERT(pPage != NULL); ASSERT_KINDOF(CPropertyPageEx, pPage); ASSERT_VALID(pPage); // add page to internal list m_pages.Add(pPage); // add page externally if (m_hWnd != NULL) { // build new prop page array PROPSHEETPAGE *ppsp = new PROPSHEETPAGE[m_pages.GetSize()]; memcpy(ppsp, m_psh.ppsp, sizeof(PROPSHEETPAGE) * (m_pages.GetSize()-1)); delete[] (PROPSHEETPAGE*)m_psh.ppsp; m_psh.ppsp = ppsp; ppsp += m_pages.GetSize()-1; memcpy(ppsp, &pPage->m_psp, sizeof(pPage->m_psp)); pPage->PreProcessPageTemplate(*ppsp, IsWizard()); if (!pPage->m_strHeaderTitle.IsEmpty()) { ppsp->pszHeaderTitle = pPage->m_strHeaderTitle; ppsp->dwFlags |= PSP_USEHEADERTITLE; } if (!pPage->m_strHeaderSubTitle.IsEmpty()) { ppsp->pszHeaderSubTitle = pPage->m_strHeaderSubTitle; ppsp->dwFlags |= PSP_USEHEADERSUBTITLE; } HPROPSHEETPAGE hPSP = CreatePropertySheetPage(ppsp); if (hPSP == NULL) AfxThrowMemoryException(); if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP)) { DestroyPropertySheetPage(hPSP); AfxThrowMemoryException(); } } } ///////////////////////////////////////////////////////////////////////////// // CPropertySheetEx Diagnostics #ifdef _DEBUG void CPropertySheetEx::AssertValid() const { CWnd::AssertValid(); m_pages.AssertValid(); ASSERT(m_psh.dwSize == sizeof(m_psh)); ASSERT((m_psh.dwFlags & PSH_PROPSHEETPAGE) == PSH_PROPSHEETPAGE); } void CPropertySheetEx::Dump(CDumpContext& dc) const { CWnd::Dump(dc); dc << "m_strCaption = " << m_strCaption << "\n"; dc << "Number of Pages = " << m_pages.GetSize() << "\n"; dc << "Stacked = " << m_bStacked << "\n"; dc << "Modeless = " << m_bModeless << "\n"; } #endif //_DEBUG #ifdef AFX_INIT_SEG #pragma code_seg(AFX_INIT_SEG) #endif IMPLEMENT_DYNAMIC(CPropertyPage, CDialog) IMPLEMENT_DYNAMIC(CPropertySheet, CWnd) IMPLEMENT_DYNAMIC(CPropertyPageEx, CPropertyPage) IMPLEMENT_DYNAMIC(CPropertySheetEx, CPropertySheet) /////////////////////////////////////////////////////////////////////////////