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 >
Wrap
C/C++ Source or Header
|
1998-06-16
|
39KB
|
1,471 lines
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#ifdef AFXCTL_PAGE_SEG
#pragma code_seg(AFXCTL_PAGE_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
#define MAX_CLASS_NAME 100
struct NotifyInfo
{
LPCTSTR szClassName; // Name of control's class
WORD wNotifyCode; // Notification code
};
#define MAX_TEXT 1024
BEGIN_INTERFACE_MAP(COlePropertyPage, CDialog)
INTERFACE_PART(COlePropertyPage, IID_IPropertyPage2, PropertyPage)
INTERFACE_PART(COlePropertyPage, IID_IPropertyPage, PropertyPage)
END_INTERFACE_MAP()
BEGIN_MESSAGE_MAP(COlePropertyPage, CDialog)
//{{AFX_MSG_MAP(COlePropertyPage)
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
#ifndef _AFX_NO_CTL3D_SUPPORT
ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)
#endif
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// AFX_DDPDATA
struct AFX_DDPDATA : public CObject
{
AFX_DDPDATA(LPVOID lpHandler, int nCtrlId, BOOL bEditCtrl, LPVOID lpMember,
UINT nType, LPCTSTR lpszOleName);
LPVOID m_lpHandler;
int m_nCtrlId;
BOOL m_bEditCtrl;
LPVOID m_lpMember;
UINT m_nType;
LPCTSTR m_lpszOleName;
};
AFX_DDPDATA::AFX_DDPDATA(LPVOID lpHandler, int nCtrlId, BOOL bEditCtrl,
LPVOID lpMember, UINT nType, LPCTSTR lpszOleName) :
m_lpHandler(lpHandler),
m_nCtrlId(nCtrlId),
m_bEditCtrl(bEditCtrl),
m_lpMember(lpMember),
m_nType(nType),
m_lpszOleName(lpszOleName)
{
}
/////////////////////////////////////////////////////////////////////////////
// cleanup helper function
AFX_STATIC void AFXAPI _AfxCleanupDDPs(CPtrArray& arrayDDP)
{
int cDDP = arrayDDP.GetSize();
for (int i = 0; i < cDDP; i++)
{
ASSERT(arrayDDP[i] != NULL);
delete (AFX_DDPDATA*)(arrayDDP[i]);
}
arrayDDP.RemoveAll();
}
/////////////////////////////////////////////////////////////////////////////
// page validation helper function (debug only)
#ifdef _DEBUG
void _ValidatePageDialog(CDialogTemplate& dt, CString& strPage,
COlePropertyPage* pPage)
{
if (GetSystemMetrics(SM_DBCSENABLED))
return;
// Only display the message boxes the first time a page is shown.
#ifdef _DEBUG
BOOL bEnable = AfxEnableMemoryTracking(FALSE);
#endif
static CPtrList _classList;
BOOL bMessageBox = FALSE;
CRuntimeClass* pClass = pPage->GetRuntimeClass();
if (_classList.Find(pClass) == NULL)
{
bMessageBox = TRUE;
_classList.AddHead(pClass);
}
#ifdef _DEBUG
AfxEnableMemoryTracking(bEnable);
#endif
SIZE sizeDLU;
CString strFontName;
WORD wFontSize;
LPTSTR pszTmp;
dt.GetSizeInDialogUnits(&sizeDLU);
dt.GetFont(strFontName, wFontSize);
if ((sizeDLU.cx != 250) || ((sizeDLU.cy != 62) && (sizeDLU.cy != 110)))
{
pszTmp = new TCHAR[strPage.GetLength() + 128];
wsprintf(pszTmp, _T("Property page '%s' has nonstandard dimensions."),
(LPCTSTR)strPage);
TRACE1("Warning: %s\n", pszTmp);
if (bMessageBox)
{
lstrcat(pszTmp,
_T(" Dimensions should be 250x62 or 250x110 dialog units."));
pPage->MessageBox(pszTmp, _T("Property page warning"),
MB_ICONEXCLAMATION);
}
delete [] pszTmp;
}
if ((wFontSize != 8) ||
(strFontName != _T("MS Sans Serif") && strFontName != _T("Helv") &&
strFontName != _T("MS Shell Dlg")))
{
pszTmp = new TCHAR[strPage.GetLength() + 128];
wsprintf(pszTmp, _T("Property page '%s' uses a nonstandard font."),
(LPCTSTR)strPage);
TRACE1("Warning: %s\n", pszTmp);
if (bMessageBox)
{
lstrcat(pszTmp, _T(" Font should be MS Sans Serif 8."));
pPage->MessageBox(pszTmp, _T("Property page warning"),
MB_ICONEXCLAMATION);
}
delete [] pszTmp;
}
}
#endif // _DEBUG
/////////////////////////////////////////////////////////////////////////////
// COlePropertyPage::COlePropertyPage
COlePropertyPage::COlePropertyPage(UINT idDlg, UINT idCaption) :
#ifdef _DEBUG
m_bNonStandardSize(FALSE),
#endif
m_bDirty(FALSE),
m_idCaption(idCaption),
m_idDlg(idDlg),
m_pPageSite(NULL),
m_ppDisp(NULL),
m_pAdvisors(NULL),
m_bPropsChanged(FALSE),
m_nObjects(0),
m_bInitializing(TRUE),
m_nControls(0),
m_pStatus(NULL),
m_hDialog(NULL),
m_dwHelpContext(0)
{
// m_lpDialogTemplate is needed later by CWnd::ExecuteDlgInit
m_lpszTemplateName = MAKEINTRESOURCE(m_idDlg);
// Keep this DLL loaded at least until this object is deleted.
AfxOleLockApp();
}
void COlePropertyPage::OnSetPageSite()
{
// Load the caption from resources
CString strCaption;
if (!strCaption.LoadString(m_idCaption))
strCaption.LoadString(AFX_IDS_PROPPAGE_UNKNOWN);
SetPageName(strCaption);
// Try to load the dialog resource template into memory and get its size
m_sizePage.cx = 0;
m_sizePage.cy = 0;
CDialogTemplate dt;
dt.Load(MAKEINTRESOURCE(m_idDlg));
#ifdef _DEBUG
if (!m_bNonStandardSize)
_ValidatePageDialog(dt, strCaption, this);
#endif
// If no font specified, set the system font.
BOOL bSetSysFont = !dt.HasFont();
CString strFace;
WORD wSize;
// On DBCS systems, also change "MS Sans Serif" or "Helv" to system font.
if ((!bSetSysFont) && GetSystemMetrics(SM_DBCSENABLED))
{
CString strFace;
dt.GetFont(strFace, wSize);
bSetSysFont = (strFace == _T("MS Shell Dlg") ||
strFace == _T("MS Sans Serif") || strFace == _T("Helv"));
}
// Here is where we actually set the font.
if (bSetSysFont && AfxGetPropSheetFont(strFace, wSize, FALSE))
dt.SetFont(strFace, wSize);
dt.GetSizeInPixels(&m_sizePage);
m_hDialog = dt.Detach();
}
BOOL COlePropertyPage::OnInitDialog()
{
CDialog::OnInitDialog();
return 0;
}
BOOL COlePropertyPage::PreTranslateMessage(LPMSG lpMsg)
{
// Don't let CDialog process the Return key or Escape key.
if ((lpMsg->message == WM_KEYDOWN) &&
((lpMsg->wParam == VK_RETURN) || (lpMsg->wParam == VK_ESCAPE)))
{
// Special case: if control with focus is an edit control with
// ES_WANTRETURN style, let it handle the Return key.
TCHAR szClass[10];
CWnd* pWndFocus = GetFocus();
if ((lpMsg->wParam == VK_RETURN) &&
((pWndFocus = GetFocus()) != NULL) &&
IsChild(pWndFocus) &&
(pWndFocus->GetStyle() & ES_WANTRETURN) &&
GetClassName(pWndFocus->m_hWnd, szClass, 10) &&
(lstrcmpi(szClass, _T("EDIT")) == 0))
{
pWndFocus->SendMessage(WM_CHAR, lpMsg->wParam, lpMsg->lParam);
return TRUE;
}
return FALSE;
}
// If it's a WM_SYSKEYDOWN, temporarily replace the hwnd in the
// message with the hwnd of our first control, and try to handle
// the message for ourselves.
BOOL bHandled;
if ((lpMsg->message == WM_SYSKEYDOWN) && !::IsChild(m_hWnd, lpMsg->hwnd))
{
HWND hWndSave = lpMsg->hwnd;
lpMsg->hwnd = ::GetWindow(m_hWnd, GW_CHILD);
bHandled = CDialog::PreTranslateMessage(lpMsg);
lpMsg->hwnd = hWndSave;
}
else
{
bHandled = CDialog::PreTranslateMessage(lpMsg);
}
return bHandled;
}
void COlePropertyPage::CleanupObjectArray()
{
if (m_pAdvisors)
{
for (ULONG nObject = 0; nObject < m_nObjects; nObject++)
AfxConnectionUnadvise(m_ppDisp[nObject], IID_IPropertyNotifySink,
&m_xPropNotifySink, FALSE, m_pAdvisors[nObject]);
delete [] m_pAdvisors;
m_pAdvisors = NULL;
}
if (m_ppDisp)
{
for (ULONG nObject = 0; nObject < m_nObjects; nObject++)
RELEASE(m_ppDisp[nObject]);
delete [] m_ppDisp;
m_ppDisp = NULL;
}
}
COlePropertyPage::~COlePropertyPage()
{
// Remember to free the resource we loaded!
if (m_hDialog != NULL)
GlobalFree(m_hDialog);
if (m_pStatus != NULL)
{
delete [] m_pStatus;
m_pStatus = NULL;
}
// Clean out any leftovers in the DDP map.
_AfxCleanupDDPs(m_arrayDDP);
RELEASE(m_pPageSite);
CleanupObjectArray();
AfxOleUnlockApp();
}
void COlePropertyPage::OnFinalRelease()
{
if (m_hWnd != NULL)
DestroyWindow();
delete this;
}
LRESULT COlePropertyPage::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
AFX_MANAGE_STATE(m_pModuleState);
// Forward WM_SYSCOMMAND messages to the frame for translation
if (msg == WM_SYSCOMMAND && (wParam & 0xFFF0) != SC_KEYMENU && m_pPageSite != NULL)
{
if (m_pPageSite->TranslateAccelerator((LPMSG)GetCurrentMessage())
== S_OK)
{
return 0;
}
}
return CDialog::WindowProc(msg, wParam, lParam);
}
HBRUSH COlePropertyPage::OnCtlColor(CDC*, CWnd* pWnd, UINT)
{
// allow the control itself first crack at the color
LRESULT lResult;
if (pWnd->SendChildNotifyLastMsg(&lResult))
return (HBRUSH)lResult;
// allow the parent window to determine the color
HWND hParent = ::GetParent(m_hWnd);
const MSG* pMsg = GetCurrentMessage();
HBRUSH hResult = (HBRUSH)::SendMessage(hParent,
pMsg->message, pMsg->wParam, pMsg->lParam);
// use default if parent returns NULL
if (hResult == NULL)
hResult = (HBRUSH)Default();
return hResult;
}
BOOL COlePropertyPage::OnHelp(LPCTSTR)
{
// May be overridden by subclass.
return FALSE;
}
BOOL COlePropertyPage::OnEditProperty(DISPID)
{
// May be overridden by subclass.
return FALSE;
}
void COlePropertyPage::OnObjectsChanged()
{
// May be overridden by subclass.
}
LPDISPATCH* COlePropertyPage::GetObjectArray(ULONG* pnObjects)
{
ASSERT_POINTER(pnObjects, ULONG);
if (pnObjects != NULL)
*pnObjects = m_nObjects;
return m_ppDisp;
}
void COlePropertyPage::SetModifiedFlag(BOOL bModified)
{
if (!bModified)
m_bPropsChanged = FALSE;
if ((m_bDirty && !bModified) || (!m_bDirty && bModified))
{
m_bDirty = bModified;
if (m_pPageSite != NULL)
{
DWORD flags = 0;
if (bModified)
flags |= PROPPAGESTATUS_DIRTY;
m_pPageSite->OnStatusChange(flags);
}
}
}
BOOL COlePropertyPage::IsModified()
{
return m_bDirty;
}
void COlePropertyPage::SetPageName(LPCTSTR lpszPageName)
{
ASSERT(AfxIsValidString(lpszPageName));
m_strPageName = lpszPageName;
}
void COlePropertyPage::SetDialogResource(HGLOBAL hDialog)
{
if (m_hDialog != NULL)
{
GlobalFree(m_hDialog);
m_hDialog = NULL;
}
CDialogTemplate dt(hDialog);
#ifdef _DEBUG
_ValidatePageDialog(dt, m_strPageName, this);
#endif
dt.GetSizeInPixels(&m_sizePage);
m_hDialog = dt.Detach();
}
void COlePropertyPage::SetHelpInfo(LPCTSTR lpszDocString,
LPCTSTR lpszHelpFile, DWORD dwHelpContext)
{
ASSERT((lpszDocString == NULL) || AfxIsValidString(lpszDocString));
ASSERT((lpszHelpFile == NULL) || AfxIsValidString(lpszHelpFile));
m_strDocString = lpszDocString;
m_strHelpFile = lpszHelpFile;
m_dwHelpContext = dwHelpContext;
}
LPPROPERTYPAGESITE COlePropertyPage::GetPageSite()
{
return m_pPageSite;
}
int COlePropertyPage::MessageBox(LPCTSTR lpszText, LPCTSTR lpszCaption,
UINT nType)
{
// use caption of page by default
if (lpszCaption == NULL)
lpszCaption = m_strPageName;
// start message box on safe owner of the page
return ::MessageBox(GetSafeOwner_(m_hWnd, NULL), lpszText, lpszCaption, nType);
}
/////////////////////////////////////////////////////////////////////////////
// COlePropertyPage::XPropertyPage
STDMETHODIMP_(ULONG) COlePropertyPage::XPropertyPage::AddRef()
{
METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
return (ULONG)pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) COlePropertyPage::XPropertyPage::Release()
{
METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
return (ULONG)pThis->ExternalRelease();
}
STDMETHODIMP COlePropertyPage::XPropertyPage::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP COlePropertyPage::XPropertyPage::SetPageSite(
LPPROPERTYPAGESITE pPageSite)
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
ASSERT_VALID(pThis);
ASSERT_POINTER(pPageSite, IPropertyPageSite);
RELEASE(pThis->m_pPageSite); // release the old one
pThis->m_pPageSite = pPageSite;
if (pPageSite != NULL)
pThis->m_pPageSite->AddRef(); // Bump the reference count
pThis->OnSetPageSite();
return S_OK;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::Activate(HWND hWndParent,
LPCRECT pRect, BOOL)
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
ASSERT_VALID(pThis);
ASSERT_NULL_OR_POINTER(pRect, RECT);
BOOL bSuccess = FALSE; // Did we successfully create the dialog box
if (pThis->m_hDialog != NULL)
{
// We've already loaded the dialog template into memory so just
// create it!
void* lpDialogTemplate = LockResource(pThis->m_hDialog);
if (lpDialogTemplate != NULL)
{
bSuccess = pThis->CreateIndirect(lpDialogTemplate, CWnd::FromHandle(hWndParent));
UnlockResource(pThis->m_hDialog);
}
else
bSuccess = pThis->Create(pThis->m_idDlg, CWnd::FromHandle(hWndParent));
}
else
bSuccess = pThis->Create(pThis->m_idDlg, CWnd::FromHandle(hWndParent));
// Were we successful in creating the dialog box!
if (bSuccess)
{
pThis->MoveWindow(pRect); // Force page to fill area given by frame *
pThis->m_bInitializing = TRUE;
pThis->UpdateData(FALSE);
pThis->SetModifiedFlag(FALSE);
pThis->m_bInitializing = FALSE;
if (pThis->m_pStatus != NULL)
{
delete [] pThis->m_pStatus;
pThis->m_pStatus = NULL;
}
pThis->m_nControls = 0;
::EnumChildWindows(pThis->GetSafeHwnd(), (WNDENUMPROC) COlePropertyPage::EnumChildProc, (LPARAM) pThis);
if (pThis->m_nControls > 0)
pThis->m_pStatus = new AFX_PPFIELDSTATUS [UINT(pThis->m_nControls)];
pThis->m_nControls = 0;
EnumChildWindows(pThis->GetSafeHwnd(), (WNDENUMPROC) COlePropertyPage::EnumControls, (LPARAM) pThis);
return S_OK;
}
return E_FAIL;
}
BOOL CALLBACK COlePropertyPage::EnumChildProc(HWND, LPARAM lParam)
{
COlePropertyPage* pDlg = (COlePropertyPage*) lParam;
ASSERT_POINTER(pDlg, COlePropertyPage);
pDlg->m_nControls++;
return TRUE;
}
BOOL CALLBACK COlePropertyPage::EnumControls(HWND hWnd, LPARAM lParam)
{
COlePropertyPage* pDlg = (COlePropertyPage*) lParam;
ASSERT_POINTER(pDlg, COlePropertyPage);
ASSERT(pDlg->m_pStatus != NULL);
pDlg->m_pStatus[pDlg->m_nControls].nID = (UINT)::GetDlgCtrlID(hWnd);
pDlg->m_pStatus[pDlg->m_nControls].bDirty = FALSE;
pDlg->m_nControls++;
return TRUE;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::Deactivate()
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
pThis->DestroyWindow();
return S_OK;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::GetPageInfo(
LPPROPPAGEINFO pPageInfo)
{
METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
ASSERT_POINTER(pPageInfo, PROPPAGEINFO);
pPageInfo->pszTitle = AfxAllocTaskOleString(pThis->m_strPageName);
pPageInfo->size = pThis->m_sizePage;
pPageInfo->pszDocString = AfxAllocTaskOleString(pThis->m_strDocString);
pPageInfo->pszHelpFile = AfxAllocTaskOleString(pThis->m_strHelpFile);
pPageInfo->dwHelpContext = pThis->m_dwHelpContext;
return S_OK;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::SetObjects(
ULONG cObjects, LPUNKNOWN* ppUnk)
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
ASSERT_VALID(pThis);
pThis->CleanupObjectArray();
if (cObjects != 0)
{
ASSERT(AfxIsValidAddress(ppUnk, sizeof(LPUNKNOWN) * (int)cObjects, FALSE));
pThis->m_ppDisp = new LPDISPATCH [(UINT)cObjects];
pThis->m_pAdvisors = new DWORD [(UINT)cObjects];
for (ULONG nObject = 0; nObject < cObjects; nObject++)
{
HRESULT hr = ppUnk[nObject]->QueryInterface(IID_IDispatch,
(VOID**)&(pThis->m_ppDisp[nObject]));
if (SUCCEEDED(hr))
{
AfxConnectionAdvise(ppUnk[nObject], IID_IPropertyNotifySink,
&pThis->m_xPropNotifySink, FALSE,
&pThis->m_pAdvisors[nObject]);
}
else
{
return hr;
}
}
}
pThis->m_nObjects = cObjects;
// No painting during the data update.
BOOL bLock = (pThis->m_hWnd != NULL) && pThis->IsWindowVisible();
if (bLock)
::LockWindowUpdate(pThis->m_hWnd);
pThis->OnObjectsChanged();
// If window exists, update the data in its fields.
if (cObjects != 0 && pThis->m_hWnd != NULL)
{
pThis->UpdateData(FALSE);
pThis->SetModifiedFlag(FALSE);
}
if (bLock)
::LockWindowUpdate(NULL);
return S_OK;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::Show(UINT nCmdShow)
{
METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
pThis->ShowWindow(nCmdShow);
if (nCmdShow == SW_SHOWNORMAL)
pThis->SetFocus();
return S_OK;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::Move(LPCRECT pRect)
{
METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
ASSERT_POINTER(pRect, RECT);
pThis->MoveWindow(pRect);
return S_OK;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::IsPageDirty()
{
METHOD_PROLOGUE_EX_(COlePropertyPage, PropertyPage)
return pThis->m_bDirty ? S_OK : S_FALSE;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::Apply()
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
ASSERT_VALID(pThis);
HRESULT hr = S_OK;
BOOL bClean = FALSE;
if (pThis->m_bDirty)
{
if (pThis->UpdateData(TRUE))
{
pThis->m_bDirty = FALSE;
bClean = TRUE;
}
else
hr = E_FAIL;
if (pThis->m_bPropsChanged)
{
pThis->UpdateData(FALSE);
pThis->m_bPropsChanged = FALSE;
bClean = TRUE;
}
if (bClean)
{
// Set the dirty status of all the controls on this page to FALSE.
if (pThis->m_pStatus != NULL)
{
for (int nControl = 0; nControl < pThis->m_nControls; nControl++)
pThis->m_pStatus[nControl].bDirty = FALSE;
}
}
}
else
ASSERT(!pThis->m_bPropsChanged);
return hr;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::Help(LPCOLESTR lpszHelpDir)
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
ASSERT_VALID(pThis);
ASSERT((lpszHelpDir == NULL) || AfxIsValidString(lpszHelpDir));
USES_CONVERSION;
if (!pThis->OnHelp(OLE2CT(lpszHelpDir)))
return S_FALSE;
else
return S_OK;
}
BOOL AFXAPI _AfxAtEndOfTabList(CDialog* pDlg, UINT nCmd)
{
if ((pDlg->SendMessage(WM_GETDLGCODE) &
(DLGC_WANTALLKEYS | DLGC_WANTMESSAGE | DLGC_WANTTAB)) == 0)
{
CWnd* pCtl = CWnd::GetFocus();
if (pDlg->IsChild(pCtl))
{
// Get top level child for controls with children, like combo.
while (pCtl->GetParent() != pDlg)
{
pCtl = pCtl->GetParent();
ASSERT_VALID(pCtl);
}
do
{
if ((pCtl = pCtl->GetWindow(nCmd)) == NULL)
return TRUE;
}
while ((pCtl->GetStyle() & (WS_DISABLED | WS_TABSTOP)) != WS_TABSTOP);
}
}
return FALSE;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::TranslateAccelerator(LPMSG lpMsg)
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
ASSERT_VALID(pThis);
ASSERT_POINTER(lpMsg, MSG);
BOOL bHandled = FALSE;
if (lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_TAB &&
GetKeyState(VK_CONTROL) >= 0)
{
if (pThis->IsChild(CWnd::GetFocus()))
{
// We already have the focus. Let's determine whether we should
// pass focus up to the frame.
if (_AfxAtEndOfTabList(pThis, GetKeyState(VK_SHIFT) < 0 ?
GW_HWNDPREV : GW_HWNDNEXT))
{
// fix for default button border
DWORD dwDefID = pThis->GetDefID();
if (HIWORD(dwDefID) == DC_HASDEFID)
{
CWnd *pDefBtn = pThis->GetDlgItem(LOWORD(dwDefID));
if (pDefBtn != NULL && pDefBtn->IsWindowEnabled())
pThis->GotoDlgCtrl(pDefBtn);
}
// Pass focus to the frame by letting the page site handle
// this message.
if (pThis->m_pPageSite != NULL)
bHandled =
pThis->m_pPageSite->TranslateAccelerator(lpMsg) ==
S_OK;
}
}
else
{
// We don't already have the focus. The frame is passing the
// focus to us.
CWnd* pWnd = pThis->GetTopWindow();
if (pWnd != NULL)
{
UINT gwInit;
UINT gwMove;
if (GetKeyState(VK_SHIFT) >= 0)
{
// Set the focus to the first tabstop in the page.
gwInit = GW_HWNDFIRST;
gwMove = GW_HWNDNEXT;
}
else
{
// Set the focus to the last tabstop in the page.
gwInit = GW_HWNDLAST;
gwMove = GW_HWNDPREV;
}
pWnd = pWnd->GetWindow(gwInit);
while (pWnd != NULL)
{
if ((pWnd->GetStyle() & (WS_DISABLED | WS_TABSTOP)) ==
WS_TABSTOP)
{
pThis->GotoDlgCtrl(pWnd);
bHandled = TRUE;
break;
}
pWnd = pWnd->GetWindow(gwMove);
}
}
}
}
// If message was not handled here, call PreTranslateMessage
return (bHandled || pThis->PreTranslateMessage(lpMsg)) ?
S_OK : S_FALSE;
}
STDMETHODIMP COlePropertyPage::XPropertyPage::EditProperty(DISPID dispid)
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropertyPage)
ASSERT_VALID(pThis);
return pThis->OnEditProperty(dispid) ? S_OK : E_NOTIMPL;
}
/////////////////////////////////////////////////////////////////////////////
// COlePropertyPage::XPropNotifySink
STDMETHODIMP_(ULONG) COlePropertyPage::XPropNotifySink::AddRef()
{
return 1;
}
STDMETHODIMP_(ULONG) COlePropertyPage::XPropNotifySink::Release()
{
return 0;
}
STDMETHODIMP COlePropertyPage::XPropNotifySink::QueryInterface(
REFIID iid, LPVOID* ppvObj)
{
if (IsEqualIID(iid, IID_IPropertyNotifySink) ||
IsEqualIID(iid, IID_IUnknown))
{
*ppvObj = this;
return S_OK;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP COlePropertyPage::XPropNotifySink::OnRequestEdit(DISPID)
{
return S_OK;
}
STDMETHODIMP COlePropertyPage::XPropNotifySink::OnChanged(DISPID)
{
METHOD_PROLOGUE_EX(COlePropertyPage, PropNotifySink)
// If we're not currently in the middle of an UpdateData, call it now.
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
if (pThis->m_hWnd != NULL &&
pThreadState->m_hLockoutNotifyWindow != pThis->m_hWnd)
{
pThis->UpdateData(FALSE);
}
else
{
pThis->m_bPropsChanged = TRUE;
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
// Handle control notifications
AFX_STATIC_DATA const NotifyInfo _afxNotifyList[] = {
{ _T("edit"), EN_CHANGE },
{ _T("button"), BN_CLICKED },
{ _T("button"), BN_DOUBLECLICKED },
{ _T("combobox"), CBN_EDITCHANGE },
{ _T("combobox"), CBN_SELCHANGE },
{ _T("listbox"), LBN_SELCHANGE },
};
AFX_STATIC_DATA const NotifyInfo _afxUpdateList[] = {
{ _T("edit"), EN_KILLFOCUS },
{ _T("button"), BN_CLICKED },
{ _T("button"), BN_DOUBLECLICKED },
{ _T("combobox"), CBN_SELCHANGE },
{ _T("combobox"), CBN_KILLFOCUS },
{ _T("listbox"), LBN_SELCHANGE },
};
BOOL AFXAPI _AfxIsRadioButton(HWND hWnd)
{
DWORD dwButtonStyle = GetWindowLong(hWnd, GWL_STYLE) & 0x0000000FL;
return ((dwButtonStyle == BS_RADIOBUTTON) ||
(dwButtonStyle == BS_AUTORADIOBUTTON));
}
BOOL COlePropertyPage::OnCommand(WPARAM wParam, LPARAM lParam)
{
// Let the base class process the message first
BOOL bSuccess = CDialog::OnCommand(wParam, lParam);
// Are we just initializing the dialog box, or do we have no objects?
if (m_bInitializing || m_ppDisp == NULL)
return bSuccess;
UINT nID = (UINT)LOWORD(wParam);
HWND hWndCtl = (HWND)lParam;
WORD wNotifyCode = HIWORD(wParam);
DWORD flags = 0;
if (hWndCtl != NULL)
{
BOOL bIgnoreControl = FALSE;
for (int count = 0; count < m_IDArray.GetSize(); count++)
{
UINT nNotID = m_IDArray.GetAt(count);
if (nID == nNotID)
bIgnoreControl = TRUE;
}
if ( !bIgnoreControl )
{
TCHAR szClassName[MAX_CLASS_NAME];
// We have a control message - check type of control and message
::GetClassName(hWndCtl, (LPTSTR)szClassName, MAX_CLASS_NAME);
for (int iNotify = 0; iNotify < _countof(_afxNotifyList); iNotify++)
{
if (lstrcmpi(_afxNotifyList[iNotify].szClassName, szClassName) == 0
&& _afxNotifyList[iNotify].wNotifyCode == wNotifyCode )
{
if ((lstrcmpi(szClassName, _T("button")) == 0) &&
_AfxIsRadioButton(hWndCtl))
{
// Special case for radio buttons:
// mark first button in group
while ((hWndCtl != NULL) &&
!(GetWindowLong(hWndCtl, GWL_STYLE) & WS_GROUP))
{
hWndCtl = ::GetWindow(hWndCtl, GW_HWNDPREV);
}
// First button in group must have WS_GROUP style,
// and must be a radio button.
ASSERT(hWndCtl != NULL);
ASSERT(_AfxIsRadioButton(hWndCtl));
// Mark first radio button as dirty
if (hWndCtl != NULL)
nID = ::GetWindowLong(hWndCtl, GWL_ID);
}
// Control has been modified
m_bDirty = TRUE;
SetControlStatus(nID, TRUE);
flags = PROPPAGESTATUS_DIRTY;
break;
}
}
if (m_bDirty)
{
for (int iNotify=0; iNotify < _countof(_afxUpdateList); iNotify++)
{
if (lstrcmpi(_afxUpdateList[iNotify].szClassName, szClassName)==0 &&
_afxUpdateList[iNotify].wNotifyCode == wNotifyCode &&
GetControlStatus(nID))
{
flags |= PROPPAGESTATUS_VALIDATE;
}
}
}
}
}
if (flags != 0)
{
ASSERT(m_pPageSite != NULL);
m_pPageSite->OnStatusChange(flags);
}
return bSuccess;
}
void COlePropertyPage::IgnoreApply(UINT nID)
{
m_IDArray.Add(nID);
}
BOOL COlePropertyPage::GetControlStatus(UINT nID)
{
for (int nControl = 0; nControl < m_nControls; nControl++)
if (m_pStatus[nControl].nID == nID)
return m_pStatus[nControl].bDirty;
// If we couldn't find the control - assume it is dirty
return TRUE;
}
BOOL COlePropertyPage::SetControlStatus(UINT nID, BOOL bDirty)
{
for (int nControl = 0; nControl < m_nControls; nControl++)
if (m_pStatus[nControl].nID == nID)
{
m_pStatus[nControl].bDirty = bDirty;
return TRUE;
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
// Function Templates using the Preprocessor
// [This should be replaced with C++ Templates when the 16 bit compiler allows]
#define DEFINE_GET_SET_PROP(ctype,casttype,vttype) \
BOOL COlePropertyPage::SetPropText(LPCTSTR pszPropName, ctype& data ) \
{ \
USES_CONVERSION;\
COleDispatchDriver PropDispDriver; \
BOOL bResult = FALSE; \
for (ULONG i = 0; i < m_nObjects; i++) \
{ \
DISPID dwDispID; \
LPCOLESTR lpOleStr = T2COLE(pszPropName);\
if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID))) \
{ \
PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE); \
PropDispDriver.SetProperty(dwDispID, vttype, (casttype)data ); \
PropDispDriver.DetachDispatch(); \
bResult = TRUE; \
} \
} \
return bResult; \
} \
BOOL COlePropertyPage::GetPropText(LPCTSTR pszPropName, ctype *data ) \
{ \
USES_CONVERSION;\
COleDispatchDriver PropDispDriver; \
BOOL bSuccess = FALSE; \
for (ULONG i = 0; i < m_nObjects; i++) \
{ \
DISPID dwDispID; \
LPCOLESTR lpOleStr = T2COLE(pszPropName);\
if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID))) \
{ \
ctype dataTemp; \
static ctype fill; \
PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE); \
PropDispDriver.GetProperty(dwDispID, vttype, &dataTemp); \
PropDispDriver.DetachDispatch(); \
if (i == 0) *data = dataTemp; \
if (*data != dataTemp) *data = fill; \
bSuccess = TRUE; \
} \
} \
return bSuccess; \
}
/////////////////////////////////////////////////////////////////////////////
// DDP_ property get/set helpers
DEFINE_GET_SET_PROP( BYTE, BYTE, VT_UI1 );
DEFINE_GET_SET_PROP( short, short, VT_I2 );
DEFINE_GET_SET_PROP( int, int, VT_I4 );
DEFINE_GET_SET_PROP( UINT, UINT, VT_I4 );
DEFINE_GET_SET_PROP( long, long, VT_I4 );
DEFINE_GET_SET_PROP( DWORD, DWORD, VT_I4 );
DEFINE_GET_SET_PROP( float, float, VT_R4 );
DEFINE_GET_SET_PROP( double, double, VT_R8 );
DEFINE_GET_SET_PROP( CString, LPCTSTR, VT_BSTR );
BOOL COlePropertyPage::SetPropCheck(LPCTSTR pszPropName, int Value)
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
BOOL bResult = FALSE;
BOOL bValue;
if (Value == 1)
bValue = TRUE;
else
bValue = FALSE; // default to off
// Set the properties for all the objects
for (ULONG i = 0; i < m_nObjects; i++)
{
DISPID dwDispID;
// Get the Dispatch ID for the property and if successful set the value of the property
LPCOLESTR lpOleStr = T2COLE(pszPropName);
if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
{
// Set property
PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
PropDispDriver.SetProperty(dwDispID, VT_BOOL, bValue);
PropDispDriver.DetachDispatch();
bResult = TRUE;
}
}
return bResult;
}
BOOL COlePropertyPage::GetPropCheck(LPCTSTR pszPropName, int* pValue)
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
BOOL bSuccess = FALSE;
// Check the property values for all the objects
for (ULONG i = 0; i < m_nObjects; i++)
{
DISPID dwDispID;
// Get the Dispatch ID for the property and if successful get the value of the property
LPCOLESTR lpOleStr = T2COLE(pszPropName);
if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
{
// Get property
BOOL bTemp = FALSE;
int tempValue;
PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
PropDispDriver.GetProperty(dwDispID, VT_BOOL, &bTemp);
PropDispDriver.DetachDispatch();
// Convert boolean value to check box equivalent
if (bTemp)
tempValue = 1;
else
tempValue = 0;
// Handle special case for first object
if (i == 0)
*pValue = tempValue;
// If the current check value is not the same as the one just retrieved then
// set the current check value to the indeterminate state.
if (tempValue != *pValue)
*pValue = 2;
bSuccess = TRUE;
}
}
return bSuccess;
}
BOOL COlePropertyPage::SetPropRadio(LPCTSTR pszPropName, int Value)
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
BOOL bSuccess = FALSE;
// Set the properties for all the objects
for (ULONG i = 0; i < m_nObjects; i++)
{
DISPID dwDispID;
// Get the Dispatch ID for the property and if successful set the value of the property
LPCOLESTR lpOleStr = T2COLE(pszPropName);
if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
{
short nTemp = (short)Value;
// Set property
PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
PropDispDriver.SetProperty(dwDispID, VT_I2, nTemp);
PropDispDriver.DetachDispatch();
bSuccess = TRUE;
}
}
return bSuccess;
}
BOOL COlePropertyPage::GetPropRadio(LPCTSTR pszPropName, int* pValue)
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
BOOL bSuccess = FALSE;
// Check the property values for all the objects
for (ULONG i = 0; i < m_nObjects; i++)
{
DISPID dwDispID;
// Get the Dispatch ID for the property and if successful get the value of the property
LPCOLESTR lpOleStr = T2COLE(pszPropName);
if (SUCCEEDED(m_ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr, 1, 0, &dwDispID)))
{
short nTemp;
// Get property
PropDispDriver.AttachDispatch(m_ppDisp[i], FALSE);
PropDispDriver.GetProperty(dwDispID, VT_I2, &nTemp);
PropDispDriver.DetachDispatch();
// Handle special case for first object
if (i == 0)
*pValue = nTemp;
// Compare the current radio value with the one just retrieved then
// if they are different then set the radio value to -1, so that no
// radio buttons will be checked.
if (nTemp != *pValue)
*pValue = -1;
bSuccess = TRUE;
}
}
return bSuccess;
}
BOOL COlePropertyPage::SetPropIndex(LPCTSTR pszPropName, int Value)
{
return SetPropRadio(pszPropName, Value);
}
BOOL COlePropertyPage::GetPropIndex(LPCTSTR pszPropName, int* pValue)
{
return GetPropRadio(pszPropName, pValue);
}
/////////////////////////////////////////////////////////////////////////////
// DDP_Begin data exchange routines (Should be C++ templated someday!)
#define DEFINE_DDP_(group,ctype,vtype,bEditCtrl) \
void AFXAPI DDP_End##group(CDataExchange* pDX, int, \
ctype *member, LPCTSTR pszPropName ) \
{ \
COlePropertyPage* propDialog = STATIC_DOWNCAST(COlePropertyPage, pDX->m_pDlgWnd); \
if (pDX->m_bSaveAndValidate) \
propDialog->SetProp##group(pszPropName, *member ); \
} \
void AFXAPI DDP_##group(CDataExchange* pDX, int nCtrlId, \
ctype &member, LPCTSTR pszPropName ) \
{ \
ASSERT(AfxIsValidString(pszPropName)); \
COlePropertyPage* propDialog = STATIC_DOWNCAST(COlePropertyPage, pDX->m_pDlgWnd); \
if (pDX->m_bSaveAndValidate) /*Are we Saving?*/ \
{ \
if (propDialog->GetControlStatus(nCtrlId)) /*Is Control Dirty?*/ \
{ \
void (AFXAPI *pfv)(CDataExchange*,int,ctype*,LPCTSTR) = \
DDP_End##group; \
AFX_DDPDATA *pDDP = new AFX_DDPDATA( (void *)pfv, nCtrlId, bEditCtrl, \
(void*)&member, (UINT)vtype, \
pszPropName ); \
propDialog->m_arrayDDP.Add(pDDP); \
} \
} \
else /* Loading data from properties! */ \
{ \
propDialog->GetProp##group(pszPropName, &member); \
propDialog->SetControlStatus(nCtrlId,FALSE); \
} \
}
/////////////////////////////////////////////////////////////////////////////
// DDP Functions (Pseudo Template Generation)
DEFINE_DDP_(Text,BYTE,VT_UI1,TRUE);
DEFINE_DDP_(Text,short,VT_I2,TRUE);
DEFINE_DDP_(Text,int,VT_I4,TRUE);
DEFINE_DDP_(Text,UINT,VT_UI4,TRUE);
DEFINE_DDP_(Text,long,VT_I4,TRUE);
DEFINE_DDP_(Text,DWORD,VT_UI4,TRUE);
DEFINE_DDP_(Text,float,VT_R4,TRUE);
DEFINE_DDP_(Text,double,VT_R8,TRUE);
DEFINE_DDP_(Text,CString,VT_BSTR,TRUE);
DEFINE_DDP_(Check,BOOL,VT_I4,FALSE);
DEFINE_DDP_(Radio,int,VT_I4,FALSE);
//////////////////////////////////////////////////////////////////////////////
// DDP Deferred Property Write Handler
void AFXAPI DDP_PostProcessing(CDataExchange*pDX)
{
if (pDX->m_bSaveAndValidate)
{
CPtrArray &arrayDDP =
((COlePropertyPage *)pDX->m_pDlgWnd)->m_arrayDDP;
AFX_DDPDATA *pDDP = NULL;
int cDDP = arrayDDP.GetSize();
for (int i = 0 ; i < cDDP; i++)
{
pDDP = (AFX_DDPDATA*)arrayDDP[i];
TRY
{
if (pDDP->m_bEditCtrl)
pDX->PrepareEditCtrl(pDDP->m_nCtrlId);
else
pDX->PrepareCtrl(pDDP->m_nCtrlId);
switch( pDDP->m_nType )
{
case VT_I1:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
char *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(char *)pDDP->m_lpMember,
pDDP->m_lpszOleName );
break;
}
case VT_UI1:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
BYTE *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(BYTE *)pDDP->m_lpMember,
pDDP->m_lpszOleName );
break;
}
case VT_I2:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
short *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(short *)pDDP->m_lpMember,
pDDP->m_lpszOleName );
break;
}
case VT_UI2:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
WORD *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(WORD *)pDDP->m_lpMember,
pDDP->m_lpszOleName );
break;
}
case VT_I4:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
long *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(long *)pDDP->m_lpMember,
pDDP->m_lpszOleName );
break;
}
case VT_UI4:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
DWORD *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(DWORD *)pDDP->m_lpMember,
pDDP->m_lpszOleName);
break;
}
case VT_R4:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
float *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(float *)pDDP->m_lpMember,
pDDP->m_lpszOleName);
break;
}
case VT_R8:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
double *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(double *)pDDP->m_lpMember,
pDDP->m_lpszOleName );
break;
}
case VT_BSTR:
{
typedef void (AFXAPI *PFV)(CDataExchange *, int,
CString *, LPCTSTR );
(*(PFV)pDDP->m_lpHandler)(pDX, pDDP->m_nCtrlId,
(CString *)pDDP->m_lpMember,
pDDP->m_lpszOleName );
break;
}
default:
// Unknown Data Type!
ASSERT(FALSE);
break;
}
}
CATCH(COleDispatchException, e)
{
// Dleanup before pDX->Fail() throws exception.
_AfxCleanupDDPs(arrayDDP);
// Display message box for dispatch exceptions.
COlePropertyPage* pPropPage = (COlePropertyPage*)pDX->m_pDlgWnd;
pPropPage->MessageBox((LPCTSTR)e->m_strDescription, NULL,
MB_ICONEXCLAMATION | MB_OK);
DELETE_EXCEPTION(e);
pDX->Fail();
}
AND_CATCH_ALL(e)
{
// Ignore other exceptions.
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
}
_AfxCleanupDDPs(arrayDDP);
}
}
/////////////////////////////////////////////////////////////////////////////
// Force any extra compiler-generated code into AFX_INIT_SEG
#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif
IMPLEMENT_DYNAMIC(COlePropertyPage, CDialog)