home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / dlgprop.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  41KB  |  1,568 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #include "occimpl.h"
  13.  
  14. #ifdef AFX_CORE4_SEG
  15. #pragma code_seg(AFX_CORE4_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. #define new DEBUG_NEW
  24.  
  25. ////////////////////////////////////////////////////////////////////////////
  26. // CPropertyPage -- one page of a tabbed dialog
  27.  
  28. UINT CALLBACK
  29. AfxPropPageCallback(HWND, UINT message, LPPROPSHEETPAGE pPropPage)
  30. {
  31.     switch (message)
  32.     {
  33.     case PSPCB_CREATE:
  34.         {
  35.             ASSERT(AfxIsValidAddress(pPropPage, sizeof(AFX_OLDPROPSHEETPAGE)));
  36.             ASSERT(AfxIsValidAddress(pPropPage, pPropPage->dwSize));
  37.             CPropertyPage* pPage =
  38.                 STATIC_DOWNCAST(CPropertyPage, (CObject*)pPropPage->lParam);
  39.             ASSERT_VALID(pPage);
  40.             TRY
  41.             {
  42.                 AfxHookWindowCreate(pPage);
  43.             }
  44.             CATCH_ALL(e)
  45.             {
  46.                 // Note: DELETE_EXCEPTION(e) not necessary
  47.                 return FALSE;
  48.             }
  49.             END_CATCH_ALL
  50.         }
  51.         return TRUE;
  52.  
  53.     case PSPCB_RELEASE:
  54.         AfxUnhookWindowCreate();
  55.         break;
  56.     }
  57.  
  58.     return 0;
  59. }
  60.  
  61. BEGIN_MESSAGE_MAP(CPropertyPage, CDialog)
  62.     //{{AFX_MSG_MAP(CPropertyPage)
  63.     ON_WM_CTLCOLOR()
  64.     //}}AFX_MSG_MAP
  65. #ifndef _AFX_NO_CTL3D_SUPPORT
  66.     ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)
  67. #endif
  68. END_MESSAGE_MAP()
  69.  
  70. CPropertyPage::CPropertyPage(UINT nIDTemplate, UINT nIDCaption)
  71. {
  72.     ASSERT(nIDTemplate != 0);
  73.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption);
  74. }
  75.  
  76. CPropertyPage::CPropertyPage(LPCTSTR lpszTemplateName, UINT nIDCaption)
  77. {
  78.     ASSERT(AfxIsValidString(lpszTemplateName));
  79.     CommonConstruct(lpszTemplateName, nIDCaption);
  80. }
  81.  
  82. void CPropertyPage::Construct(UINT nIDTemplate, UINT nIDCaption)
  83. {
  84.     ASSERT(nIDTemplate != 0);
  85.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption);
  86. }
  87.  
  88. void CPropertyPage::Construct(LPCTSTR lpszTemplateName, UINT nIDCaption)
  89. {
  90.     ASSERT(HIWORD(lpszTemplateName) == 0 ||
  91.         AfxIsValidString(lpszTemplateName));
  92.     CommonConstruct(lpszTemplateName, nIDCaption);
  93. }
  94.  
  95. CPropertyPage::CPropertyPage()
  96. {
  97.     CommonConstruct(NULL, 0);
  98. }
  99.  
  100. void CPropertyPage::CommonConstruct(LPCTSTR lpszTemplateName, UINT nIDCaption)
  101. {
  102.     memset(&m_psp, 0, sizeof(m_psp));
  103.     m_psp.dwSize = sizeof(m_psp);
  104.     m_psp.dwFlags = PSP_USECALLBACK;
  105.     if (lpszTemplateName != NULL)
  106.         m_psp.hInstance = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
  107.     m_psp.pszTemplate = lpszTemplateName;
  108.     m_psp.pfnDlgProc = AfxDlgProc;
  109.     m_psp.lParam = (LPARAM)this;
  110.     m_psp.pfnCallback = AfxPropPageCallback;
  111.     if (nIDCaption != 0)
  112.     {
  113.         VERIFY(m_strCaption.LoadString(nIDCaption));
  114.         m_psp.pszTitle = m_strCaption;
  115.         m_psp.dwFlags |= PSP_USETITLE;
  116.     }
  117.     if (AfxHelpEnabled())
  118.         m_psp.dwFlags |= PSP_HASHELP;
  119.     if (HIWORD(lpszTemplateName) == 0)
  120.         m_nIDHelp = LOWORD((DWORD)lpszTemplateName);
  121.     m_lpszTemplateName = m_psp.pszTemplate;
  122.     m_bFirstSetActive = TRUE;
  123. }
  124.  
  125. CPropertyPage::~CPropertyPage()
  126. {
  127. #ifndef _AFX_NO_OCC_SUPPORT
  128.     Cleanup();
  129. #endif
  130.  
  131.     if (m_hDialogTemplate != NULL)
  132.         GlobalFree(m_hDialogTemplate);
  133. }
  134.  
  135. #ifndef _AFX_NO_OCC_SUPPORT
  136.  
  137. void CPropertyPage::Cleanup()
  138. {
  139.     COccManager* pOccManager = afxOccManager;
  140.     if ((pOccManager != NULL) && (m_pOccDialogInfo != NULL))
  141.     {
  142.         pOccManager->PostCreateDialog(m_pOccDialogInfo);
  143.         free(m_pOccDialogInfo);
  144.         m_pOccDialogInfo = NULL;
  145.     }
  146. }
  147.  
  148. AFX_STATIC DLGTEMPLATE* AFXAPI
  149. _AfxChangePropPageFont(const DLGTEMPLATE* pTemplate, BOOL bWizard)
  150. {
  151.     CString strFaceDefault;
  152.     WORD wSizeDefault;
  153.  
  154.     if (!AfxGetPropSheetFont(strFaceDefault, wSizeDefault, bWizard))
  155.         return NULL;
  156.  
  157.     // set font of property page to same font used by property sheet
  158.     CString strFace;
  159.     WORD wSize;
  160.     if ((!CDialogTemplate::GetFont(pTemplate, strFace, wSize)) ||
  161.         (strFace != strFaceDefault) || (wSize != wSizeDefault))
  162.     {
  163.         CDialogTemplate dlgTemplate(pTemplate);
  164.         dlgTemplate.SetFont(strFaceDefault, wSizeDefault);
  165.         return (DLGTEMPLATE*)dlgTemplate.Detach();
  166.     }
  167.  
  168.     return NULL;
  169. }
  170.  
  171. const DLGTEMPLATE* CPropertyPage::InitDialogInfo(const DLGTEMPLATE* pTemplate)
  172. {
  173.     // cleanup from previous run, if any
  174.     Cleanup();
  175.  
  176.     m_pOccDialogInfo = (_AFX_OCC_DIALOG_INFO*)malloc(
  177.         sizeof(_AFX_OCC_DIALOG_INFO));
  178.  
  179.     return afxOccManager->PreCreateDialog(m_pOccDialogInfo, pTemplate);
  180. }
  181.  
  182. #endif
  183.  
  184. void CPropertyPage::PreProcessPageTemplate(PROPSHEETPAGE& psp, BOOL bWizard)
  185. {
  186.     const DLGTEMPLATE* pTemplate;
  187.  
  188.     if (psp.dwFlags & PSP_DLGINDIRECT)
  189.     {
  190.         pTemplate = psp.pResource;
  191.     }
  192.     else
  193.     {
  194.         HRSRC hResource = ::FindResource(psp.hInstance,
  195.             psp.pszTemplate, RT_DIALOG);
  196.         HGLOBAL hTemplate = LoadResource(psp.hInstance,
  197.             hResource);
  198.         pTemplate = (LPCDLGTEMPLATE)LockResource(hTemplate);
  199.     }
  200.  
  201.     ASSERT(pTemplate != NULL);
  202.  
  203. #ifdef _DEBUG
  204.     // WINBUG: Windows currently does not support DIALOGEX resources!
  205.     // Assert that the template is *not* a DIALOGEX template.
  206.     // DIALOGEX templates are not supported by the PropertySheet API.
  207.  
  208.     // To change a DIALOGEX template back to a DIALOG template,
  209.     // remove the following:
  210.     //  1. Extended styles on the dialog
  211.     //  2. Help IDs on any control in the dialog
  212.     //  3. Control IDs that are DWORDs
  213.     //  4. Weight, italic, or charset attributes on the dialog's font
  214.  
  215.     if (((DLGTEMPLATEEX*)pTemplate)->signature == 0xFFFF)
  216.     {
  217.         // it's a DIALOGEX -- we'd better check
  218.         DWORD dwVersion = ::GetVersion();
  219.         if (dwVersion & 0x80000000)
  220.         {
  221.             // it's Win95 -- versions of COMCTL32.DLL that export
  222.             // a function called DllGetVersion are okay
  223.  
  224.             HINSTANCE hInst = LoadLibrary(_T("COMCTL32.DLL"));
  225.             ASSERT(hInst != NULL);
  226.             if (hInst != NULL)
  227.             {
  228.                 FARPROC proc = GetProcAddress(hInst, "DllGetVersion");
  229.                 if (proc == NULL)
  230.                     ASSERT(FALSE);
  231.                 FreeLibrary(hInst);
  232.             }
  233.         }
  234.         else if (LOBYTE(LOWORD(dwVersion)) == 3)
  235.         {
  236.             // it's Windows NT 3.x; we have no hope of this working
  237.             ASSERT(FALSE);
  238.         }
  239.     }
  240. #endif // _DEBUG
  241.  
  242. #ifndef _AFX_NO_OCC_SUPPORT
  243.     // if the dialog could contain OLE controls, deal with them now
  244.     if (afxOccManager != NULL)
  245.         pTemplate = InitDialogInfo(pTemplate);
  246. #endif
  247.  
  248.     // set font of property page to same font used by property sheet
  249.     HGLOBAL hTemplate = _AfxChangePropPageFont(pTemplate, bWizard);
  250.  
  251.     if (m_hDialogTemplate != NULL)
  252.     {
  253.         GlobalFree(m_hDialogTemplate);
  254.         m_hDialogTemplate = NULL;
  255.     }
  256.  
  257.     if (hTemplate != NULL)
  258.     {
  259.         pTemplate = (LPCDLGTEMPLATE)hTemplate;
  260.         m_hDialogTemplate = hTemplate;
  261.     }
  262.     psp.pResource = pTemplate;
  263.     psp.dwFlags |= PSP_DLGINDIRECT;
  264. }
  265.  
  266. void CPropertyPage::CancelToClose()
  267. {
  268.     ASSERT(::IsWindow(m_hWnd));
  269.     ASSERT(GetParent() != NULL);
  270.  
  271.     GetParent()->SendMessage(PSM_CANCELTOCLOSE);
  272. }
  273.  
  274. void CPropertyPage::SetModified(BOOL bChanged)
  275. {
  276.     if (m_hWnd == NULL) // allowed for backward compatibility
  277.         return;
  278.  
  279.     ASSERT(::IsWindow(m_hWnd));
  280.     ASSERT(GetParent() != NULL);
  281.  
  282.     CWnd* pParentWnd = GetParent();
  283.     if (bChanged)
  284.         pParentWnd->SendMessage(PSM_CHANGED, (WPARAM)m_hWnd);
  285.     else
  286.         pParentWnd->SendMessage(PSM_UNCHANGED, (WPARAM)m_hWnd);
  287. }
  288.  
  289. LRESULT CPropertyPage::QuerySiblings(WPARAM wParam, LPARAM lParam)
  290. {
  291.     ASSERT(::IsWindow(m_hWnd));
  292.     ASSERT(GetParent() != NULL);
  293.  
  294.     return GetParent()->SendMessage(PSM_QUERYSIBLINGS, wParam, lParam);
  295. }
  296.  
  297. BOOL CPropertyPage::OnApply()
  298. {
  299.     ASSERT_VALID(this);
  300.  
  301.     OnOK();
  302.     return TRUE;
  303. }
  304.  
  305. void CPropertyPage::OnReset()
  306. {
  307.     ASSERT_VALID(this);
  308.  
  309.     OnCancel();
  310. }
  311.  
  312. void CPropertyPage::OnOK()
  313. {
  314.     ASSERT_VALID(this);
  315. }
  316.  
  317. void CPropertyPage::OnCancel()
  318. {
  319.     ASSERT_VALID(this);
  320. }
  321.  
  322. BOOL CPropertyPage::OnSetActive()
  323. {
  324.     ASSERT_VALID(this);
  325.  
  326.     if (m_bFirstSetActive)
  327.         m_bFirstSetActive = FALSE;
  328.     else
  329.         UpdateData(FALSE);
  330.     return TRUE;
  331. }
  332.  
  333. BOOL CPropertyPage::OnKillActive()
  334. {
  335.     ASSERT_VALID(this);
  336.  
  337.     if (!UpdateData())
  338.     {
  339.         TRACE0("UpdateData failed during page deactivation\n");
  340.         return FALSE;
  341.     }
  342.     return TRUE;
  343. }
  344.  
  345. BOOL CPropertyPage::OnQueryCancel()
  346. {
  347.     return TRUE;    // ok to cancel
  348. }
  349.  
  350. LRESULT CPropertyPage::OnWizardBack()
  351. {
  352.     return 0;
  353. }
  354.  
  355. LRESULT CPropertyPage::OnWizardNext()
  356. {
  357.     return 0;
  358. }
  359.  
  360. BOOL CPropertyPage::OnWizardFinish()
  361. {
  362.     return TRUE;
  363. }
  364.  
  365. LRESULT CPropertyPage::MapWizardResult(LRESULT lToMap)
  366. {
  367.     // -1 and 0 are special
  368.     if (lToMap == -1 || lToMap == 0)
  369.         return lToMap;
  370.  
  371.     // only do special stuff if MFC owns the property sheet
  372.     CWnd* pParent = GetParent();
  373.     CPropertySheet* pSheet = DYNAMIC_DOWNCAST(CPropertySheet, pParent);
  374.     if (pSheet != NULL)
  375.     {
  376.         // the structures are laid out different for CPropertySheeEx
  377.         CPropertySheetEx* pSheetEx = DYNAMIC_DOWNCAST(CPropertySheetEx, pParent);
  378.         const PROPSHEETPAGE* ppsp;
  379.         if (pSheetEx != NULL)
  380.             ppsp = pSheetEx->m_psh.ppsp;
  381.         else
  382.             ppsp = pSheet->m_psh.ppsp;
  383.  
  384.         // search the pages for a matching ID
  385.         for (int i = 0; i < pSheet->m_pages.GetSize(); i++)
  386.         {
  387.             // check page[i] for a match
  388.             CPropertyPage* pPage = pSheet->GetPage(i);
  389.             if ((LRESULT)pPage->m_psp.pszTemplate == lToMap)
  390.                 return (LRESULT)ppsp->pResource;
  391.  
  392.             // jump to next page
  393.             (BYTE*&)ppsp += ppsp->dwSize;
  394.         }
  395.     }
  396.     // otherwise, just use the original value
  397.     return lToMap;
  398. }
  399.  
  400. BOOL CPropertyPage::IsButtonEnabled(int iButton)
  401. {
  402.     HWND hWnd = ::GetDlgItem(::GetParent(m_hWnd), iButton);
  403.     if (hWnd == NULL)
  404.         return FALSE;
  405.     return ::IsWindowEnabled(hWnd);
  406. }
  407.  
  408. BOOL CPropertyPage::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  409. {
  410.     ASSERT(pResult != NULL);
  411.     NMHDR* pNMHDR = (NMHDR*)lParam;
  412.  
  413.     // allow message map to override
  414.     if (CDialog::OnNotify(wParam, lParam, pResult))
  415.         return TRUE;
  416.  
  417.     // don't handle messages not from the page/sheet itself
  418.     if (pNMHDR->hwndFrom != m_hWnd && pNMHDR->hwndFrom != ::GetParent(m_hWnd))
  419.         return FALSE;
  420.  
  421.     // handle default
  422.     switch (pNMHDR->code)
  423.     {
  424.     case PSN_SETACTIVE:
  425.         {
  426.             CPropertySheet* pSheet = DYNAMIC_DOWNCAST(CPropertySheet, GetParent());
  427.             if (pSheet != NULL && !(pSheet->m_nFlags & WF_CONTINUEMODAL) && !(pSheet->m_bModeless))
  428.                 *pResult = -1;
  429.             else
  430.                 *pResult = OnSetActive() ? 0 : -1;
  431.         }
  432.         break;
  433.     case PSN_KILLACTIVE:
  434.         *pResult = !OnKillActive();
  435.         break;
  436.     case PSN_APPLY:
  437.         *pResult = OnApply() ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE;
  438.         break;
  439.     case PSN_RESET:
  440.         OnReset();
  441.         break;
  442.     case PSN_QUERYCANCEL:
  443.         *pResult = !OnQueryCancel();
  444.         break;
  445.     case PSN_WIZNEXT:
  446.         //WINBUG: Win32 will send a PSN_WIZBACK even if the button is disabled.
  447.         if (IsButtonEnabled(ID_WIZNEXT))
  448.             *pResult = MapWizardResult(OnWizardNext());
  449.         break;
  450.     case PSN_WIZBACK:
  451.         //WINBUG: Win32 will send a PSN_WIZBACK even if the button is disabled.
  452.         if (IsButtonEnabled(ID_WIZBACK))
  453.             *pResult = MapWizardResult(OnWizardBack());
  454.         break;
  455.     case PSN_WIZFINISH:
  456.         *pResult = !OnWizardFinish();
  457.         break;
  458.     case PSN_HELP:
  459.         SendMessage(WM_COMMAND, ID_HELP);
  460.         break;
  461.  
  462.     default:
  463.         return FALSE;   // not handled
  464.     }
  465.  
  466.     return TRUE;    // handled
  467. }
  468.  
  469. /////////////////////////////////////////////////////////////////////////////
  470. // CPropertyPage message Handlers
  471.  
  472. BOOL CPropertyPage::PreTranslateMessage(MSG* pMsg)
  473. {
  474.     VERIFY(!CWnd::PreTranslateMessage(pMsg));
  475.  
  476.     return FALSE;   // handled by CPropertySheet::PreTranslateMessage
  477. }
  478.  
  479. HBRUSH CPropertyPage::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  480. {
  481.     LRESULT lResult;
  482.     if (pWnd->SendChildNotifyLastMsg(&lResult))
  483.         return (HBRUSH)lResult;
  484.  
  485.     if (afxData.bWin4)
  486.         return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
  487.  
  488.     if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
  489.       afxData.hbrBtnFace, afxData.clrBtnText))
  490.         return (HBRUSH)Default();
  491.     return afxData.hbrBtnFace;
  492. }
  493.  
  494. /////////////////////////////////////////////////////////////////////////////
  495. // CPropertyPage Diagnostics
  496.  
  497. #ifdef _DEBUG
  498. void CPropertyPage::AssertValid() const
  499. {
  500.     CDialog::AssertValid();
  501.     ASSERT(m_psp.dwSize == sizeof(m_psp));
  502.     ASSERT(m_psp.dwFlags & PSP_USECALLBACK);
  503.     ASSERT(m_psp.pfnDlgProc == AfxDlgProc);
  504.     ASSERT(m_psp.lParam == (LPARAM)this);
  505. }
  506.  
  507. void CPropertyPage::Dump(CDumpContext& dc) const
  508. {
  509.     CDialog::Dump(dc);
  510.  
  511.     dc << "m_strCaption = " << m_strCaption << "\n";
  512.     dc << "m_psp.dwFlags = " << m_psp.dwFlags << "\n";
  513. }
  514. #endif //_DEBUG
  515.  
  516. void CPropertyPage::EndDialog(int nID)
  517. {
  518.     // Normally you shouldn't call EndDialog from a page. But in case it does
  519.     // happen during error situations, call CPropertySheet::EndDialog instead.
  520.  
  521.     CPropertySheet* pParent = DYNAMIC_DOWNCAST(CPropertySheet, GetParent());
  522.     if (pParent != NULL)
  523.         pParent->EndDialog(nID);
  524. }
  525.  
  526. /////////////////////////////////////////////////////////////////////////////
  527. // CPropertySheet -- a tabbed "dialog" (really a popup-window)
  528.  
  529. BEGIN_MESSAGE_MAP(CPropertySheet, CWnd)
  530.     //{{AFX_MSG_MAP(CPropertySheet)
  531.     ON_WM_CTLCOLOR()
  532.     ON_WM_NCCREATE()
  533.     ON_MESSAGE(WM_INITDIALOG, HandleInitDialog)
  534.     ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp)
  535.     ON_WM_CLOSE()
  536.     ON_WM_SYSCOMMAND()
  537.     ON_MESSAGE(DM_SETDEFID, OnSetDefID)
  538.     //}}AFX_MSG_MAP
  539. END_MESSAGE_MAP()
  540.  
  541. AFX_STATIC_DATA const int _afxPropSheetIDs[4] = { ID_WIZNEXT, ID_WIZFINISH, ID_WIZBACK, IDCANCEL };
  542.  
  543. AFX_OLDPROPSHEETHEADER* CPropertySheet::GetPropSheetHeader()
  544. {
  545.     CPropertySheetEx* pSheetEx = DYNAMIC_DOWNCAST(CPropertySheetEx, this);
  546.     if (pSheetEx != NULL)
  547.         return (AFX_OLDPROPSHEETHEADER*)&pSheetEx->m_psh;
  548.     else
  549.         return &m_psh;
  550. }
  551.  
  552. LRESULT CPropertySheet::OnSetDefID(WPARAM wParam, LPARAM lParam)
  553. {
  554.     // WINBUG -- A wrong or invalid ID may be passed in here.  If this is the
  555.     // case, then look for a valid one.
  556.     HWND hWnd;
  557.     if (IsWizard() &&
  558.         (
  559.             ((hWnd = ::GetDlgItem(m_hWnd, wParam)) == NULL) ||
  560.             !(::GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) ||
  561.             !::IsWindowEnabled(hWnd)
  562.         ))
  563.     {
  564.  
  565.         for (int i = 0; i < 4; i++)
  566.         {
  567.             // find first button that is visible and  enabled
  568.             HWND hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetIDs[i]);
  569.             if ((GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) &&
  570.                 ::IsWindowEnabled(hWnd))
  571.             {
  572.                 //WINBUG -- focus could be incorrect as well in this case
  573.                 // so ... let's set it to the default button
  574.                 HWND hWndFocus = ::GetFocus();
  575.                 if (!::IsWindowEnabled(hWndFocus))
  576.                     ::SetFocus(hWnd);
  577.                 return DefWindowProc(DM_SETDEFID, _afxPropSheetIDs[i], lParam);
  578.             }
  579.         }
  580.     }
  581.     return Default();
  582. }
  583.  
  584. CPropertySheet::CPropertySheet()
  585. {
  586.     CommonConstruct(NULL, 0);
  587. }
  588.  
  589. CPropertySheet::CPropertySheet(UINT nIDCaption, CWnd* pParentWnd,
  590.     UINT iSelectPage)
  591. {
  592.     ASSERT(nIDCaption != 0);
  593.  
  594.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  595.     CommonConstruct(pParentWnd, iSelectPage);
  596. }
  597.  
  598. CPropertySheet::CPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd,
  599.     UINT iSelectPage)
  600. {
  601.     ASSERT(pszCaption != NULL);
  602.  
  603.     m_strCaption = pszCaption;
  604.     CommonConstruct(pParentWnd, iSelectPage);
  605. }
  606.  
  607. void CPropertySheet::Construct(UINT nIDCaption, CWnd* pParentWnd,
  608.     UINT iSelectPage)
  609. {
  610.     ASSERT(nIDCaption != 0);
  611.  
  612.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  613.     CommonConstruct(pParentWnd, iSelectPage);
  614. }
  615.  
  616. void CPropertySheet::Construct(LPCTSTR pszCaption, CWnd* pParentWnd,
  617.     UINT iSelectPage)
  618. {
  619.     ASSERT(pszCaption != NULL);
  620.  
  621.     m_strCaption = pszCaption;
  622.     CommonConstruct(pParentWnd, iSelectPage);
  623. }
  624.  
  625. void CPropertySheet::CommonConstruct(CWnd* pParentWnd, UINT iSelectPage)
  626. {
  627.     memset(&m_psh, 0, sizeof(m_psh));
  628.     m_psh.dwSize = sizeof(m_psh);
  629.     m_psh.dwFlags = PSH_PROPSHEETPAGE;
  630.     m_psh.pszCaption = m_strCaption;
  631.     m_psh.nStartPage = iSelectPage;
  632.     m_bStacked = TRUE;
  633.     m_bModeless = FALSE;
  634.  
  635.     if (AfxHelpEnabled())
  636.         m_psh.dwFlags |= PSH_HASHELP;
  637.  
  638.     m_pParentWnd = pParentWnd;  // m_psh.hwndParent set in DoModal/create
  639. }
  640.  
  641. void CPropertySheet::EnableStackedTabs(BOOL bStacked)
  642. {
  643.     m_bStacked = bStacked;
  644. }
  645.  
  646. void CPropertySheet::SetTitle(LPCTSTR lpszText, UINT nStyle)
  647. {
  648.     ASSERT((nStyle & ~PSH_PROPTITLE) == 0); // only PSH_PROPTITLE is valid
  649.     ASSERT(lpszText == NULL || AfxIsValidString(lpszText));
  650.  
  651.     if (m_hWnd == NULL)
  652.     {
  653.         AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
  654.         // set internal state
  655.         m_strCaption = lpszText;
  656.         psh->pszCaption = m_strCaption;
  657.         psh->dwFlags &= ~PSH_PROPTITLE;
  658.         psh->dwFlags |= nStyle;
  659.     }
  660.     else
  661.     {
  662.         // set external state
  663.         SendMessage(PSM_SETTITLE, nStyle, (LPARAM)lpszText);
  664.     }
  665. }
  666.  
  667. CPropertySheet::~CPropertySheet()
  668. {
  669.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  670. }
  671.  
  672. BOOL CPropertySheet::PreTranslateMessage(MSG* pMsg)
  673. {
  674.     ASSERT_VALID(this);
  675.  
  676.     // allow tooltip messages to be filtered
  677.     if (CWnd::PreTranslateMessage(pMsg))
  678.         return TRUE;
  679.  
  680.     // allow sheet to translate Ctrl+Tab, Shift+Ctrl+Tab,
  681.     //  Ctrl+PageUp, and Ctrl+PageDown
  682.     if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 &&
  683.         (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT))
  684.     {
  685.         if (SendMessage(PSM_ISDIALOGMESSAGE, 0, (LPARAM)pMsg))
  686.             return TRUE;
  687.     }
  688.  
  689.     // handle rest with IsDialogMessage
  690.     return PreTranslateInput(pMsg);
  691. }
  692.  
  693. BOOL CPropertySheet::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  694.     AFX_CMDHANDLERINFO* pHandlerInfo)
  695. {
  696.     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  697.         return TRUE;
  698.  
  699.     if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) ||
  700.             !IS_COMMAND_ID(nID) || nID >= 0xf000)
  701.     {
  702.         // control notification or non-command button or system command
  703.         return FALSE;       // not routed any further
  704.     }
  705.  
  706.     // if we have an owner window, give it second crack
  707.     CWnd* pOwner = GetParent();
  708.     if (pOwner != NULL)
  709.     {
  710. #ifdef _DEBUG
  711.         if (afxTraceFlags & traceCmdRouting)
  712.             TRACE1("Routing command id 0x%04X to owner window.\n", nID);
  713. #endif
  714.         ASSERT(pOwner != this);
  715.         if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  716.             return TRUE;
  717.     }
  718.  
  719.     // last crack goes to the current CWinThread object
  720.     CWinThread* pThread = AfxGetThread();
  721.     if (pThread != NULL)
  722.     {
  723. #ifdef _DEBUG
  724.         if (afxTraceFlags & traceCmdRouting)
  725.             TRACE1("Routing command id 0x%04X to app.\n", nID);
  726. #endif
  727.         if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  728.             return TRUE;
  729.     }
  730.  
  731. #ifdef _DEBUG
  732.     if (afxTraceFlags & traceCmdRouting)
  733.     {
  734.         TRACE2("IGNORING command id 0x%04X sent to %hs dialog.\n", nID,
  735.                 GetRuntimeClass()->m_lpszClassName);
  736.     }
  737. #endif
  738.     return FALSE;
  739. }
  740.  
  741. CPropertyPage* CPropertySheet::GetActivePage() const
  742. {
  743.     ASSERT_VALID(this);
  744.  
  745.     CPropertyPage* pPage;
  746.     if (m_hWnd != NULL)
  747.         pPage = STATIC_DOWNCAST(CPropertyPage,
  748.             CWnd::FromHandle((HWND)::SendMessage(m_hWnd, PSM_GETCURRENTPAGEHWND, 0, 0)));
  749.     else
  750.         pPage = GetPage(GetActiveIndex());
  751.     return pPage;
  752. }
  753.  
  754. BOOL CPropertySheet::ContinueModal()
  755. {
  756.     // allow CWnd::EndModalLoop to be used
  757.     if (!CWnd::ContinueModal())
  758.         return FALSE;
  759.  
  760.     // when active page is NULL, the modal loop should end
  761.     ASSERT(::IsWindow(m_hWnd));
  762.     BOOL bResult = SendMessage(PSM_GETCURRENTPAGEHWND);
  763.     return bResult;
  764. }
  765.  
  766. int CPropertySheet::DoModal()
  767. {
  768.     ASSERT_VALID(this);
  769.     ASSERT(m_hWnd == NULL);
  770.  
  771.     // register common controls
  772.     VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
  773.     AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);
  774.  
  775.     // finish building PROPSHEETHEADER structure
  776.     BuildPropPageArray();
  777.  
  778.     // allow OLE servers to disable themselves
  779.     CWinApp* pApp = AfxGetApp();
  780.     if (pApp != NULL)
  781.         pApp->EnableModeless(FALSE);
  782.  
  783.     // find parent HWND
  784.     HWND hWndTop;
  785.     HWND hWndParent = CWnd::GetSafeOwner_(m_pParentWnd->GetSafeHwnd(), &hWndTop);
  786.     AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
  787.     psh->hwndParent = hWndParent;
  788.     BOOL bEnableParent = FALSE;
  789.     if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
  790.     {
  791.         ::EnableWindow(hWndParent, FALSE);
  792.         bEnableParent = TRUE;
  793.     }
  794.     HWND hWndCapture = ::GetCapture();
  795.     if (hWndCapture != NULL)
  796.         ::SendMessage(hWndCapture, WM_CANCELMODE, 0, 0);
  797.  
  798.     // setup for modal loop and creation
  799.     m_nModalResult = 0;
  800.     m_nFlags |= WF_CONTINUEMODAL;
  801.  
  802.     // hook for creation of window
  803.     AfxHookWindowCreate(this);
  804.     psh->dwFlags |= PSH_MODELESS;
  805.     m_nFlags |= WF_CONTINUEMODAL;
  806.     HWND hWnd = (HWND)::PropertySheet((PROPSHEETHEADER*)psh);
  807. #ifdef _DEBUG
  808.     DWORD dwError = ::GetLastError();
  809. #endif
  810.     psh->dwFlags &= ~PSH_MODELESS;
  811.     AfxUnhookWindowCreate();
  812.  
  813.     // handle error
  814.     if (hWnd == NULL || hWnd == (HWND)-1)
  815.     {
  816.         TRACE1("PropertySheet() failed: GetLastError returned %d\n", dwError);
  817.         m_nFlags &= ~WF_CONTINUEMODAL;
  818.     }
  819.  
  820.     int nResult = m_nModalResult;
  821.     if (ContinueModal())
  822.     {
  823.         // enter modal loop
  824.         DWORD dwFlags = MLF_SHOWONIDLE;
  825.         if (GetStyle() & DS_NOIDLEMSG)
  826.             dwFlags |= MLF_NOIDLEMSG;
  827.         nResult = RunModalLoop(dwFlags);
  828.     }
  829.  
  830.     // hide the window before enabling parent window, etc.
  831.     if (m_hWnd != NULL)
  832.     {
  833.         SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
  834.             SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
  835.     }
  836.     if (bEnableParent)
  837.         ::EnableWindow(hWndParent, TRUE);
  838.     if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
  839.         ::SetActiveWindow(hWndParent);
  840.  
  841.     // cleanup
  842.     DestroyWindow();
  843.  
  844.     // allow OLE servers to enable themselves
  845.     if (pApp != NULL)
  846.         pApp->EnableModeless(TRUE);
  847.     if (hWndTop != NULL)
  848.         ::EnableWindow(hWndTop, TRUE);
  849.  
  850.     return nResult;
  851. }
  852.  
  853. int CALLBACK
  854. AfxPropSheetCallback(HWND, UINT message, LPARAM lParam)
  855. {
  856.     switch (message)
  857.     {
  858.     case PSCB_PRECREATE:
  859.         {
  860.             _AFX_THREAD_STATE* pState = AfxGetThreadState();
  861.             LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE)lParam;
  862.             if (lpTemplate->style != pState->m_dwPropStyle ||
  863.                 lpTemplate->dwExtendedStyle != pState->m_dwPropExStyle)
  864.             {
  865.                 // Mark the dialog template as read-write.
  866.                 DWORD dwOldProtect;
  867.                 VirtualProtect(lpTemplate, sizeof(DLGTEMPLATE), PAGE_READWRITE, &dwOldProtect);
  868.  
  869.                 // Ensure DS_SETFONT is set correctly.
  870.                 lpTemplate->style = lpTemplate->style & DS_SETFONT ?
  871.                                     pState->m_dwPropStyle | DS_SETFONT :
  872.                                     pState->m_dwPropStyle & ~DS_SETFONT;
  873.  
  874.                 lpTemplate->dwExtendedStyle = pState->m_dwPropExStyle;
  875.                 return TRUE;
  876.             }
  877.             return FALSE;
  878.         }
  879.     }
  880.  
  881.     return 0;
  882. }
  883.  
  884. BOOL CPropertySheet::Create(CWnd* pParentWnd, DWORD dwStyle, DWORD dwExStyle)
  885. {
  886.     _AFX_THREAD_STATE* pState = AfxGetThreadState();
  887.  
  888.     // Calculate the default window style.
  889.     if (dwStyle == (DWORD)-1)
  890.     {
  891.         pState->m_dwPropStyle = DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP |
  892.                                 DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION;
  893.  
  894.         // Wizards don't have WS_SYSMENU.
  895.         if (!IsWizard())
  896.             pState->m_dwPropStyle |= WS_SYSMENU;
  897.     }
  898.     else
  899.     {
  900.         pState->m_dwPropStyle = dwStyle;
  901.     }
  902.     pState->m_dwPropExStyle = dwExStyle;
  903.  
  904.     ASSERT_VALID(this);
  905.     ASSERT(m_hWnd == NULL);
  906.  
  907.     VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
  908.     AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);
  909.  
  910.     // finish building PROPSHEETHEADER structure
  911.     AFX_OLDPROPSHEETHEADER* psh = GetPropSheetHeader();
  912.  
  913.     BuildPropPageArray();
  914.     m_bModeless = TRUE;
  915.     psh->dwFlags |= (PSH_MODELESS|PSH_USECALLBACK);
  916.     psh->pfnCallback = AfxPropSheetCallback;
  917.     psh->hwndParent = pParentWnd->GetSafeHwnd();
  918.  
  919.     // hook the window creation process
  920.     AfxHookWindowCreate(this);
  921.     HWND hWnd = (HWND)PropertySheet((PROPSHEETHEADER*)psh);
  922. #ifdef _DEBUG
  923.     DWORD dwError = ::GetLastError();
  924. #endif
  925.  
  926.     // cleanup on failure, otherwise return TRUE
  927.     if (!AfxUnhookWindowCreate())
  928.         PostNcDestroy();    // cleanup if Create fails
  929.  
  930.     if (hWnd == NULL || hWnd == (HWND)-1)
  931.     {
  932.         TRACE1("PropertySheet() failed: GetLastError returned %d\n", dwError);
  933.         return FALSE;
  934.     }
  935.  
  936.     ASSERT(hWnd == m_hWnd);
  937.     return TRUE;
  938. }
  939.  
  940. void CPropertySheet::BuildPropPageArray()
  941. {
  942.     // delete existing prop page array
  943.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  944.     m_psh.ppsp = NULL;
  945.  
  946.     // build new prop page array
  947.     AFX_OLDPROPSHEETPAGE* ppsp = new AFX_OLDPROPSHEETPAGE[m_pages.GetSize()];
  948.     m_psh.ppsp = (LPPROPSHEETPAGE)ppsp;
  949.     BOOL bWizard = (m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97));
  950.     for (int i = 0; i < m_pages.GetSize(); i++)
  951.     {
  952.         CPropertyPage* pPage = GetPage(i);
  953.         memcpy(&ppsp[i], &pPage->m_psp, sizeof(pPage->m_psp));
  954.         pPage->PreProcessPageTemplate((PROPSHEETPAGE&)ppsp[i], bWizard);
  955.     }
  956.     m_psh.nPages = m_pages.GetSize();
  957. }
  958.  
  959. ////////////////////////////////////////////////////////////////////////////
  960.  
  961. int CPropertySheet::GetPageCount() const
  962. {
  963.     ASSERT_VALID(this);
  964.  
  965.     if (m_hWnd == NULL)
  966.         return m_pages.GetSize();
  967.  
  968.     CTabCtrl* pTab = GetTabControl();
  969.     ASSERT_VALID(pTab);
  970.     return pTab->GetItemCount();
  971. }
  972.  
  973. int CPropertySheet::GetActiveIndex() const
  974. {
  975.     if (m_hWnd == NULL)
  976.         return ((CPropertySheet*)this)->GetPropSheetHeader()->nStartPage;
  977.  
  978.     CTabCtrl* pTab = GetTabControl();
  979.     ASSERT_VALID(pTab);
  980.     return pTab->GetCurSel();
  981. }
  982.  
  983. BOOL CPropertySheet::SetActivePage(int nPage)
  984. {
  985.     if (m_hWnd == NULL)
  986.     {
  987.         GetPropSheetHeader()->nStartPage = nPage;
  988.         return TRUE;
  989.     }
  990.     return (BOOL)SendMessage(PSM_SETCURSEL, nPage);
  991. }
  992.  
  993. int CPropertySheet::GetPageIndex(CPropertyPage* pPage)
  994. {
  995.     for (int i = 0; i < GetPageCount(); i++)
  996.     {
  997.         if (GetPage(i) == pPage)
  998.             return i;
  999.     }
  1000.     return -1;  // pPage not found
  1001. }
  1002.  
  1003. BOOL CPropertySheet::SetActivePage(CPropertyPage* pPage)
  1004. {
  1005.     ASSERT_VALID(this);
  1006.     ASSERT(pPage != NULL);
  1007.     ASSERT_KINDOF(CPropertyPage, pPage);
  1008.  
  1009.     int nPage = GetPageIndex(pPage);
  1010.     ASSERT(pPage >= 0);
  1011.  
  1012.     return SetActivePage(nPage);
  1013. }
  1014.  
  1015. void CPropertySheet::AddPage(CPropertyPage* pPage)
  1016. {
  1017.     ASSERT_VALID(this);
  1018.     ASSERT(pPage != NULL);
  1019.     ASSERT_KINDOF(CPropertyPage, pPage);
  1020.     ASSERT_VALID(pPage);
  1021.  
  1022.     // add page to internal list
  1023.     m_pages.Add(pPage);
  1024.  
  1025.     // add page externally
  1026.     if (m_hWnd != NULL)
  1027.     {
  1028.         // build new prop page array
  1029.         AFX_OLDPROPSHEETPAGE *ppsp = new AFX_OLDPROPSHEETPAGE[m_pages.GetSize()];
  1030.         memcpy(ppsp, m_psh.ppsp, sizeof(AFX_OLDPROPSHEETPAGE) * (m_pages.GetSize()-1));
  1031.         delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1032.         m_psh.ppsp = (PROPSHEETPAGE*)ppsp;
  1033.         ppsp += m_pages.GetSize()-1;
  1034.  
  1035.         // copy processed PROPSHEETPAGE struct to end
  1036.         memcpy(ppsp, &pPage->m_psp, sizeof(pPage->m_psp));
  1037.         pPage->PreProcessPageTemplate((PROPSHEETPAGE&)*ppsp, IsWizard());
  1038.         HPROPSHEETPAGE hPSP = CreatePropertySheetPage((PROPSHEETPAGE*)ppsp);
  1039.         if (hPSP == NULL)
  1040.             AfxThrowMemoryException();
  1041.  
  1042.         if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP))
  1043.         {
  1044.             DestroyPropertySheetPage(hPSP);
  1045.             AfxThrowMemoryException();
  1046.         }
  1047.     }
  1048. }
  1049.  
  1050. void CPropertySheet::RemovePage(CPropertyPage* pPage)
  1051. {
  1052.     ASSERT_VALID(this);
  1053.     ASSERT(pPage != NULL);
  1054.     ASSERT_KINDOF(CPropertyPage, pPage);
  1055.  
  1056.     int nPage = GetPageIndex(pPage);
  1057.     ASSERT(nPage >= 0);
  1058.  
  1059.     RemovePage(nPage);
  1060. }
  1061.  
  1062. void CPropertySheet::RemovePage(int nPage)
  1063. {
  1064.     ASSERT_VALID(this);
  1065.  
  1066.     // remove the page externally
  1067.     if (m_hWnd != NULL)
  1068.         SendMessage(PSM_REMOVEPAGE, nPage);
  1069.  
  1070.     // remove the page from internal list
  1071.     m_pages.RemoveAt(nPage);
  1072. }
  1073.  
  1074. void CPropertySheet::EndDialog(int nEndID)
  1075. {
  1076.     ASSERT_VALID(this);
  1077.  
  1078.     CWnd::EndModalLoop(nEndID);
  1079.     if (m_bModeless)
  1080.         DestroyWindow();
  1081.     else
  1082.         PostMessage(PSM_PRESSBUTTON, PSBTN_CANCEL);
  1083. }
  1084.  
  1085. void CPropertySheet::OnClose()
  1086. {
  1087.     m_nModalResult = IDCANCEL;
  1088.     if (m_bModeless)
  1089.         DestroyWindow();
  1090.     else
  1091.         Default();
  1092. }
  1093.  
  1094. void CPropertySheet::OnSysCommand(UINT nID, LPARAM)
  1095. {
  1096.     m_nModalResult = IDCANCEL;
  1097.     switch (nID & 0xFFF0)
  1098.     {
  1099.     case SC_CLOSE:
  1100.         if (m_bModeless)
  1101.         {
  1102.             SendMessage(WM_CLOSE);
  1103.             return;
  1104.         }
  1105.     }
  1106.     Default();
  1107. }
  1108.  
  1109. /////////////////////////////////////////////////////////////////////////////
  1110. // CPropertySheet message handlers
  1111.  
  1112. AFX_STATIC_DATA int _afxPropSheetButtons[] = { IDOK, IDCANCEL, ID_APPLY_NOW, IDHELP };
  1113.  
  1114. BOOL CPropertySheet::OnInitDialog()
  1115. {
  1116.     // change tab style if scrolling tabs desired (stacked tabs are default)
  1117.     if (!m_bStacked)
  1118.     {
  1119.         HWND hWndTab = (HWND)::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL);
  1120.         if (hWndTab != NULL)
  1121.             CWnd::ModifyStyle(hWndTab, TCS_MULTILINE, TCS_SINGLELINE, 0);
  1122.     }
  1123.  
  1124.     if (!IsWizard())
  1125.     {
  1126.         // resize the tab control so the layout is less restrictive
  1127.         HWND hWnd = (HWND)::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL);
  1128.         ASSERT(hWnd != NULL);
  1129.         CRect rectOld;
  1130.         ::GetWindowRect(hWnd, &rectOld);
  1131.         ScreenToClient(rectOld);
  1132.         CRect rectNew(0, 0, 0, 32);
  1133.         ::MapDialogRect(m_hWnd, &rectNew);
  1134.         if (rectNew.bottom < rectOld.bottom)
  1135.         {
  1136.             // move tab control
  1137.             int cyDiff = rectOld.Height() - rectNew.bottom;
  1138.             ::SetWindowPos(hWnd, NULL, 0, 0, rectOld.Width(), rectNew.bottom,
  1139.                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  1140.  
  1141.             // move buttons by similar amount
  1142.             for (int i = 0; i < _countof(_afxPropSheetButtons); i++)
  1143.             {
  1144.                 hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetButtons[i]);
  1145.                 if (hWnd != NULL)
  1146.                 {
  1147.                     ::GetWindowRect(hWnd, &rectOld);
  1148.                     ScreenToClient(&rectOld);
  1149.                     ::SetWindowPos(hWnd, NULL,
  1150.                         rectOld.left, rectOld.top - cyDiff,
  1151.                         0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  1152.                 }
  1153.             }
  1154.  
  1155.             // resize property sheet itself similarly
  1156.             GetWindowRect(&rectOld);
  1157.             SetWindowPos(NULL, 0, 0, rectOld.Width(), rectOld.Height() - cyDiff,
  1158.                 SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  1159.         }
  1160.     }
  1161.  
  1162.     BOOL bResult = (BOOL)Default();
  1163.  
  1164.     if (m_bModeless && !IsWizard())
  1165.     {
  1166.         // layout property sheet so button area is not accounted for
  1167.         CRect rectWnd;
  1168.         GetWindowRect(rectWnd);
  1169.         CRect rectButton;
  1170.         HWND hWnd = ::GetDlgItem(m_hWnd, IDOK);
  1171.         ASSERT(hWnd != NULL);
  1172.         ::GetWindowRect(hWnd, rectButton);
  1173.         SetWindowPos(NULL, 0, 0,
  1174.             rectWnd.Width(), rectButton.top - rectWnd.top,
  1175.             SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  1176.  
  1177.         // remove standard buttons for modeless dialogs
  1178.         for (int i = 0; i < _countof(_afxPropSheetButtons); i++)
  1179.         {
  1180.             HWND hWnd = ::GetDlgItem(m_hWnd, _afxPropSheetButtons[i]);
  1181.             if (hWnd != NULL)
  1182.             {
  1183.                 ::ShowWindow(hWnd, SW_HIDE);
  1184.                 ::EnableWindow(hWnd, FALSE);
  1185.             }
  1186.         }
  1187.     }
  1188.  
  1189.     // center the property sheet relative to the parent window
  1190.     if (!(GetStyle() & WS_CHILD))
  1191.         CenterWindow();
  1192.  
  1193.     return bResult;
  1194. }
  1195.  
  1196. BOOL CPropertySheet::OnNcCreate(LPCREATESTRUCT)
  1197. {
  1198.     // By default, MFC does not directly support the new style
  1199.     // help button in the caption, so it is turned off here.
  1200.     // It can be added back in and implemented by derived classes
  1201.     // from CPropertySheet.
  1202.     ModifyStyleEx(WS_EX_CONTEXTHELP, 0);
  1203.  
  1204.     return (BOOL)Default();
  1205. }
  1206.  
  1207. LRESULT CPropertySheet::HandleInitDialog(WPARAM, LPARAM)
  1208. {
  1209.     LRESULT lResult = OnInitDialog();
  1210.     return lResult;
  1211. }
  1212.  
  1213. BOOL CPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam)
  1214. {
  1215.     // allow message map override
  1216.     if (CWnd::OnCommand(wParam, lParam))
  1217.         return TRUE;
  1218.  
  1219.     // crack message parameters
  1220.     UINT nID = LOWORD(wParam);
  1221.     HWND hWndCtrl = (HWND)lParam;
  1222.     int nCode = HIWORD(wParam);
  1223.  
  1224.     // set m_nModalResult to ID of button, whenever button is clicked
  1225.     if (hWndCtrl != NULL && nCode == BN_CLICKED)
  1226.     {
  1227.         if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0) &
  1228.             (DLGC_BUTTON|DLGC_DEFPUSHBUTTON))
  1229.         {
  1230.             LONG lStyle = ::GetWindowLong(hWndCtrl, GWL_STYLE) & 0x0F;
  1231.             if (lStyle == BS_PUSHBUTTON || lStyle == BS_DEFPUSHBUTTON ||
  1232.                 lStyle == BS_USERBUTTON || lStyle == BS_OWNERDRAW)
  1233.             {
  1234.                 m_nModalResult = nID;
  1235.             }
  1236.         }
  1237.     }
  1238.     return FALSE;
  1239. }
  1240.  
  1241. LRESULT CPropertySheet::OnCommandHelp(WPARAM wParam, LPARAM lParam)
  1242. {
  1243.     ASSERT_VALID(this);
  1244.  
  1245.     CPropertyPage* pPage = GetActivePage();
  1246.     ASSERT_VALID(pPage);
  1247.     return AfxCallWndProc(pPage, pPage->m_hWnd, WM_COMMANDHELP, wParam, lParam);
  1248. }
  1249.  
  1250. HBRUSH CPropertySheet::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  1251. {
  1252.     LRESULT lResult;
  1253.     if (pWnd->SendChildNotifyLastMsg(&lResult))
  1254.         return (HBRUSH)lResult;
  1255.  
  1256.     if (afxData.bWin4)
  1257.         return CWnd::OnCtlColor(pDC, pWnd, nCtlColor);
  1258.  
  1259.     if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
  1260.       afxData.hbrBtnFace, afxData.clrBtnText))
  1261.         return (HBRUSH)Default();
  1262.     return afxData.hbrBtnFace;
  1263. }
  1264.  
  1265. /////////////////////////////////////////////////////////////////////////////
  1266. // CPropertySheet Diagnostics
  1267.  
  1268. #ifdef _DEBUG
  1269. void CPropertySheet::AssertValid() const
  1270. {
  1271.     CWnd::AssertValid();
  1272.     m_pages.AssertValid();
  1273.     ASSERT(m_psh.dwSize == sizeof(m_psh));
  1274.     ASSERT((m_psh.dwFlags & PSH_PROPSHEETPAGE) == PSH_PROPSHEETPAGE);
  1275. }
  1276.  
  1277. void CPropertySheet::Dump(CDumpContext& dc) const
  1278. {
  1279.     CWnd::Dump(dc);
  1280.  
  1281.     dc << "m_strCaption = " << m_strCaption << "\n";
  1282.     dc << "Number of Pages = " << m_pages.GetSize() << "\n";
  1283.     dc << "Stacked = " << m_bStacked << "\n";
  1284.     dc << "Modeless = " << m_bModeless << "\n";
  1285. }
  1286. #endif //_DEBUG
  1287.  
  1288. ////////////////////////////////////////////////////////////////////////////
  1289. // CPropertyPageEx -- one page of a tabbed dialog, extended for IE4 features
  1290.  
  1291. CPropertyPageEx::CPropertyPageEx(UINT nIDTemplate, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1292. {
  1293.     ASSERT(nIDTemplate != 0);
  1294.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1295. }
  1296.  
  1297. CPropertyPageEx::CPropertyPageEx(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1298. {
  1299.     ASSERT(AfxIsValidString(lpszTemplateName));
  1300.     CommonConstruct(lpszTemplateName, nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1301. }
  1302.  
  1303. void CPropertyPageEx::Construct(UINT nIDTemplate, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1304. {
  1305.     ASSERT(nIDTemplate != 0);
  1306.     CommonConstruct(MAKEINTRESOURCE(nIDTemplate), nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1307. }
  1308.  
  1309. void CPropertyPageEx::Construct(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1310. {
  1311.     ASSERT(HIWORD(lpszTemplateName) == 0 ||
  1312.         AfxIsValidString(lpszTemplateName));
  1313.     CommonConstruct(lpszTemplateName, nIDCaption, nIDHeaderTitle, nIDHeaderSubTitle);
  1314. }
  1315.  
  1316. CPropertyPageEx::CPropertyPageEx()
  1317. {
  1318.     CommonConstruct(NULL, 0, 0, 0);
  1319. }
  1320.  
  1321. void CPropertyPageEx::CommonConstruct(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle)
  1322. {
  1323.     memset(&m_psp, 0, sizeof(m_psp));
  1324.     m_psp.dwSize = sizeof(m_psp);
  1325.     m_psp.dwFlags = PSP_USECALLBACK;
  1326.     if (lpszTemplateName != NULL)
  1327.         m_psp.hInstance = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
  1328.     m_psp.pszTemplate = lpszTemplateName;
  1329.     m_psp.pfnDlgProc = AfxDlgProc;
  1330.     m_psp.lParam = (LPARAM)this;
  1331.     m_psp.pfnCallback = AfxPropPageCallback;
  1332.     if (nIDCaption != 0)
  1333.     {
  1334.         VERIFY(m_strCaption.LoadString(nIDCaption));
  1335.         m_psp.pszTitle = m_strCaption;
  1336.         m_psp.dwFlags |= PSP_USETITLE;
  1337.     }
  1338.     if (nIDHeaderTitle != 0)
  1339.         VERIFY(m_strHeaderTitle.LoadString(nIDHeaderTitle));
  1340.     if (nIDHeaderSubTitle != 0)
  1341.         VERIFY(m_strHeaderSubTitle.LoadString(nIDHeaderSubTitle));
  1342.     if (AfxHelpEnabled())
  1343.         m_psp.dwFlags |= PSP_HASHELP;
  1344.     if (HIWORD(lpszTemplateName) == 0)
  1345.         m_nIDHelp = LOWORD((DWORD)lpszTemplateName);
  1346.     m_lpszTemplateName = m_psp.pszTemplate;
  1347.     m_bFirstSetActive = TRUE;
  1348. }
  1349.  
  1350. #ifdef _DEBUG
  1351. void CPropertyPageEx::AssertValid() const
  1352. {
  1353.     CDialog::AssertValid();
  1354.     ASSERT(m_psp.dwSize == sizeof(m_psp));
  1355.     ASSERT(m_psp.dwFlags & PSP_USECALLBACK);
  1356.     ASSERT(m_psp.pfnDlgProc == AfxDlgProc);
  1357.     ASSERT(m_psp.lParam == (LPARAM)this);
  1358. }
  1359.  
  1360. void CPropertyPageEx::Dump(CDumpContext& dc) const
  1361. {
  1362.     CDialog::Dump(dc);
  1363.  
  1364.     dc << "m_strCaption = " << m_strCaption << "\n";
  1365.     dc << "m_psp.dwFlags = " << m_psp.dwFlags << "\n";
  1366.     dc << "m_strHeaderTitle = " << m_strHeaderTitle << "\n";
  1367.     dc << "m_strHeaderSubTitle = " << m_strHeaderSubTitle << "\n";
  1368. }
  1369. #endif //_DEBUG
  1370.  
  1371. /////////////////////////////////////////////////////////////////////////////
  1372. // CPropertySheetEx -- a tabbed "dialog" (really a popup-window), extended
  1373. //                     for IE4
  1374.  
  1375. CPropertySheetEx::CPropertySheetEx()
  1376. {
  1377.     CommonConstruct(NULL, 0, NULL, NULL, NULL);
  1378. }
  1379.  
  1380. CPropertySheetEx::CPropertySheetEx(UINT nIDCaption, CWnd* pParentWnd,
  1381.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1382.     HBITMAP hbmHeader)
  1383. {
  1384.     ASSERT(nIDCaption != 0);
  1385.  
  1386.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  1387.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1388. }
  1389.  
  1390. CPropertySheetEx::CPropertySheetEx(LPCTSTR pszCaption, CWnd* pParentWnd,
  1391.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1392.     HBITMAP hbmHeader)
  1393. {
  1394.     ASSERT(pszCaption != NULL);
  1395.  
  1396.     m_strCaption = pszCaption;
  1397.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1398. }
  1399.  
  1400. void CPropertySheetEx::Construct(UINT nIDCaption, CWnd* pParentWnd,
  1401.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1402.     HBITMAP hbmHeader)
  1403. {
  1404.     ASSERT(nIDCaption != 0);
  1405.  
  1406.     VERIFY(m_strCaption.LoadString(nIDCaption) != 0);
  1407.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1408. }
  1409.  
  1410. void CPropertySheetEx::Construct(LPCTSTR pszCaption, CWnd* pParentWnd,
  1411.     UINT iSelectPage, HBITMAP hbmWatermark, HPALETTE hpalWatermark,
  1412.     HBITMAP hbmHeader)
  1413. {
  1414.     ASSERT(pszCaption != NULL);
  1415.  
  1416.     m_strCaption = pszCaption;
  1417.     CommonConstruct(pParentWnd, iSelectPage, hbmWatermark, hpalWatermark, hbmHeader);
  1418. }
  1419.  
  1420. void CPropertySheetEx::CommonConstruct(CWnd* pParentWnd, UINT iSelectPage,
  1421.     HBITMAP hbmWatermark, HPALETTE hpalWatermark, HBITMAP hbmHeader)
  1422. {
  1423.     memset(&m_psh, 0, sizeof(m_psh));
  1424.     m_psh.dwSize = sizeof(m_psh);
  1425.     m_psh.dwFlags = PSH_PROPSHEETPAGE;
  1426.     m_psh.pszCaption = m_strCaption;
  1427.     m_psh.nStartPage = iSelectPage;
  1428.     m_bStacked = TRUE;
  1429.     m_bModeless = FALSE;
  1430.  
  1431.     if (AfxHelpEnabled())
  1432.         m_psh.dwFlags |= PSH_HASHELP;
  1433.  
  1434.     if (hbmWatermark != NULL)
  1435.     {
  1436.         m_psh.hbmWatermark = hbmWatermark;
  1437.         m_psh.dwFlags |= (PSH_USEHBMWATERMARK | PSH_WATERMARK);
  1438.     }
  1439.     if (hpalWatermark != NULL)
  1440.     {
  1441.         m_psh.hplWatermark = hpalWatermark;
  1442.         m_psh.dwFlags |= PSH_USEHPLWATERMARK;
  1443.     }
  1444.     if (hbmHeader != NULL)
  1445.     {
  1446.         m_psh.hbmHeader = hbmHeader;
  1447.         m_psh.dwFlags |= (PSH_USEHBMHEADER | PSH_HEADER);
  1448.     }
  1449.     m_pParentWnd = pParentWnd;  // m_psh.hwndParent set in DoModal/create
  1450. }
  1451.  
  1452. void CPropertySheetEx::BuildPropPageArray()
  1453. {
  1454.     // delete existing prop page array
  1455.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1456.     m_psh.ppsp = NULL;
  1457.  
  1458.     // build new prop page array
  1459.     PROPSHEETPAGE* ppsp = new PROPSHEETPAGE[m_pages.GetSize()];
  1460.     m_psh.ppsp = (LPPROPSHEETPAGE)ppsp;
  1461.     BOOL bWizard = (m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97));
  1462.     for (int i = 0; i < m_pages.GetSize(); i++)
  1463.     {
  1464.         CPropertyPage* pPage = GetPage(i);
  1465.         memcpy(&ppsp[i], &pPage->m_psp, sizeof(pPage->m_psp));
  1466.         ppsp[i].dwSize = sizeof(PROPSHEETPAGE);
  1467.         if (pPage->IsKindOf(RUNTIME_CLASS(CPropertyPageEx)))
  1468.         {
  1469.             CPropertyPageEx* pPageEx = (CPropertyPageEx*)pPage;
  1470.             if (!pPageEx->m_strHeaderTitle.IsEmpty())
  1471.             {
  1472.                 ppsp[i].pszHeaderTitle = pPageEx->m_strHeaderTitle;
  1473.                 ppsp[i].dwFlags |= PSP_USEHEADERTITLE;
  1474.             }
  1475.             if (!pPageEx->m_strHeaderSubTitle.IsEmpty())
  1476.             {
  1477.                 ppsp[i].pszHeaderSubTitle = pPageEx->m_strHeaderSubTitle;
  1478.                 ppsp[i].dwFlags |= PSP_USEHEADERSUBTITLE;
  1479.             }
  1480.         }
  1481.         pPage->PreProcessPageTemplate(ppsp[i], bWizard);
  1482.     }
  1483.     m_psh.nPages = m_pages.GetSize();
  1484. }
  1485.  
  1486. CPropertySheetEx::~CPropertySheetEx()
  1487. {
  1488.     delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1489. }
  1490.  
  1491. void CPropertySheetEx::AddPage(CPropertyPageEx* pPage)
  1492. {
  1493.     ASSERT_VALID(this);
  1494.     ASSERT(pPage != NULL);
  1495.     ASSERT_KINDOF(CPropertyPageEx, pPage);
  1496.     ASSERT_VALID(pPage);
  1497.  
  1498.     // add page to internal list
  1499.     m_pages.Add(pPage);
  1500.  
  1501.     // add page externally
  1502.     if (m_hWnd != NULL)
  1503.     {
  1504.         // build new prop page array
  1505.         PROPSHEETPAGE *ppsp = new PROPSHEETPAGE[m_pages.GetSize()];
  1506.         memcpy(ppsp, m_psh.ppsp, sizeof(PROPSHEETPAGE) * (m_pages.GetSize()-1));
  1507.         delete[] (PROPSHEETPAGE*)m_psh.ppsp;
  1508.         m_psh.ppsp = ppsp;
  1509.         ppsp += m_pages.GetSize()-1;
  1510.  
  1511.         memcpy(ppsp, &pPage->m_psp, sizeof(pPage->m_psp));
  1512.         pPage->PreProcessPageTemplate(*ppsp, IsWizard());
  1513.         if (!pPage->m_strHeaderTitle.IsEmpty())
  1514.         {
  1515.             ppsp->pszHeaderTitle = pPage->m_strHeaderTitle;
  1516.             ppsp->dwFlags |= PSP_USEHEADERTITLE;
  1517.         }
  1518.         if (!pPage->m_strHeaderSubTitle.IsEmpty())
  1519.         {
  1520.             ppsp->pszHeaderSubTitle = pPage->m_strHeaderSubTitle;
  1521.             ppsp->dwFlags |= PSP_USEHEADERSUBTITLE;
  1522.         }
  1523.         HPROPSHEETPAGE hPSP = CreatePropertySheetPage(ppsp);
  1524.         if (hPSP == NULL)
  1525.             AfxThrowMemoryException();
  1526.  
  1527.         if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP))
  1528.         {
  1529.             DestroyPropertySheetPage(hPSP);
  1530.             AfxThrowMemoryException();
  1531.         }
  1532.     }
  1533. }
  1534.  
  1535. /////////////////////////////////////////////////////////////////////////////
  1536. // CPropertySheetEx Diagnostics
  1537.  
  1538. #ifdef _DEBUG
  1539. void CPropertySheetEx::AssertValid() const
  1540. {
  1541.     CWnd::AssertValid();
  1542.     m_pages.AssertValid();
  1543.     ASSERT(m_psh.dwSize == sizeof(m_psh));
  1544.     ASSERT((m_psh.dwFlags & PSH_PROPSHEETPAGE) == PSH_PROPSHEETPAGE);
  1545. }
  1546.  
  1547. void CPropertySheetEx::Dump(CDumpContext& dc) const
  1548. {
  1549.     CWnd::Dump(dc);
  1550.  
  1551.     dc << "m_strCaption = " << m_strCaption << "\n";
  1552.     dc << "Number of Pages = " << m_pages.GetSize() << "\n";
  1553.     dc << "Stacked = " << m_bStacked << "\n";
  1554.     dc << "Modeless = " << m_bModeless << "\n";
  1555. }
  1556. #endif //_DEBUG
  1557.  
  1558. #ifdef AFX_INIT_SEG
  1559. #pragma code_seg(AFX_INIT_SEG)
  1560. #endif
  1561.  
  1562. IMPLEMENT_DYNAMIC(CPropertyPage, CDialog)
  1563. IMPLEMENT_DYNAMIC(CPropertySheet, CWnd)
  1564. IMPLEMENT_DYNAMIC(CPropertyPageEx, CPropertyPage)
  1565. IMPLEMENT_DYNAMIC(CPropertySheetEx, CPropertySheet)
  1566.  
  1567. /////////////////////////////////////////////////////////////////////////////
  1568.