home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / ctlppg.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  39KB  |  1,471 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.  
  13. #ifdef AFXCTL_PAGE_SEG
  14. #pragma code_seg(AFXCTL_PAGE_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. #define MAX_CLASS_NAME  100
  25.  
  26. struct NotifyInfo
  27. {
  28.     LPCTSTR     szClassName;        // Name of control's class
  29.     WORD        wNotifyCode;        // Notification code
  30. };
  31.  
  32. #define MAX_TEXT    1024
  33.  
  34. BEGIN_INTERFACE_MAP(COlePropertyPage, CDialog)
  35.     INTERFACE_PART(COlePropertyPage, IID_IPropertyPage2, PropertyPage)
  36.     INTERFACE_PART(COlePropertyPage, IID_IPropertyPage, PropertyPage)
  37. END_INTERFACE_MAP()
  38.  
  39. BEGIN_MESSAGE_MAP(COlePropertyPage, CDialog)
  40.     //{{AFX_MSG_MAP(COlePropertyPage)
  41.     ON_WM_CTLCOLOR()
  42.     //}}AFX_MSG_MAP
  43. #ifndef _AFX_NO_CTL3D_SUPPORT
  44.     ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)
  45. #endif
  46. END_MESSAGE_MAP()
  47.  
  48. /////////////////////////////////////////////////////////////////////////////
  49. // AFX_DDPDATA
  50.  
  51. struct AFX_DDPDATA : public CObject
  52. {
  53.     AFX_DDPDATA(LPVOID lpHandler, int nCtrlId, BOOL bEditCtrl, LPVOID lpMember,
  54.         UINT nType, LPCTSTR lpszOleName);
  55.  
  56.     LPVOID  m_lpHandler;
  57.     int     m_nCtrlId;
  58.     BOOL    m_bEditCtrl;
  59.     LPVOID  m_lpMember;
  60.     UINT    m_nType;
  61.     LPCTSTR m_lpszOleName;
  62. };
  63.  
  64. AFX_DDPDATA::AFX_DDPDATA(LPVOID lpHandler, int nCtrlId, BOOL bEditCtrl,
  65.     LPVOID lpMember, UINT nType, LPCTSTR lpszOleName) :
  66.     m_lpHandler(lpHandler),
  67.     m_nCtrlId(nCtrlId),
  68.     m_bEditCtrl(bEditCtrl),
  69.     m_lpMember(lpMember),
  70.     m_nType(nType),
  71.     m_lpszOleName(lpszOleName)
  72. {
  73. }
  74.  
  75. /////////////////////////////////////////////////////////////////////////////
  76. // cleanup helper function
  77.  
  78. AFX_STATIC void AFXAPI _AfxCleanupDDPs(CPtrArray& arrayDDP)
  79. {
  80.     int cDDP = arrayDDP.GetSize();
  81.     for (int i = 0; i < cDDP; i++)
  82.     {
  83.         ASSERT(arrayDDP[i] != NULL);
  84.         delete (AFX_DDPDATA*)(arrayDDP[i]);
  85.     }
  86.     arrayDDP.RemoveAll();
  87. }
  88.  
  89. /////////////////////////////////////////////////////////////////////////////
  90. // page validation helper function (debug only)
  91.  
  92. #ifdef _DEBUG
  93. void _ValidatePageDialog(CDialogTemplate& dt, CString& strPage,
  94.     COlePropertyPage* pPage)
  95. {
  96.     if (GetSystemMetrics(SM_DBCSENABLED))
  97.         return;
  98.  
  99.     // Only display the message boxes the first time a page is shown.
  100. #ifdef _DEBUG
  101.     BOOL bEnable = AfxEnableMemoryTracking(FALSE);
  102. #endif
  103.     static CPtrList _classList;
  104.     BOOL bMessageBox = FALSE;
  105.     CRuntimeClass* pClass = pPage->GetRuntimeClass();
  106.     if (_classList.Find(pClass) == NULL)
  107.     {
  108.         bMessageBox = TRUE;
  109.         _classList.AddHead(pClass);
  110.     }
  111. #ifdef _DEBUG
  112.     AfxEnableMemoryTracking(bEnable);
  113. #endif
  114.  
  115.     SIZE sizeDLU;
  116.     CString strFontName;
  117.     WORD wFontSize;
  118.     LPTSTR pszTmp;
  119.  
  120.     dt.GetSizeInDialogUnits(&sizeDLU);
  121.     dt.GetFont(strFontName, wFontSize);
  122.  
  123.     if ((sizeDLU.cx != 250) || ((sizeDLU.cy != 62) && (sizeDLU.cy != 110)))
  124.     {
  125.         pszTmp = new TCHAR[strPage.GetLength() + 128];
  126.  
  127.         wsprintf(pszTmp, _T("Property page '%s' has nonstandard dimensions."),
  128.             (LPCTSTR)strPage);
  129.  
  130.         TRACE1("Warning: %s\n", pszTmp);
  131.  
  132.         if (bMessageBox)
  133.         {
  134.             lstrcat(pszTmp,
  135.                 _T(" Dimensions should be 250x62 or 250x110 dialog units."));
  136.             pPage->MessageBox(pszTmp, _T("Property page warning"),
  137.                 MB_ICONEXCLAMATION);
  138.         }
  139.  
  140.         delete [] pszTmp;
  141.     }
  142.  
  143.     if ((wFontSize != 8) ||
  144.         (strFontName != _T("MS Sans Serif") && strFontName != _T("Helv") &&
  145.         strFontName != _T("MS Shell Dlg")))
  146.     {
  147.         pszTmp = new TCHAR[strPage.GetLength() + 128];
  148.  
  149.         wsprintf(pszTmp, _T("Property page '%s' uses a nonstandard font."),
  150.             (LPCTSTR)strPage);
  151.  
  152.         TRACE1("Warning: %s\n", pszTmp);
  153.  
  154.         if (bMessageBox)
  155.         {
  156.             lstrcat(pszTmp, _T(" Font should be MS Sans Serif 8."));
  157.             pPage->MessageBox(pszTmp, _T("Property page warning"),
  158.                 MB_ICONEXCLAMATION);
  159.         }
  160.  
  161.         delete [] pszTmp;
  162.     }
  163. }
  164. #endif // _DEBUG
  165.  
  166. /////////////////////////////////////////////////////////////////////////////
  167. // COlePropertyPage::COlePropertyPage
  168.  
  169. COlePropertyPage::COlePropertyPage(UINT idDlg, UINT idCaption) :
  170. #ifdef _DEBUG
  171.     m_bNonStandardSize(FALSE),
  172. #endif
  173.     m_bDirty(FALSE),
  174.     m_idCaption(idCaption),
  175.     m_idDlg(idDlg),
  176.     m_pPageSite(NULL),
  177.     m_ppDisp(NULL),
  178.     m_pAdvisors(NULL),
  179.     m_bPropsChanged(FALSE),
  180.     m_nObjects(0),
  181.     m_bInitializing(TRUE),
  182.     m_nControls(0),
  183.     m_pStatus(NULL),
  184.     m_hDialog(NULL),
  185.     m_dwHelpContext(0)
  186. {
  187.     // m_lpDialogTemplate is needed later by CWnd::ExecuteDlgInit
  188.     m_lpszTemplateName = MAKEINTRESOURCE(m_idDlg);
  189.  
  190.     // Keep this DLL loaded at least until this object is deleted.
  191.     AfxOleLockApp();
  192. }
  193.  
  194. void COlePropertyPage::OnSetPageSite()
  195. {
  196.     // Load the caption from resources
  197.     CString strCaption;
  198.     if (!strCaption.LoadString(m_idCaption))
  199.         strCaption.LoadString(AFX_IDS_PROPPAGE_UNKNOWN);
  200.     SetPageName(strCaption);
  201.  
  202.     // Try to load the dialog resource template into memory and get its size
  203.     m_sizePage.cx = 0;
  204.     m_sizePage.cy = 0;
  205.  
  206.     CDialogTemplate dt;
  207.     dt.Load(MAKEINTRESOURCE(m_idDlg));
  208.  
  209. #ifdef _DEBUG
  210.     if (!m_bNonStandardSize)
  211.         _ValidatePageDialog(dt, strCaption, this);
  212. #endif
  213.  
  214.     // If no font specified, set the system font.
  215.     BOOL bSetSysFont = !dt.HasFont();
  216.     CString strFace;
  217.     WORD wSize;
  218.  
  219.     // On DBCS systems, also change "MS Sans Serif" or "Helv" to system font.
  220.     if ((!bSetSysFont) && GetSystemMetrics(SM_DBCSENABLED))
  221.     {
  222.         CString strFace;
  223.         dt.GetFont(strFace, wSize);
  224.         bSetSysFont = (strFace == _T("MS Shell Dlg") ||
  225.             strFace == _T("MS Sans Serif") || strFace == _T("Helv"));
  226.     }
  227.  
  228.     // Here is where we actually set the font.
  229.     if (bSetSysFont && AfxGetPropSheetFont(strFace, wSize, FALSE))
  230.         dt.SetFont(strFace, wSize);
  231.  
  232.     dt.GetSizeInPixels(&m_sizePage);
  233.     m_hDialog = dt.Detach();
  234. }
  235.  
  236. BOOL COlePropertyPage::OnInitDialog()
  237. {
  238.     CDialog::OnInitDialog();
  239.     return 0;
  240. }
  241.  
  242. BOOL COlePropertyPage::PreTranslateMessage(LPMSG lpMsg)
  243. {
  244.     // Don't let CDialog process the Return key or Escape key.
  245.     if ((lpMsg->message == WM_KEYDOWN) &&
  246.         ((lpMsg->wParam == VK_RETURN) || (lpMsg->wParam == VK_ESCAPE)))
  247.     {
  248.         // Special case: if control with focus is an edit control with
  249.         // ES_WANTRETURN style, let it handle the Return key.
  250.  
  251.         TCHAR szClass[10];
  252.         CWnd* pWndFocus = GetFocus();
  253.         if ((lpMsg->wParam == VK_RETURN) &&
  254.             ((pWndFocus = GetFocus()) != NULL) &&
  255.             IsChild(pWndFocus) &&
  256.             (pWndFocus->GetStyle() & ES_WANTRETURN) &&
  257.             GetClassName(pWndFocus->m_hWnd, szClass, 10) &&
  258.             (lstrcmpi(szClass, _T("EDIT")) == 0))
  259.         {
  260.             pWndFocus->SendMessage(WM_CHAR, lpMsg->wParam, lpMsg->lParam);
  261.             return TRUE;
  262.         }
  263.  
  264.         return FALSE;
  265.     }
  266.  
  267.     // If it's a WM_SYSKEYDOWN, temporarily replace the hwnd in the
  268.     // message with the hwnd of our first control, and try to handle
  269.     // the message for ourselves.
  270.  
  271.     BOOL bHandled;
  272.  
  273.     if ((lpMsg->message == WM_SYSKEYDOWN) && !::IsChild(m_hWnd, lpMsg->hwnd))
  274.     {
  275.         HWND hWndSave = lpMsg->hwnd;
  276.         lpMsg->hwnd = ::GetWindow(m_hWnd, GW_CHILD);
  277.         bHandled = CDialog::PreTranslateMessage(lpMsg);
  278.         lpMsg->hwnd = hWndSave;
  279.     }
  280.     else
  281.     {
  282.         bHandled = CDialog::PreTranslateMessage(lpMsg);
  283.     }
  284.  
  285.     return bHandled;
  286. }
  287.  
  288. void COlePropertyPage::CleanupObjectArray()
  289. {
  290.     if (m_pAdvisors)
  291.     {
  292.         for (ULONG nObject = 0; nObject < m_nObjects; nObject++)
  293.             AfxConnectionUnadvise(m_ppDisp[nObject], IID_IPropertyNotifySink,
  294.                 &m_xPropNotifySink, FALSE, m_pAdvisors[nObject]);
  295.         delete [] m_pAdvisors;
  296.         m_pAdvisors = NULL;
  297.     }
  298.     if (m_ppDisp)
  299.     {
  300.         for (ULONG nObject = 0; nObject < m_nObjects; nObject++)
  301.             RELEASE(m_ppDisp[nObject]);
  302.         delete [] m_ppDisp;
  303.         m_ppDisp = NULL;
  304.     }
  305. }
  306.  
  307. COlePropertyPage::~COlePropertyPage()
  308. {
  309.     // Remember to free the resource we loaded!
  310.     if (m_hDialog != NULL)
  311.         GlobalFree(m_hDialog);
  312.  
  313.     if (m_pStatus != NULL)
  314.     {
  315.         delete [] m_pStatus;
  316.         m_pStatus = NULL;
  317.     }
  318.  
  319.     // Clean out any leftovers in the DDP map.
  320.     _AfxCleanupDDPs(m_arrayDDP);
  321.  
  322.     RELEASE(m_pPageSite);
  323.     CleanupObjectArray();
  324.     AfxOleUnlockApp();
  325. }
  326.  
  327. void COlePropertyPage::OnFinalRelease()
  328. {
  329.     if (m_hWnd != NULL)
  330.         DestroyWindow();
  331.  
  332.     delete this;
  333. }
  334.  
  335. LRESULT COlePropertyPage::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
  336. {
  337.     AFX_MANAGE_STATE(m_pModuleState);
  338.  
  339.     // Forward WM_SYSCOMMAND messages to the frame for translation
  340.     if (msg == WM_SYSCOMMAND && (wParam & 0xFFF0) != SC_KEYMENU && m_pPageSite != NULL)
  341.     {
  342.         if (m_pPageSite->TranslateAccelerator((LPMSG)GetCurrentMessage())
  343.             == S_OK)
  344.         {
  345.             return 0;
  346.         }
  347.     }
  348.  
  349.     return CDialog::WindowProc(msg, wParam, lParam);
  350. }
  351.  
  352. HBRUSH COlePropertyPage::OnCtlColor(CDC*, CWnd* pWnd, UINT)
  353. {
  354.     // allow the control itself first crack at the color
  355.     LRESULT lResult;
  356.     if (pWnd->SendChildNotifyLastMsg(&lResult))
  357.         return (HBRUSH)lResult;
  358.  
  359.     // allow the parent window to determine the color
  360.     HWND hParent = ::GetParent(m_hWnd);
  361.     const MSG* pMsg = GetCurrentMessage();
  362.     HBRUSH hResult = (HBRUSH)::SendMessage(hParent,
  363.         pMsg->message, pMsg->wParam, pMsg->lParam);
  364.  
  365.     // use default if parent returns NULL
  366.     if (hResult == NULL)
  367.         hResult = (HBRUSH)Default();
  368.     return hResult;
  369. }
  370.  
  371. BOOL COlePropertyPage::OnHelp(LPCTSTR)
  372. {
  373.     // May be overridden by subclass.
  374.     return FALSE;
  375. }
  376.  
  377. BOOL COlePropertyPage::OnEditProperty(DISPID)
  378. {
  379.     // May be overridden by subclass.
  380.     return FALSE;
  381. }
  382.  
  383. void COlePropertyPage::OnObjectsChanged()
  384. {
  385.     // May be overridden by subclass.
  386. }
  387.  
  388. LPDISPATCH* COlePropertyPage::GetObjectArray(ULONG* pnObjects)
  389. {
  390.     ASSERT_POINTER(pnObjects, ULONG);
  391.  
  392.     if (pnObjects != NULL)
  393.         *pnObjects = m_nObjects;
  394.  
  395.     return m_ppDisp;
  396. }
  397.  
  398. void COlePropertyPage::SetModifiedFlag(BOOL bModified)
  399. {
  400.     if (!bModified)
  401.         m_bPropsChanged = FALSE;
  402.  
  403.     if ((m_bDirty && !bModified) || (!m_bDirty && bModified))
  404.     {
  405.         m_bDirty = bModified;
  406.  
  407.         if (m_pPageSite != NULL)
  408.         {
  409.             DWORD flags = 0;
  410.             if (bModified)
  411.                 flags |= PROPPAGESTATUS_DIRTY;
  412.  
  413.             m_pPageSite->OnStatusChange(flags);
  414.         }
  415.     }
  416. }
  417.  
  418. BOOL COlePropertyPage::IsModified()
  419. {
  420.     return m_bDirty;
  421. }
  422.  
  423. void COlePropertyPage::SetPageName(LPCTSTR lpszPageName)
  424. {
  425.     ASSERT(AfxIsValidString(lpszPageName));
  426.     m_strPageName = lpszPageName;
  427. }
  428.  
  429. void COlePropertyPage::SetDialogResource(HGLOBAL hDialog)
  430. {
  431.     if (m_hDialog != NULL)
  432.     {
  433.         GlobalFree(m_hDialog);
  434.         m_hDialog = NULL;
  435.     }
  436.  
  437.     CDialogTemplate dt(hDialog);
  438.  
  439. #ifdef _DEBUG
  440.     _ValidatePageDialog(dt, m_strPageName, this);
  441. #endif
  442.  
  443.     dt.GetSizeInPixels(&m_sizePage);
  444.     m_hDialog = dt.Detach();
  445. }
  446.  
  447. void COlePropertyPage::SetHelpInfo(LPCTSTR lpszDocString,
  448.     LPCTSTR lpszHelpFile, DWORD dwHelpContext)
  449. {
  450.     ASSERT((lpszDocString == NULL) || AfxIsValidString(lpszDocString));
  451.     ASSERT((lpszHelpFile == NULL) || AfxIsValidString(lpszHelpFile));
  452.  
  453.     m_strDocString = lpszDocString;
  454.     m_strHelpFile = lpszHelpFile;
  455.     m_dwHelpContext = dwHelpContext;
  456. }
  457.  
  458. LPPROPERTYPAGESITE COlePropertyPage::GetPageSite()
  459. {
  460.     return m_pPageSite;
  461. }
  462.  
  463. int COlePropertyPage::MessageBox(LPCTSTR lpszText, LPCTSTR lpszCaption,
  464.     UINT nType)
  465. {
  466.     // use caption of page by default
  467.     if (lpszCaption == NULL)
  468.         lpszCaption = m_strPageName;
  469.  
  470.     // start message box on safe owner of the page
  471.     return ::MessageBox(GetSafeOwner_(m_hWnd, NULL), lpszText, lpszCaption, nType);
  472. }
  473.  
  474. /////////////////////////////////////////////////////////////////////////////
  475. // COlePropertyPage::XPropertyPage
  476.  
  477. STDMETHODIMP_(ULONG) COlePropertyPage::XPropertyPage::AddRef()
  478. {
  479.     METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
  480.     return (ULONG)pThis->ExternalAddRef();
  481. }
  482.  
  483. STDMETHODIMP_(ULONG) COlePropertyPage::XPropertyPage::Release()
  484. {
  485.     METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
  486.     return (ULONG)pThis->ExternalRelease();
  487. }
  488.  
  489. STDMETHODIMP COlePropertyPage::XPropertyPage::QueryInterface(
  490.     REFIID iid, LPVOID* ppvObj)
  491. {
  492.     METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
  493.     return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
  494. }
  495.  
  496. STDMETHODIMP COlePropertyPage::XPropertyPage::SetPageSite(
  497.     LPPROPERTYPAGESITE pPageSite)
  498. {
  499.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  500.     ASSERT_VALID(pThis);
  501.     ASSERT_POINTER(pPageSite, IPropertyPageSite);
  502.  
  503.     RELEASE(pThis->m_pPageSite);            // release the old one
  504.  
  505.     pThis->m_pPageSite = pPageSite;
  506.     if (pPageSite != NULL)
  507.         pThis->m_pPageSite->AddRef();       // Bump the reference count
  508.  
  509.     pThis->OnSetPageSite();
  510.     return S_OK;
  511. }
  512.  
  513. STDMETHODIMP COlePropertyPage::XPropertyPage::Activate(HWND hWndParent,
  514.     LPCRECT pRect, BOOL)
  515. {
  516.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  517.     ASSERT_VALID(pThis);
  518.     ASSERT_NULL_OR_POINTER(pRect, RECT);
  519.  
  520.     BOOL bSuccess = FALSE;  // Did we successfully create the dialog box
  521.  
  522.     if (pThis->m_hDialog != NULL)
  523.     {
  524.         // We've already loaded the dialog template into memory so just
  525.         // create it!
  526.  
  527.         void* lpDialogTemplate = LockResource(pThis->m_hDialog);
  528.         if (lpDialogTemplate != NULL)
  529.         {
  530.             bSuccess = pThis->CreateIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent));
  531.             UnlockResource(pThis->m_hDialog);
  532.         }
  533.         else
  534.             bSuccess = pThis->Create(pThis->m_idDlg, CWnd::FromHandle(hWndParent));
  535.     }
  536.     else
  537.         bSuccess = pThis->Create(pThis->m_idDlg, CWnd::FromHandle(hWndParent));
  538.  
  539.     // Were we successful in creating the dialog box!
  540.     if (bSuccess)
  541.     {
  542.         pThis->MoveWindow(pRect);       // Force page to fill area given by frame *
  543.         pThis->m_bInitializing = TRUE;
  544.         pThis->UpdateData(FALSE);
  545.         pThis->SetModifiedFlag(FALSE);
  546.         pThis->m_bInitializing = FALSE;
  547.  
  548.         if (pThis->m_pStatus != NULL)
  549.         {
  550.             delete [] pThis->m_pStatus;
  551.             pThis->m_pStatus = NULL;
  552.         }
  553.  
  554.         pThis->m_nControls = 0;
  555.         ::EnumChildWindows(pThis->GetSafeHwnd(), (WNDENUMPROC) COlePropertyPage::EnumChildProc, (LPARAM) pThis);
  556.  
  557.         if (pThis->m_nControls > 0)
  558.             pThis->m_pStatus = new AFX_PPFIELDSTATUS [UINT(pThis->m_nControls)];
  559.  
  560.         pThis->m_nControls = 0;
  561.         EnumChildWindows(pThis->GetSafeHwnd(), (WNDENUMPROC) COlePropertyPage::EnumControls, (LPARAM) pThis);
  562.  
  563.         return S_OK;
  564.     }
  565.  
  566.     return E_FAIL;
  567. }
  568.  
  569. BOOL CALLBACK COlePropertyPage::EnumChildProc(HWND, LPARAM lParam)
  570. {
  571.     COlePropertyPage* pDlg = (COlePropertyPage*) lParam;
  572.     ASSERT_POINTER(pDlg, COlePropertyPage);
  573.  
  574.     pDlg->m_nControls++;
  575.     return TRUE;
  576. }
  577.  
  578. BOOL CALLBACK COlePropertyPage::EnumControls(HWND hWnd, LPARAM lParam)
  579. {
  580.     COlePropertyPage* pDlg = (COlePropertyPage*) lParam;
  581.     ASSERT_POINTER(pDlg, COlePropertyPage);
  582.     ASSERT(pDlg->m_pStatus != NULL);
  583.  
  584.     pDlg->m_pStatus[pDlg->m_nControls].nID = (UINT)::GetDlgCtrlID(hWnd);
  585.     pDlg->m_pStatus[pDlg->m_nControls].bDirty = FALSE;
  586.     pDlg->m_nControls++;
  587.  
  588.     return TRUE;
  589. }
  590.  
  591. STDMETHODIMP COlePropertyPage::XPropertyPage::Deactivate()
  592. {
  593.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  594.     pThis->DestroyWindow();
  595.  
  596.     return S_OK;
  597. }
  598.  
  599. STDMETHODIMP COlePropertyPage::XPropertyPage::GetPageInfo(
  600.     LPPROPPAGEINFO pPageInfo)
  601. {
  602.     METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
  603.     ASSERT_POINTER(pPageInfo, PROPPAGEINFO);
  604.  
  605.     pPageInfo->pszTitle = AfxAllocTaskOleString(pThis->m_strPageName);
  606.     pPageInfo->size = pThis->m_sizePage;
  607.     pPageInfo->pszDocString = AfxAllocTaskOleString(pThis->m_strDocString);
  608.     pPageInfo->pszHelpFile = AfxAllocTaskOleString(pThis->m_strHelpFile);
  609.     pPageInfo->dwHelpContext = pThis->m_dwHelpContext;
  610.     return S_OK;
  611. }
  612.  
  613. STDMETHODIMP COlePropertyPage::XPropertyPage::SetObjects(
  614.         ULONG cObjects, LPUNKNOWN* ppUnk)
  615. {
  616.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  617.     ASSERT_VALID(pThis);
  618.  
  619.     pThis->CleanupObjectArray();
  620.  
  621.     if (cObjects != 0)
  622.     {
  623.         ASSERT(AfxIsValidAddress(ppUnk, sizeof(LPUNKNOWN) * (int)cObjects, FALSE));
  624.  
  625.         pThis->m_ppDisp = new LPDISPATCH [(UINT)cObjects];
  626.         pThis->m_pAdvisors = new DWORD [(UINT)cObjects];
  627.         for (ULONG nObject = 0; nObject < cObjects; nObject++)
  628.         {
  629.             HRESULT hr = ppUnk[nObject]->QueryInterface(IID_IDispatch,
  630.                 (VOID**)&(pThis->m_ppDisp[nObject]));
  631.             if (SUCCEEDED(hr))
  632.             {
  633.                 AfxConnectionAdvise(ppUnk[nObject], IID_IPropertyNotifySink,
  634.                     &pThis->m_xPropNotifySink, FALSE,
  635.                     &pThis->m_pAdvisors[nObject]);
  636.             }
  637.             else
  638.             {
  639.                 return hr;
  640.             }
  641.         }
  642.     }
  643.  
  644.     pThis->m_nObjects = cObjects;
  645.  
  646.     // No painting during the data update.
  647.     BOOL bLock = (pThis->m_hWnd != NULL) && pThis->IsWindowVisible();
  648.     if (bLock)
  649.         ::LockWindowUpdate(pThis->m_hWnd);
  650.  
  651.     pThis->OnObjectsChanged();
  652.  
  653.     // If window exists, update the data in its fields.
  654.     if (cObjects != 0 && pThis->m_hWnd != NULL)
  655.     {
  656.         pThis->UpdateData(FALSE);
  657.         pThis->SetModifiedFlag(FALSE);
  658.     }
  659.  
  660.     if (bLock)
  661.         ::LockWindowUpdate(NULL);
  662.  
  663.     return S_OK;
  664. }
  665.  
  666. STDMETHODIMP COlePropertyPage::XPropertyPage::Show(UINT nCmdShow)
  667. {
  668.     METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
  669.  
  670.     pThis->ShowWindow(nCmdShow);
  671.     if (nCmdShow == SW_SHOWNORMAL)
  672.         pThis->SetFocus();
  673.     return S_OK;
  674. }
  675.  
  676. STDMETHODIMP COlePropertyPage::XPropertyPage::Move(LPCRECT pRect)
  677. {
  678.     METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
  679.     ASSERT_POINTER(pRect, RECT);
  680.  
  681.     pThis->MoveWindow(pRect);
  682.     return S_OK;
  683. }
  684.  
  685. STDMETHODIMP COlePropertyPage::XPropertyPage::IsPageDirty()
  686. {
  687.     METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
  688.  
  689.     return pThis->m_bDirty ? S_OK : S_FALSE;
  690. }
  691.  
  692. STDMETHODIMP COlePropertyPage::XPropertyPage::Apply()
  693. {
  694.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  695.     ASSERT_VALID(pThis);
  696.  
  697.     HRESULT hr = S_OK;
  698.     BOOL bClean = FALSE;
  699.  
  700.     if (pThis->m_bDirty)
  701.     {
  702.         if (pThis->UpdateData(TRUE))
  703.         {
  704.             pThis->m_bDirty = FALSE;
  705.             bClean = TRUE;
  706.         }
  707.         else
  708.             hr = E_FAIL;
  709.  
  710.         if (pThis->m_bPropsChanged)
  711.         {
  712.             pThis->UpdateData(FALSE);
  713.             pThis->m_bPropsChanged = FALSE;
  714.             bClean = TRUE;
  715.         }
  716.  
  717.         if (bClean)
  718.         {
  719.             // Set the dirty status of all the controls on this page to FALSE.
  720.             if (pThis->m_pStatus != NULL)
  721.             {
  722.                 for (int nControl = 0; nControl < pThis->m_nControls; nControl++)
  723.                     pThis->m_pStatus[nControl].bDirty = FALSE;
  724.             }
  725.         }
  726.     }
  727.     else
  728.         ASSERT(!pThis->m_bPropsChanged);
  729.  
  730.     return hr;
  731. }
  732.  
  733. STDMETHODIMP COlePropertyPage::XPropertyPage::Help(LPCOLESTR lpszHelpDir)
  734. {
  735.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  736.     ASSERT_VALID(pThis);
  737.     ASSERT((lpszHelpDir == NULL) || AfxIsValidString(lpszHelpDir));
  738.  
  739.     USES_CONVERSION;
  740.  
  741.     if (!pThis->OnHelp(OLE2CT(lpszHelpDir)))
  742.         return S_FALSE;
  743.     else
  744.         return S_OK;
  745. }
  746.  
  747. BOOL AFXAPI _AfxAtEndOfTabList(CDialog* pDlg, UINT nCmd)
  748. {
  749.     if ((pDlg->SendMessage(WM_GETDLGCODE) &
  750.         (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE | DLGC_WANTTAB)) == 0)
  751.     {
  752.         CWnd* pCtl = CWnd::GetFocus();
  753.         if (pDlg->IsChild(pCtl))
  754.         {
  755.             // Get top level child for controls with children, like combo.
  756.             while (pCtl->GetParent() != pDlg)
  757.             {
  758.                 pCtl = pCtl->GetParent();
  759.                 ASSERT_VALID(pCtl);
  760.             }
  761.  
  762.             do
  763.             {
  764.                 if ((pCtl = pCtl->GetWindow(nCmd)) == NULL)
  765.                     return TRUE;
  766.             }
  767.             while ((pCtl->GetStyle() & (WS_DISABLED | WS_TABSTOP)) != WS_TABSTOP);
  768.         }
  769.     }
  770.  
  771.     return FALSE;
  772. }
  773.  
  774. STDMETHODIMP COlePropertyPage::XPropertyPage::TranslateAccelerator(LPMSG lpMsg)
  775. {
  776.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  777.     ASSERT_VALID(pThis);
  778.     ASSERT_POINTER(lpMsg, MSG);
  779.  
  780.     BOOL bHandled = FALSE;
  781.  
  782.     if (lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_TAB &&
  783.         GetKeyState(VK_CONTROL) >= 0)
  784.     {
  785.         if (pThis->IsChild(CWnd::GetFocus()))
  786.         {
  787.             // We already have the focus.  Let's determine whether we should
  788.             // pass focus up to the frame.
  789.  
  790.             if (_AfxAtEndOfTabList(pThis, GetKeyState(VK_SHIFT) < 0 ?
  791.                 GW_HWNDPREV : GW_HWNDNEXT))
  792.             {
  793.                 // fix for default button border
  794.                 DWORD dwDefID = pThis->GetDefID();
  795.                 if (HIWORD(dwDefID) == DC_HASDEFID)
  796.                 {
  797.                     CWnd *pDefBtn = pThis->GetDlgItem(LOWORD(dwDefID));
  798.                     if (pDefBtn != NULL && pDefBtn->IsWindowEnabled())
  799.                         pThis->GotoDlgCtrl(pDefBtn);
  800.                 }
  801.  
  802.                 // Pass focus to the frame by letting the page site handle
  803.                 // this message.
  804.                 if (pThis->m_pPageSite != NULL)
  805.                     bHandled =
  806.                         pThis->m_pPageSite->TranslateAccelerator(lpMsg) ==
  807.                         S_OK;
  808.             }
  809.         }
  810.         else
  811.         {
  812.             // We don't already have the focus.  The frame is passing the
  813.             // focus to us.
  814.  
  815.             CWnd* pWnd = pThis->GetTopWindow();
  816.             if (pWnd != NULL)
  817.             {
  818.                 UINT gwInit;
  819.                 UINT gwMove;
  820.  
  821.                 if (GetKeyState(VK_SHIFT) >= 0)
  822.                 {
  823.                     // Set the focus to the first tabstop in the page.
  824.                     gwInit = GW_HWNDFIRST;
  825.                     gwMove = GW_HWNDNEXT;
  826.                 }
  827.                 else
  828.                 {
  829.                     // Set the focus to the last tabstop in the page.
  830.                     gwInit = GW_HWNDLAST;
  831.                     gwMove = GW_HWNDPREV;
  832.                 }
  833.  
  834.                 pWnd = pWnd->GetWindow(gwInit);
  835.                 while (pWnd != NULL)
  836.                 {
  837.                     if ((pWnd->GetStyle() & (WS_DISABLED | WS_TABSTOP)) ==
  838.                         WS_TABSTOP)
  839.                     {
  840.                         pThis->GotoDlgCtrl(pWnd);
  841.                         bHandled = TRUE;
  842.                         break;
  843.                     }
  844.  
  845.                     pWnd = pWnd->GetWindow(gwMove);
  846.                 }
  847.             }
  848.         }
  849.     }
  850.  
  851.     // If message was not handled here, call PreTranslateMessage
  852.     return (bHandled || pThis->PreTranslateMessage(lpMsg)) ?
  853.         S_OK : S_FALSE;
  854. }
  855.  
  856. STDMETHODIMP COlePropertyPage::XPropertyPage::EditProperty(DISPID dispid)
  857. {
  858.     METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
  859.     ASSERT_VALID(pThis);
  860.  
  861.     return pThis->OnEditProperty(dispid) ? S_OK : E_NOTIMPL;
  862. }
  863.  
  864. /////////////////////////////////////////////////////////////////////////////
  865. // COlePropertyPage::XPropNotifySink
  866.  
  867. STDMETHODIMP_(ULONG) COlePropertyPage::XPropNotifySink::AddRef()
  868. {
  869.     return 1;
  870. }
  871.  
  872. STDMETHODIMP_(ULONG) COlePropertyPage::XPropNotifySink::Release()
  873. {
  874.     return 0;
  875. }
  876.  
  877. STDMETHODIMP COlePropertyPage::XPropNotifySink::QueryInterface(
  878.     REFIID iid, LPVOID* ppvObj)
  879. {
  880.     if (IsEqualIID(iid, IID_IPropertyNotifySink) ||
  881.         IsEqualIID(iid, IID_IUnknown))
  882.     {
  883.         *ppvObj = this;
  884.         return S_OK;
  885.     }
  886.     else
  887.     {
  888.         *ppvObj = NULL;
  889.         return E_NOINTERFACE;
  890.     }
  891. }
  892.  
  893. STDMETHODIMP COlePropertyPage::XPropNotifySink::OnRequestEdit(DISPID)
  894. {
  895.     return S_OK;
  896. }
  897.  
  898. STDMETHODIMP COlePropertyPage::XPropNotifySink::OnChanged(DISPID)
  899. {
  900.     METHOD_PROLOGUE_EX(COlePropertyPage, PropNotifySink)
  901.  
  902.     // If we're not currently in the middle of an UpdateData, call it now.
  903.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  904.     if (pThis->m_hWnd != NULL &&
  905.         pThreadState->m_hLockoutNotifyWindow != pThis->m_hWnd)
  906.     {
  907.         pThis->UpdateData(FALSE);
  908.     }
  909.     else
  910.     {
  911.         pThis->m_bPropsChanged = TRUE;
  912.     }
  913.  
  914.     return S_OK;
  915. }
  916.  
  917. /////////////////////////////////////////////////////////////////////////////
  918. // Handle control notifications
  919.  
  920. AFX_STATIC_DATA const NotifyInfo _afxNotifyList[] = {
  921.     { _T("edit"),       EN_CHANGE },
  922.     { _T("button"),     BN_CLICKED },
  923.     { _T("button"),     BN_DOUBLECLICKED },
  924.     { _T("combobox"),   CBN_EDITCHANGE },
  925.     { _T("combobox"),   CBN_SELCHANGE },
  926.     { _T("listbox"),    LBN_SELCHANGE },
  927. };
  928.  
  929. AFX_STATIC_DATA const NotifyInfo _afxUpdateList[] = {
  930.     { _T("edit"),       EN_KILLFOCUS },
  931.     { _T("button"),     BN_CLICKED },
  932.     { _T("button"),     BN_DOUBLECLICKED },
  933.     { _T("combobox"),   CBN_SELCHANGE },
  934.     { _T("combobox"),   CBN_KILLFOCUS },
  935.     { _T("listbox"),    LBN_SELCHANGE },
  936. };
  937.  
  938. BOOL AFXAPI _AfxIsRadioButton(HWND hWnd)
  939. {
  940.     DWORD dwButtonStyle = GetWindowLong(hWnd, GWL_STYLE) & 0x0000000FL;
  941.     return ((dwButtonStyle == BS_RADIOBUTTON) ||
  942.             (dwButtonStyle == BS_AUTORADIOBUTTON));
  943. }
  944.  
  945. BOOL COlePropertyPage::OnCommand(WPARAM wParam, LPARAM lParam)
  946. {
  947.     // Let the base class process the message first
  948.     BOOL bSuccess = CDialog::OnCommand(wParam, lParam);
  949.  
  950.     // Are we just initializing the dialog box, or do we have no objects?
  951.     if (m_bInitializing || m_ppDisp == NULL)
  952.         return bSuccess;
  953.  
  954.     UINT nID = (UINT)LOWORD(wParam);
  955.     HWND hWndCtl = (HWND)lParam;
  956.     WORD wNotifyCode = HIWORD(wParam);
  957.  
  958.     DWORD flags = 0;
  959.  
  960.     if (hWndCtl != NULL)
  961.     {
  962.         BOOL bIgnoreControl = FALSE;
  963.         for (int count = 0; count < m_IDArray.GetSize(); count++)
  964.         {
  965.             UINT nNotID = m_IDArray.GetAt(count);
  966.             if (nID == nNotID)
  967.                 bIgnoreControl = TRUE;
  968.         }
  969.  
  970.         if ( !bIgnoreControl )
  971.         {
  972.             TCHAR szClassName[MAX_CLASS_NAME];
  973.  
  974.             // We have a control message - check type of control and message
  975.             ::GetClassName(hWndCtl, (LPTSTR)szClassName, MAX_CLASS_NAME);
  976.  
  977.             for (int iNotify = 0; iNotify < _countof(_afxNotifyList); iNotify++)
  978.             {
  979.                 if (lstrcmpi(_afxNotifyList[iNotify].szClassName, szClassName) == 0
  980.                  && _afxNotifyList[iNotify].wNotifyCode == wNotifyCode )
  981.                 {
  982.                     if ((lstrcmpi(szClassName, _T("button")) == 0) &&
  983.                         _AfxIsRadioButton(hWndCtl))
  984.                     {
  985.                         // Special case for radio buttons:
  986.                         // mark first button in group
  987.  
  988.                         while ((hWndCtl != NULL) &&
  989.                                 !(GetWindowLong(hWndCtl, GWL_STYLE) & WS_GROUP))
  990.                         {
  991.                             hWndCtl = ::GetWindow(hWndCtl, GW_HWNDPREV);
  992.                         }
  993.  
  994.                         // First button in group must have WS_GROUP style,
  995.                         // and must be a radio button.
  996.                         ASSERT(hWndCtl != NULL);
  997.                         ASSERT(_AfxIsRadioButton(hWndCtl));
  998.  
  999.                         // Mark first radio button as dirty
  1000.                         if (hWndCtl != NULL)
  1001.                             nID = ::GetWindowLong(hWndCtl, GWL_ID);
  1002.                     }
  1003.  
  1004.                     // Control has been modified
  1005.                     m_bDirty = TRUE;
  1006.                     SetControlStatus(nID, TRUE);
  1007.  
  1008.                     flags = PROPPAGESTATUS_DIRTY;
  1009.                     break;
  1010.                 }
  1011.             }
  1012.  
  1013.             if (m_bDirty)
  1014.             {
  1015.                 for (int iNotify=0; iNotify < _countof(_afxUpdateList); iNotify++)
  1016.                 {
  1017.                     if (lstrcmpi(_afxUpdateList[iNotify].szClassName, szClassName)==0 &&
  1018.                         _afxUpdateList[iNotify].wNotifyCode == wNotifyCode &&
  1019.                         GetControlStatus(nID))
  1020.                     {
  1021.                         flags |= PROPPAGESTATUS_VALIDATE;
  1022.                     }
  1023.                 }
  1024.             }
  1025.         }
  1026.     }
  1027.  
  1028.     if (flags != 0)
  1029.     {
  1030.         ASSERT(m_pPageSite != NULL);
  1031.         m_pPageSite->OnStatusChange(flags);
  1032.     }
  1033.  
  1034.     return bSuccess;
  1035. }
  1036.  
  1037. void COlePropertyPage::IgnoreApply(UINT nID)
  1038. {
  1039.     m_IDArray.Add(nID);
  1040. }
  1041.  
  1042. BOOL COlePropertyPage::GetControlStatus(UINT nID)
  1043. {
  1044.     for (int nControl = 0; nControl < m_nControls; nControl++)
  1045.         if (m_pStatus[nControl].nID == nID)
  1046.             return m_pStatus[nControl].bDirty;
  1047.  
  1048.     // If we couldn't find the control - assume it is dirty
  1049.     return TRUE;
  1050. }
  1051.  
  1052. BOOL COlePropertyPage::SetControlStatus(UINT nID, BOOL bDirty)
  1053. {
  1054.     for (int nControl = 0; nControl < m_nControls; nControl++)
  1055.         if (m_pStatus[nControl].nID == nID)
  1056.         {
  1057.             m_pStatus[nControl].bDirty = bDirty;
  1058.             return TRUE;
  1059.         }
  1060.     return FALSE;
  1061. }
  1062.  
  1063. //////////////////////////////////////////////////////////////////////////////
  1064. // Function Templates using the Preprocessor
  1065. // [This should be replaced with C++ Templates when the 16 bit compiler allows]
  1066.  
  1067. #define DEFINE_GET_SET_PROP(ctype,casttype,vttype) \
  1068. BOOL COlePropertyPage::SetPropText(LPCTSTR pszPropName, ctype& data ) \
  1069. { \
  1070.     USES_CONVERSION;\
  1071.     COleDispatchDriver PropDispDriver; \
  1072.     BOOL bResult = FALSE; \
  1073.     for (ULONG i = 0; i < m_nObjects; i++) \
  1074.     { \
  1075.         DISPID dwDispID; \
  1076.         LPCOLESTR lpOleStr = T2COLE(pszPropName);\
  1077.         if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID))) \
  1078.         { \
  1079.             PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE); \
  1080.             PropDispDriver.SetProperty(dwDispID, vttype, (casttype)data ); \
  1081.             PropDispDriver.DetachDispatch(); \
  1082.             bResult = TRUE; \
  1083.         } \
  1084.     } \
  1085.     return bResult; \
  1086. } \
  1087. BOOL COlePropertyPage::GetPropText(LPCTSTR pszPropName, ctype *data ) \
  1088. { \
  1089.     USES_CONVERSION;\
  1090.     COleDispatchDriver PropDispDriver; \
  1091.     BOOL bSuccess = FALSE; \
  1092.     for (ULONG i = 0; i < m_nObjects; i++) \
  1093.     { \
  1094.         DISPID dwDispID; \
  1095.         LPCOLESTR lpOleStr = T2COLE(pszPropName);\
  1096.         if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID))) \
  1097.         { \
  1098.             ctype dataTemp; \
  1099.             static ctype fill; \
  1100.             PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE); \
  1101.             PropDispDriver.GetProperty(dwDispID, vttype, &dataTemp); \
  1102.             PropDispDriver.DetachDispatch(); \
  1103.             if (i == 0) *data = dataTemp; \
  1104.             if (*data != dataTemp) *data = fill; \
  1105.             bSuccess = TRUE; \
  1106.         } \
  1107.     } \
  1108.     return bSuccess; \
  1109. }
  1110.  
  1111. /////////////////////////////////////////////////////////////////////////////
  1112. // DDP_ property get/set helpers
  1113.  
  1114. DEFINE_GET_SET_PROP( BYTE, BYTE, VT_UI1 );
  1115. DEFINE_GET_SET_PROP( short, short, VT_I2 );
  1116. DEFINE_GET_SET_PROP( int, int, VT_I4 );
  1117. DEFINE_GET_SET_PROP( UINT, UINT, VT_I4 );
  1118. DEFINE_GET_SET_PROP( long, long, VT_I4 );
  1119. DEFINE_GET_SET_PROP( DWORD, DWORD, VT_I4 );
  1120. DEFINE_GET_SET_PROP( float, float, VT_R4 );
  1121. DEFINE_GET_SET_PROP( double, double, VT_R8 );
  1122. DEFINE_GET_SET_PROP( CString, LPCTSTR, VT_BSTR );
  1123.  
  1124. BOOL COlePropertyPage::SetPropCheck(LPCTSTR pszPropName, int Value)
  1125. {
  1126.     USES_CONVERSION;
  1127.  
  1128.     COleDispatchDriver PropDispDriver;
  1129.     BOOL bResult = FALSE;
  1130.     BOOL bValue;
  1131.  
  1132.     if (Value == 1)
  1133.         bValue = TRUE;
  1134.     else
  1135.         bValue = FALSE;         // default to off
  1136.  
  1137.     // Set the properties for all the objects
  1138.     for (ULONG i = 0; i < m_nObjects; i++)
  1139.     {
  1140.         DISPID dwDispID;
  1141.  
  1142.         // Get the Dispatch ID for the property and if successful set the value of the property
  1143.         LPCOLESTR lpOleStr = T2COLE(pszPropName);
  1144.         if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
  1145.         {
  1146.             // Set property
  1147.             PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
  1148.             PropDispDriver.SetProperty(dwDispID, VT_BOOL, bValue);
  1149.             PropDispDriver.DetachDispatch();
  1150.             bResult = TRUE;
  1151.         }
  1152.     }
  1153.     return bResult;
  1154. }
  1155.  
  1156. BOOL COlePropertyPage::GetPropCheck(LPCTSTR pszPropName, int* pValue)
  1157. {
  1158.     USES_CONVERSION;
  1159.  
  1160.     COleDispatchDriver PropDispDriver;
  1161.     BOOL bSuccess = FALSE;
  1162.  
  1163.     // Check the property values for all the objects
  1164.     for (ULONG i = 0; i < m_nObjects; i++)
  1165.     {
  1166.         DISPID dwDispID;
  1167.  
  1168.         // Get the Dispatch ID for the property and if successful get the value of the property
  1169.         LPCOLESTR lpOleStr = T2COLE(pszPropName);
  1170.         if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
  1171.         {
  1172.             // Get property
  1173.             BOOL bTemp = FALSE;
  1174.             int tempValue;
  1175.  
  1176.             PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
  1177.             PropDispDriver.GetProperty(dwDispID, VT_BOOL, &bTemp);
  1178.             PropDispDriver.DetachDispatch();
  1179.  
  1180.             // Convert boolean value to check box equivalent
  1181.             if (bTemp)
  1182.                 tempValue = 1;
  1183.             else
  1184.                 tempValue = 0;
  1185.  
  1186.             // Handle special case for first object
  1187.             if (i == 0)
  1188.                 *pValue = tempValue;
  1189.  
  1190.             // If the current check value is not the same as the one just retrieved then
  1191.             // set the current check value to the indeterminate state.
  1192.             if (tempValue != *pValue)
  1193.                 *pValue = 2;
  1194.  
  1195.             bSuccess = TRUE;
  1196.         }
  1197.     }
  1198.     return bSuccess;
  1199. }
  1200.  
  1201. BOOL COlePropertyPage::SetPropRadio(LPCTSTR pszPropName, int Value)
  1202. {
  1203.     USES_CONVERSION;
  1204.  
  1205.     COleDispatchDriver PropDispDriver;
  1206.     BOOL bSuccess = FALSE;
  1207.  
  1208.     // Set the properties for all the objects
  1209.     for (ULONG i = 0; i < m_nObjects; i++)
  1210.     {
  1211.         DISPID dwDispID;
  1212.  
  1213.         // Get the Dispatch ID for the property and if successful set the value of the property
  1214.         LPCOLESTR lpOleStr = T2COLE(pszPropName);
  1215.         if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
  1216.         {
  1217.             short nTemp = (short)Value;
  1218.  
  1219.             // Set property
  1220.             PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
  1221.             PropDispDriver.SetProperty(dwDispID, VT_I2, nTemp);
  1222.             PropDispDriver.DetachDispatch();
  1223.             bSuccess = TRUE;
  1224.         }
  1225.     }
  1226.     return bSuccess;
  1227. }
  1228.  
  1229. BOOL COlePropertyPage::GetPropRadio(LPCTSTR pszPropName, int* pValue)
  1230. {
  1231.     USES_CONVERSION;
  1232.  
  1233.     COleDispatchDriver PropDispDriver;
  1234.     BOOL bSuccess = FALSE;
  1235.  
  1236.     // Check the property values for all the objects
  1237.     for (ULONG i = 0; i < m_nObjects; i++)
  1238.     {
  1239.         DISPID dwDispID;
  1240.  
  1241.         // Get the Dispatch ID for the property and if successful get the value of the property
  1242.         LPCOLESTR lpOleStr = T2COLE(pszPropName);
  1243.         if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
  1244.         {
  1245.             short nTemp;
  1246.  
  1247.             // Get property
  1248.             PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
  1249.             PropDispDriver.GetProperty(dwDispID, VT_I2, &nTemp);
  1250.             PropDispDriver.DetachDispatch();
  1251.  
  1252.             // Handle special case for first object
  1253.             if (i == 0)
  1254.                 *pValue = nTemp;
  1255.  
  1256.             // Compare the current radio value with the one just retrieved then
  1257.             // if they are different then set the radio value to -1, so that no
  1258.             // radio buttons will be checked.
  1259.             if (nTemp != *pValue)
  1260.                 *pValue = -1;
  1261.  
  1262.             bSuccess = TRUE;
  1263.         }
  1264.     }
  1265.     return bSuccess;
  1266. }
  1267.  
  1268. BOOL COlePropertyPage::SetPropIndex(LPCTSTR pszPropName, int Value)
  1269. {
  1270.     return SetPropRadio(pszPropName, Value);
  1271. }
  1272.  
  1273. BOOL COlePropertyPage::GetPropIndex(LPCTSTR pszPropName, int* pValue)
  1274. {
  1275.     return GetPropRadio(pszPropName, pValue);
  1276. }
  1277.  
  1278. /////////////////////////////////////////////////////////////////////////////
  1279. // DDP_Begin data exchange routines (Should be C++ templated someday!)
  1280.  
  1281. #define DEFINE_DDP_(group,ctype,vtype,bEditCtrl) \
  1282. void AFXAPI DDP_End##group(CDataExchange* pDX, int, \
  1283.                            ctype *member, LPCTSTR pszPropName ) \
  1284. { \
  1285.     COlePropertyPage* propDialog = STATIC_DOWNCAST(COlePropertyPage, pDX->m_pDlgWnd); \
  1286.     if (pDX->m_bSaveAndValidate) \
  1287.         propDialog->SetProp##group(pszPropName, *member ); \
  1288. } \
  1289. void AFXAPI DDP_##group(CDataExchange* pDX, int nCtrlId, \
  1290.                         ctype &member, LPCTSTR pszPropName ) \
  1291. { \
  1292.     ASSERT(AfxIsValidString(pszPropName)); \
  1293.     COlePropertyPage* propDialog = STATIC_DOWNCAST(COlePropertyPage, pDX->m_pDlgWnd); \
  1294.     if (pDX->m_bSaveAndValidate) /*Are we Saving?*/ \
  1295.     { \
  1296.         if (propDialog->GetControlStatus(nCtrlId)) /*Is Control Dirty?*/ \
  1297.         { \
  1298.             void (AFXAPI *pfv)(CDataExchange*,int,ctype*,LPCTSTR) = \
  1299.                  DDP_End##group; \
  1300.             AFX_DDPDATA *pDDP = new AFX_DDPDATA( (void *)pfv, nCtrlId, bEditCtrl, \
  1301.                                                  (void*)&member, (UINT)vtype, \
  1302.                                                  pszPropName ); \
  1303.             propDialog->m_arrayDDP.Add(pDDP); \
  1304.         } \
  1305.     } \
  1306.     else /* Loading data from properties! */ \
  1307.     { \
  1308.             propDialog->GetProp##group(pszPropName, &member); \
  1309.             propDialog->SetControlStatus(nCtrlId,FALSE); \
  1310.     } \
  1311. }
  1312.  
  1313. /////////////////////////////////////////////////////////////////////////////
  1314. // DDP Functions (Pseudo Template Generation)
  1315.  
  1316. DEFINE_DDP_(Text,BYTE,VT_UI1,TRUE);
  1317. DEFINE_DDP_(Text,short,VT_I2,TRUE);
  1318. DEFINE_DDP_(Text,int,VT_I4,TRUE);
  1319. DEFINE_DDP_(Text,UINT,VT_UI4,TRUE);
  1320. DEFINE_DDP_(Text,long,VT_I4,TRUE);
  1321. DEFINE_DDP_(Text,DWORD,VT_UI4,TRUE);
  1322. DEFINE_DDP_(Text,float,VT_R4,TRUE);
  1323. DEFINE_DDP_(Text,double,VT_R8,TRUE);
  1324. DEFINE_DDP_(Text,CString,VT_BSTR,TRUE);
  1325. DEFINE_DDP_(Check,BOOL,VT_I4,FALSE);
  1326. DEFINE_DDP_(Radio,int,VT_I4,FALSE);
  1327.  
  1328. //////////////////////////////////////////////////////////////////////////////
  1329. // DDP Deferred Property Write Handler
  1330.  
  1331. void AFXAPI DDP_PostProcessing(CDataExchange*pDX)
  1332. {
  1333.     if (pDX->m_bSaveAndValidate)
  1334.     {
  1335.         CPtrArray &arrayDDP =
  1336.             ((COlePropertyPage *)pDX->m_pDlgWnd)->m_arrayDDP;
  1337.         AFX_DDPDATA *pDDP = NULL;
  1338.         int cDDP = arrayDDP.GetSize();
  1339.         for (int i = 0 ; i < cDDP; i++)
  1340.         {
  1341.             pDDP = (AFX_DDPDATA*)arrayDDP[i];
  1342.             TRY
  1343.             {
  1344.                 if (pDDP->m_bEditCtrl)
  1345.                     pDX->PrepareEditCtrl(pDDP->m_nCtrlId);
  1346.                 else
  1347.                     pDX->PrepareCtrl(pDDP->m_nCtrlId);
  1348.  
  1349.                 switch( pDDP->m_nType )
  1350.                 {
  1351.                     case    VT_I1:
  1352.                     {
  1353.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1354.                                                    char *, LPCTSTR );
  1355.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1356.                                                   (char *)pDDP->m_lpMember,
  1357.                                                   pDDP->m_lpszOleName );
  1358.                         break;
  1359.                     }
  1360.                     case    VT_UI1:
  1361.                     {
  1362.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1363.                                                    BYTE *, LPCTSTR );
  1364.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1365.                                                   (BYTE *)pDDP->m_lpMember,
  1366.                                                   pDDP->m_lpszOleName );
  1367.  
  1368.                         break;
  1369.                     }
  1370.                     case    VT_I2:
  1371.                     {
  1372.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1373.                                                    short *, LPCTSTR );
  1374.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1375.                                                   (short *)pDDP->m_lpMember,
  1376.                                                   pDDP->m_lpszOleName );
  1377.                         break;
  1378.                     }
  1379.                     case    VT_UI2:
  1380.                     {
  1381.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1382.                                                    WORD *, LPCTSTR );
  1383.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1384.                                                   (WORD *)pDDP->m_lpMember,
  1385.                                                   pDDP->m_lpszOleName );
  1386.                         break;
  1387.                     }
  1388.                     case    VT_I4:
  1389.                     {
  1390.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1391.                                                    long *, LPCTSTR );
  1392.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1393.                                                   (long *)pDDP->m_lpMember,
  1394.                                                   pDDP->m_lpszOleName );
  1395.                         break;
  1396.                     }
  1397.                     case    VT_UI4:
  1398.                     {
  1399.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1400.                                                    DWORD *, LPCTSTR );
  1401.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1402.                                                   (DWORD *)pDDP->m_lpMember,
  1403.                                                   pDDP->m_lpszOleName);
  1404.                         break;
  1405.                     }
  1406.                     case    VT_R4:
  1407.                     {
  1408.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1409.                                                    float *, LPCTSTR );
  1410.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1411.                                                   (float *)pDDP->m_lpMember,
  1412.                                                   pDDP->m_lpszOleName);
  1413.                         break;
  1414.                     }
  1415.                     case    VT_R8:
  1416.                     {
  1417.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1418.                                                    double *, LPCTSTR );
  1419.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1420.                                                   (double *)pDDP->m_lpMember,
  1421.                                                   pDDP->m_lpszOleName );
  1422.                         break;
  1423.                     }
  1424.                     case    VT_BSTR:
  1425.                     {
  1426.                         typedef void (AFXAPI *PFV)(CDataExchange *, int,
  1427.                                                    CString *, LPCTSTR );
  1428.                         (*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
  1429.                                                   (CString *)pDDP->m_lpMember,
  1430.                                                   pDDP->m_lpszOleName );
  1431.                         break;
  1432.                     }
  1433.                     default:
  1434.                         // Unknown Data Type!
  1435.                         ASSERT(FALSE);
  1436.                         break;
  1437.                 }
  1438.             }
  1439.             CATCH(COleDispatchException, e)
  1440.             {
  1441.                 // Dleanup before pDX->Fail() throws exception.
  1442.                 _AfxCleanupDDPs(arrayDDP);
  1443.  
  1444.                 // Display message box for dispatch exceptions.
  1445.                 COlePropertyPage* pPropPage = (COlePropertyPage*)pDX->m_pDlgWnd;
  1446.                 pPropPage->MessageBox((LPCTSTR)e->m_strDescription, NULL,
  1447.                     MB_ICONEXCLAMATION | MB_OK);
  1448.                 DELETE_EXCEPTION(e);
  1449.                 pDX->Fail();
  1450.             }
  1451.             AND_CATCH_ALL(e)
  1452.             {
  1453.                 // Ignore other exceptions.
  1454.                 DELETE_EXCEPTION(e);
  1455.             }
  1456.             END_CATCH_ALL
  1457.         }
  1458.  
  1459.         _AfxCleanupDDPs(arrayDDP);
  1460.     }
  1461. }
  1462.  
  1463. /////////////////////////////////////////////////////////////////////////////
  1464. // Force any extra compiler-generated code into AFX_INIT_SEG
  1465.  
  1466. #ifdef AFX_INIT_SEG
  1467. #pragma code_seg(AFX_INIT_SEG)
  1468. #endif
  1469.  
  1470. IMPLEMENT_DYNAMIC(COlePropertyPage, CDialog)
  1471.