home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / CTLPPG.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-03  |  37.6 KB  |  1,479 lines

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