You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1082 lines
30 KiB
1082 lines
30 KiB
//=--------------------------------------------------------------------------=
|
|
// PropertyPages.Cpp
|
|
//=--------------------------------------------------------------------------=
|
|
// Copyright 1995 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//=--------------------------------------------------------------------------=
|
|
//
|
|
// implementation of CPropertyPage object.
|
|
//
|
|
#include "pch.h"
|
|
#include "PropPage.H"
|
|
|
|
#if defined(VS_HELP) || defined(HTML_HELP)
|
|
|
|
#ifdef MS_BUILD
|
|
#include "HtmlHelp.h"
|
|
#else
|
|
#include "HtmlHelp.hxx"
|
|
#endif
|
|
|
|
#ifdef VS_HELP
|
|
#include "VSHelp.h" // Visual Studio html help support
|
|
#endif
|
|
|
|
#endif
|
|
|
|
// for ASSERT and FAIL
|
|
//
|
|
SZTHISFILE
|
|
|
|
|
|
|
|
// this variable is used to pass the pointer to the object to the hwnd.
|
|
//
|
|
static CPropertyPage *s_pLastPageCreated;
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::CPropertyPage
|
|
//=--------------------------------------------------------------------------=
|
|
// constructor.
|
|
//
|
|
// Parameters:
|
|
// IUnknown * - [in] controlling unknown
|
|
// int - [in] object type.
|
|
//
|
|
// Notes:
|
|
//
|
|
#pragma warning(disable:4355) // using 'this' in constructor
|
|
CPropertyPage::CPropertyPage
|
|
(
|
|
IUnknown *pUnkOuter,
|
|
int iObjectType
|
|
)
|
|
: CUnknownObject(pUnkOuter, this), m_ObjectType(iObjectType)
|
|
{
|
|
// initialize various dudes.
|
|
//
|
|
m_pPropertyPageSite = NULL;
|
|
m_hwnd = NULL;
|
|
m_cObjects = 0;
|
|
|
|
m_fDirty = FALSE;
|
|
m_fActivated = FALSE;
|
|
m_fDeactivating = FALSE;
|
|
m_ppUnkObjects = NULL;
|
|
}
|
|
#pragma warning(default:4355) // using 'this' in constructor
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::~CPropertyPage
|
|
//=--------------------------------------------------------------------------=
|
|
// destructor.
|
|
//
|
|
// Notes:
|
|
//
|
|
CPropertyPage::~CPropertyPage()
|
|
{
|
|
// clean up our window.
|
|
//
|
|
if (m_hwnd) {
|
|
SetWindowLong(m_hwnd, GWL_USERDATA, 0xffffffff);
|
|
DestroyWindow(m_hwnd);
|
|
m_hwnd = NULL;
|
|
}
|
|
|
|
// release all the objects we're holding on to.
|
|
//
|
|
ReleaseAllObjects();
|
|
|
|
// release the site
|
|
//
|
|
RELEASE_OBJECT(m_pPropertyPageSite);
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::InternalQueryInterface
|
|
//=--------------------------------------------------------------------------=
|
|
// we support IPP and IPP2.
|
|
//
|
|
// Parameters:
|
|
// REFIID - [in] interface they want
|
|
// void ** - [out] where they want to put the resulting object ptr.
|
|
//
|
|
// Output:
|
|
// HRESULT - S_OK, E_NOINTERFACE
|
|
//
|
|
// Notes:
|
|
//
|
|
HRESULT CPropertyPage::InternalQueryInterface
|
|
(
|
|
REFIID riid,
|
|
void **ppvObjOut
|
|
)
|
|
{
|
|
IUnknown *pUnk;
|
|
|
|
*ppvObjOut = NULL;
|
|
|
|
if (DO_GUIDS_MATCH(IID_IPropertyPage, riid)) {
|
|
pUnk = (IUnknown *)this;
|
|
} else if (DO_GUIDS_MATCH(IID_IPropertyPage2, riid)) {
|
|
pUnk = (IUnknown *)this;
|
|
} else {
|
|
return CUnknownObject::InternalQueryInterface(riid, ppvObjOut);
|
|
}
|
|
|
|
pUnk->AddRef();
|
|
*ppvObjOut = (void *)pUnk;
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::SetPageSite [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// the initialization function for a property page through which the page
|
|
// receives an IPropertyPageSite pointer.
|
|
//
|
|
// Parameters:
|
|
// IPropertyPageSite * - [in] new site.
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes;
|
|
//
|
|
STDMETHODIMP CPropertyPage::SetPageSite
|
|
(
|
|
IPropertyPageSite *pPropertyPageSite
|
|
)
|
|
{
|
|
RELEASE_OBJECT(m_pPropertyPageSite);
|
|
m_pPropertyPageSite = pPropertyPageSite;
|
|
ADDREF_OBJECT(pPropertyPageSite);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::Activate [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// instructs the page to create it's display window as a child of hwndparent
|
|
// and to position it according to prc.
|
|
//
|
|
// Parameters:
|
|
// HWND - [in] parent window
|
|
// LPCRECT - [in] where to position ourselves
|
|
// BOOL - [in] whether we're modal or not.
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::Activate
|
|
(
|
|
HWND hwndParent,
|
|
LPCRECT prcBounds,
|
|
BOOL fModal
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// first make sure the dialog window is loaded and created.
|
|
//
|
|
hr = EnsureLoaded();
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
// fire off a PPM_NEWOBJECTS now
|
|
//
|
|
hr = NewObjects(); // Note: m_fDirty is cleared after this call
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
// set our parent window if we haven't done so yet.
|
|
//
|
|
if (!m_fActivated) {
|
|
SetParent(m_hwnd, hwndParent);
|
|
m_fActivated = TRUE;
|
|
}
|
|
|
|
// now move ourselves to where we're told to be and show ourselves
|
|
//
|
|
Move(prcBounds);
|
|
ShowWindow(m_hwnd, SW_SHOW);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::Deactivate [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// instructs the page to destroy the window created in activate
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::Deactivate
|
|
(
|
|
void
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_fDeactivating = TRUE;
|
|
|
|
SendMessage(m_hwnd, PPM_FREEOBJECTS, 0, (LPARAM)&hr);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
// blow away your window.
|
|
//
|
|
if (m_hwnd)
|
|
{
|
|
DestroyWindow(m_hwnd);
|
|
m_hwnd = NULL;
|
|
}
|
|
|
|
m_fActivated = FALSE;
|
|
m_fDeactivating = FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::GetPageInfo [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// asks the page to fill a PROPPAGEINFO structure
|
|
//
|
|
// Parameters:
|
|
// PROPPAGEINFO * - [out] where to put info.
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::GetPageInfo
|
|
(
|
|
PROPPAGEINFO *pPropPageInfo
|
|
)
|
|
{
|
|
RECT rect;
|
|
|
|
CHECK_POINTER(pPropPageInfo);
|
|
|
|
EnsureLoaded();
|
|
|
|
// clear it out first.
|
|
//
|
|
memset(pPropPageInfo, 0, sizeof(PROPPAGEINFO));
|
|
|
|
pPropPageInfo->pszTitle = OLESTRFROMRESID(TITLEIDOFPROPPAGE(m_ObjectType));
|
|
pPropPageInfo->pszDocString = OLESTRFROMRESID(DOCSTRINGIDOFPROPPAGE(m_ObjectType));
|
|
pPropPageInfo->pszHelpFile = OLESTRFROMANSI(HELPFILEOFPROPPAGE(m_ObjectType));
|
|
pPropPageInfo->dwHelpContext = HELPCONTEXTOFPROPPAGE(m_ObjectType);
|
|
|
|
if (!(pPropPageInfo->pszTitle && pPropPageInfo->pszDocString && pPropPageInfo->pszHelpFile))
|
|
goto CleanUp;
|
|
|
|
// if we've got a window yet, go and set up the size information they want.
|
|
//
|
|
if (m_hwnd) {
|
|
GetWindowRect(m_hwnd, &rect);
|
|
|
|
pPropPageInfo->size.cx = rect.right - rect.left;
|
|
pPropPageInfo->size.cy = rect.bottom - rect.top;
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
CleanUp:
|
|
if (pPropPageInfo->pszDocString) {
|
|
CoTaskMemFree(pPropPageInfo->pszDocString);
|
|
pPropPageInfo->pszDocString = NULL;
|
|
}
|
|
if (pPropPageInfo->pszHelpFile) {
|
|
CoTaskMemFree(pPropPageInfo->pszHelpFile);
|
|
pPropPageInfo->pszHelpFile = NULL;
|
|
}
|
|
if (pPropPageInfo->pszTitle) {
|
|
CoTaskMemFree(pPropPageInfo->pszTitle);
|
|
pPropPageInfo->pszTitle = NULL;
|
|
}
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::SetObjects [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// provides the page with the objects being affected by the changes.
|
|
//
|
|
// Parameters:
|
|
// ULONG - [in] count of objects.
|
|
// IUnknown ** - [in] objects.
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::SetObjects
|
|
(
|
|
ULONG cObjects,
|
|
IUnknown **ppUnkObjects
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
ULONG x;
|
|
|
|
//Vegas 33683 - joejo
|
|
// make sure the old control is updated
|
|
// if page is dirty!
|
|
if (m_fDirty)
|
|
hr = Apply();
|
|
|
|
// make sure we've been loaded, and free out any other objects that might
|
|
// have been hanging around
|
|
//
|
|
ReleaseAllObjects();
|
|
|
|
if (!cObjects)
|
|
return S_OK;
|
|
|
|
// now go and set up the new ones.
|
|
//
|
|
m_ppUnkObjects = (IUnknown **)CtlHeapAlloc(g_hHeap, 0, cObjects * sizeof(IUnknown *));
|
|
RETURN_ON_NULLALLOC(m_ppUnkObjects);
|
|
|
|
// loop through and copy over all the objects.
|
|
//
|
|
for (x = 0; x < cObjects; x++) {
|
|
m_ppUnkObjects[x] = ppUnkObjects[x];
|
|
ADDREF_OBJECT(m_ppUnkObjects[x]);
|
|
}
|
|
|
|
// go and tell the page that there are new objects [but only if it's been
|
|
// activated]
|
|
//
|
|
hr = S_OK;
|
|
m_cObjects = cObjects;
|
|
if (m_fActivated)
|
|
hr = NewObjects(); // Note: m_fDirty is cleared after this call
|
|
|
|
return hr;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::Show [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// asks the page to show or hide its window
|
|
//
|
|
// Parameters:
|
|
// UINT - [in] whether to show or hide
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::Show
|
|
(
|
|
UINT nCmdShow
|
|
)
|
|
{
|
|
if (m_hwnd)
|
|
ShowWindow(m_hwnd, nCmdShow);
|
|
else
|
|
return E_UNEXPECTED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::Move [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// asks the page to relocate and resize itself to a position other than what
|
|
// was specified through Activate
|
|
//
|
|
// Parameters:
|
|
// LPCRECT - [in] new position and size
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::Move
|
|
(
|
|
LPCRECT prcBounds
|
|
)
|
|
{
|
|
// do what they sez
|
|
//
|
|
if (m_hwnd)
|
|
SetWindowPos(m_hwnd, NULL, prcBounds->left, prcBounds->top,
|
|
prcBounds->right - prcBounds->left,
|
|
prcBounds->bottom - prcBounds->top,
|
|
SWP_NOZORDER);
|
|
else
|
|
return E_UNEXPECTED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::IsPageDirty [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// asks the page whether it has changed its state
|
|
//
|
|
// Output
|
|
// S_OK - yep
|
|
// S_FALSE - nope
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::IsPageDirty
|
|
(
|
|
void
|
|
)
|
|
{
|
|
return m_fDirty ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::Apply [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// instructs the page to send its changes to all the objects passed through
|
|
// SetObjects()
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::Apply
|
|
(
|
|
void
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_hwnd) {
|
|
SendMessage(m_hwnd, PPM_APPLY, 0, (LPARAM)&hr);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
if (m_fDirty) {
|
|
m_fDirty = FALSE;
|
|
|
|
if (m_pPropertyPageSite && !m_fDeactivating)
|
|
m_pPropertyPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
|
|
}
|
|
} else
|
|
return E_UNEXPECTED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::Help [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// instructs the page that the help button was clicked.
|
|
//
|
|
// Parameters:
|
|
// LPCOLESTR - [in] help directory
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::Help
|
|
(
|
|
LPCOLESTR pszHelpDir // Note: With VS_HELP set this parameter is ignored
|
|
)
|
|
{
|
|
char *pszExt;
|
|
char buf[MAX_PATH];
|
|
BOOL f = FALSE;
|
|
|
|
#ifdef VS_HELP
|
|
BOOL bHelpStarted;
|
|
HRESULT hr;
|
|
#endif
|
|
|
|
ASSERT(m_hwnd, "CPropertyPage::Help called with no hwnd!");
|
|
|
|
pszExt = FileExtension(HELPFILEOFPROPPAGE(m_ObjectType));
|
|
if (pszExt)
|
|
{
|
|
|
|
#ifdef VS_HELP
|
|
#else
|
|
#endif
|
|
|
|
#if defined(VS_HELP) || defined(HTML_HELP)
|
|
|
|
if (lstrcmpi(pszExt, "CHM") == 0)
|
|
{
|
|
|
|
#ifdef VS_HELP
|
|
|
|
lstrcpy(buf, HELPFILEOFPROPPAGE(m_ObjectType));
|
|
|
|
// First try to show help through VisualStudio
|
|
//
|
|
hr = VisualStudioShowHelpTopic(buf, HELPCONTEXTOFPROPPAGE(m_ObjectType), &bHelpStarted);
|
|
f = SUCCEEDED(hr);
|
|
|
|
// Check to see if Visual Studio help could be successfully started. If not,
|
|
// assume it doesn't exist
|
|
//
|
|
if (!bHelpStarted)
|
|
|
|
#endif
|
|
|
|
{
|
|
MAKE_ANSIPTR_FROMWIDE(psz, pszHelpDir);
|
|
lstrcpy(buf, psz);
|
|
lstrcat(buf, "\\");
|
|
lstrcat(buf, HELPFILEOFPROPPAGE(m_ObjectType));
|
|
|
|
// Show the help topic manually by calling HtmlHelp directly
|
|
//
|
|
f = (BOOL) HtmlHelp(m_hwnd, buf, HH_HELP_CONTEXT,
|
|
HELPCONTEXTOFPROPPAGE(m_ObjectType));
|
|
}
|
|
|
|
#ifdef VS_HELP
|
|
|
|
else
|
|
{
|
|
ASSERT(SUCCEEDED(hr), "Failed to show help topic from Visual Studio");
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else if (lstrcmpi(pszExt, "HLP") == 0)
|
|
|
|
#endif
|
|
|
|
{
|
|
// WinHelp
|
|
|
|
// get the helpfile name
|
|
//
|
|
MAKE_ANSIPTR_FROMWIDE(psz, pszHelpDir);
|
|
lstrcpy(buf, psz);
|
|
lstrcat(buf, "\\");
|
|
lstrcat(buf, HELPFILEOFPROPPAGE(m_ObjectType));
|
|
|
|
lstrcat(buf, ">LangRef"); // Use LangRef window style
|
|
f = WinHelp(m_hwnd, buf, HELP_CONTEXT,
|
|
HELPCONTEXTOFPROPPAGE(m_ObjectType));
|
|
}
|
|
|
|
#if defined(VS_HELP) || defined(HTML_HELP)
|
|
|
|
else
|
|
{
|
|
FAIL("Unrecognized help file type");
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
return f ? S_OK : E_FAIL;
|
|
}
|
|
|
|
static BOOL IsLastTabItem(HWND hdlg, HWND hctl, UINT nCmd)
|
|
{
|
|
if ((SendMessage(hdlg, WM_GETDLGCODE, 0, 0L) &
|
|
(DLGC_WANTALLKEYS | DLGC_WANTMESSAGE | DLGC_WANTTAB)) == 0)
|
|
{
|
|
// Get top level child for controls with children, like combo.
|
|
//
|
|
HWND hwnd;
|
|
for (/**/; hctl != hdlg; hctl = GetParent(hctl))
|
|
hwnd = hctl;
|
|
|
|
// Walk the zorder list until we reached the end
|
|
// or until we get to a valid tab item
|
|
//
|
|
do
|
|
{
|
|
if ((hwnd = GetWindow(hwnd, nCmd)) == NULL)
|
|
return TRUE;
|
|
}
|
|
while ((GetWindowLong(hwnd, GWL_STYLE) & (WS_DISABLED|WS_TABSTOP|WS_VISIBLE)) != (WS_TABSTOP|WS_VISIBLE));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::TranslateAccelerator [IPropertyPage]
|
|
//=--------------------------------------------------------------------------=
|
|
// informs the page of keyboard events, allowing it to implement it's own
|
|
// keyboard interface.
|
|
//
|
|
// Parameters:
|
|
// LPMSG - [in] message that triggered this
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::TranslateAccelerator
|
|
(
|
|
LPMSG pmsg
|
|
)
|
|
{
|
|
ASSERT(m_hwnd, "How can we get a TranslateAccelerator call if we're not visible?");
|
|
CHECK_POINTER(pmsg);
|
|
|
|
BOOL fHandled = FALSE;
|
|
HWND hctl;
|
|
|
|
// Special consideration for the Return and Escape keys.
|
|
//
|
|
if ((pmsg->message == WM_KEYDOWN) &&
|
|
((pmsg->wParam == VK_RETURN) || (pmsg->wParam == VK_ESCAPE))) {
|
|
|
|
// Always let the frame handle the Escape key, but if we have the
|
|
// Return key, then we need to ask the focus control if it wants it.
|
|
// Usually, controls that want the Return key will return DLGC_WANTALLKEYS
|
|
// when they process WM_GETDLGCODE. This is the case when an
|
|
// Edit or RichEdit control has the ES_MULTILINE style
|
|
//
|
|
if (VK_RETURN == pmsg->wParam && m_hwnd != pmsg->hwnd
|
|
&& (SendMessage(pmsg->hwnd, WM_GETDLGCODE, 0, 0L) & DLGC_WANTALLKEYS)) {
|
|
|
|
// Pass the Return key on as a WM_CHAR, because
|
|
// this is what TranslateMessage will do for a
|
|
// WM_KEYDOWN
|
|
//
|
|
// If a control does not process this message, then
|
|
// it should return non-zero, in which case we can
|
|
// pass it on to the Frame
|
|
//
|
|
fHandled = !SendMessage(pmsg->hwnd, WM_CHAR, pmsg->wParam, pmsg->lParam);
|
|
}
|
|
|
|
// If the message was not handled, then let the
|
|
// Frame handle this message, but we need to change
|
|
// the window handle to that of the propage.
|
|
// This needs to be done because edit controls
|
|
// with ES_WANTRETURN will return DLGC_WANTALLKEYS,
|
|
// which will cause the Frame not to handle the Escape key
|
|
//
|
|
return fHandled ? S_OK : (pmsg->hwnd = m_hwnd, S_FALSE);
|
|
}
|
|
|
|
if (pmsg->message == WM_KEYDOWN && pmsg->wParam == VK_TAB && GetKeyState(VK_CONTROL) >= 0) {
|
|
// If we already have the focus. Let's determine whether we should
|
|
// pass focus up to the frame.
|
|
//
|
|
if (IsChild(m_hwnd, pmsg->hwnd)) {
|
|
// Fix for default button border
|
|
//
|
|
DWORD dwDefID = SendMessage(m_hwnd, DM_GETDEFID, 0, 0);
|
|
if (HIWORD(dwDefID) == DC_HASDEFID) {
|
|
hctl = GetDlgItem(m_hwnd, LOWORD(dwDefID));
|
|
if (NULL != hctl && IsWindowEnabled(hctl))
|
|
SendMessage(m_hwnd, WM_NEXTDLGCTL, (WPARAM)hctl, 1L);
|
|
}
|
|
// If the focus control is the last the the tab order
|
|
// then we will pass the message to the frame
|
|
//
|
|
if (IsLastTabItem(m_hwnd, pmsg->hwnd, GetKeyState(VK_SHIFT) < 0 ?
|
|
GW_HWNDPREV : GW_HWNDNEXT)) {
|
|
// Pass focus to the frame by letting the page site handle
|
|
// this message.
|
|
if (NULL != m_pPropertyPageSite)
|
|
fHandled = m_pPropertyPageSite->TranslateAccelerator(pmsg) == S_OK;
|
|
}
|
|
|
|
} else {
|
|
|
|
// We don't already have the focus. The frame is passing the
|
|
// focus to us.
|
|
//
|
|
hctl = GetNextDlgTabItem(m_hwnd, NULL, GetKeyState(VK_SHIFT) < 0);
|
|
if (NULL != hctl)
|
|
fHandled = (BOOL)SendMessage(m_hwnd, WM_NEXTDLGCTL, (WPARAM)hctl, 1L);
|
|
}
|
|
}
|
|
|
|
// just pass this message on to the dialog proc and see if they want it.
|
|
//
|
|
// just pass this message on to the dialog proc and see if they want it.
|
|
//
|
|
if (FALSE == fHandled) {
|
|
// In order for accelerators to work properly, we need to
|
|
// temporarily replace the message window handle to that
|
|
// of the first child in the property page in response
|
|
// to a WM_SYSKEYDOWN. This will allow the user to use an
|
|
// accelerator key to go from a tab to a control on the
|
|
// active page.
|
|
//
|
|
hctl = pmsg->hwnd;
|
|
if (WM_SYSKEYDOWN == pmsg->message && !IsChild(m_hwnd, pmsg->hwnd))
|
|
pmsg->hwnd = GetWindow(m_hwnd, GW_CHILD);
|
|
|
|
fHandled = IsDialogMessage(m_hwnd, pmsg);
|
|
pmsg->hwnd = hctl;
|
|
}
|
|
|
|
return fHandled ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::EditProperty [IPropertyPage2]
|
|
//=--------------------------------------------------------------------------=
|
|
// instructs the page to set the focus to the property matching the dispid.
|
|
//
|
|
// Parameters:
|
|
// DISPID - [in] dispid of property to set focus to.
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
STDMETHODIMP CPropertyPage::EditProperty
|
|
(
|
|
DISPID dispid
|
|
)
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
|
|
// send the message on to the control, and see what they want to do with it.
|
|
//
|
|
SendMessage(m_hwnd, PPM_EDITPROPERTY, (WPARAM)dispid, (LPARAM)&hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::EnsureLoaded
|
|
//=--------------------------------------------------------------------------=
|
|
// makes sure the dialog is actually loaded
|
|
//
|
|
// Output:
|
|
// HRESULT
|
|
//
|
|
// Notes:
|
|
//
|
|
HRESULT CPropertyPage::EnsureLoaded
|
|
(
|
|
void
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRSRC hrsrc;
|
|
HGLOBAL hDlg;
|
|
LPCDLGTEMPLATE pDlg;
|
|
HWND hwndDlg;
|
|
|
|
// duh
|
|
//
|
|
if (m_hwnd)
|
|
return S_OK;
|
|
|
|
// create the dialog window
|
|
//
|
|
hrsrc = FindResource(GetResourceHandle(), TEMPLATENAMEOFPROPPAGE(m_ObjectType), RT_DIALOG);
|
|
ASSERT(hrsrc, "Failed to find dialog template");
|
|
if (!hrsrc)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
hDlg = LoadResource(GetResourceHandle(), hrsrc);
|
|
ASSERT(hDlg, "Failed to load dialog resource");
|
|
if (!hDlg)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
pDlg = (LPCDLGTEMPLATE) LockResource(hDlg);
|
|
ASSERT(pDlg, "Failed to lock dialog resource");
|
|
if (!pDlg)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
// set up the global variable so that when we're in the dialog proc, we can
|
|
// stuff this in the hwnd
|
|
//
|
|
// crit sect this whole creation process for apartment threading support.
|
|
//
|
|
ENTERCRITICALSECTION1(&g_CriticalSection);
|
|
s_pLastPageCreated = this;
|
|
|
|
// Why not call CreateDialog instead? The answer is that the property page
|
|
// dialog resource may contain Windows custom controls where the window proc for the
|
|
// Windows control resides in your control DLL (e.g., .OCX file), not the
|
|
// satellite DLL. If CreateDialog calls CreateWindow to create your Windows custom control
|
|
// with a different instance than where the window class for the control is
|
|
// registered, it will fail.
|
|
//
|
|
hwndDlg = CreateDialogIndirect(g_hInstance, pDlg, GetParkingWindow(),
|
|
CPropertyPage::PropPageDlgProc);
|
|
|
|
ASSERT(hwndDlg, "Couldn't load Dialog Resource!!!");
|
|
ASSERT(hwndDlg == m_hwnd, "Returned hwnd doesn't match cached hwnd");
|
|
|
|
// clean up variables and leave the critical section
|
|
//
|
|
s_pLastPageCreated = NULL;
|
|
LEAVECRITICALSECTION1(&g_CriticalSection);
|
|
|
|
if (!m_hwnd)
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
#if 0
|
|
// go and notify the window that it should pick up any objects that are
|
|
// available
|
|
//
|
|
SendMessage(m_hwnd, PPM_NEWOBJECTS, 0, (LPARAM)&hr);
|
|
#endif // 0
|
|
|
|
return hr;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::ReleaseAllObjects
|
|
//=--------------------------------------------------------------------------=
|
|
// releases all the objects that we're working with
|
|
//
|
|
// Notes:
|
|
//
|
|
void CPropertyPage::ReleaseAllObjects
|
|
(
|
|
void
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
UINT x;
|
|
|
|
// some people will want to stash pointers in the PPM_INITOBJECTS case, so
|
|
// we want to tell them to release them now.
|
|
//
|
|
if (m_fActivated && m_hwnd)
|
|
SendMessage(m_hwnd, PPM_FREEOBJECTS, 0, (LPARAM)&hr);
|
|
|
|
if (!m_cObjects) return;
|
|
// loop through and blow them all away.
|
|
//
|
|
for (x = 0; x < m_cObjects; x++)
|
|
QUICK_RELEASE(m_ppUnkObjects[x]);
|
|
|
|
CtlHeapFree(g_hHeap, 0, m_ppUnkObjects);
|
|
m_ppUnkObjects = NULL;
|
|
m_cObjects = 0;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::PropPageDlgProc
|
|
//=--------------------------------------------------------------------------=
|
|
// static global helper dialog proc that gets called before we pass the message
|
|
// on to anybody ..
|
|
//
|
|
// Parameters:
|
|
// - see win32sdk docs on DialogProc
|
|
//
|
|
// Notes:
|
|
//
|
|
INT_PTR CALLBACK CPropertyPage::PropPageDlgProc
|
|
(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
CPropertyPage *pPropertyPage;
|
|
|
|
// get the window long, and see if it's been set to the object this hwnd
|
|
// is operating against. if not, go and set it now.
|
|
//
|
|
pPropertyPage = (CPropertyPage *)GetWindowLong(hwnd, GWL_USERDATA);
|
|
if ((ULONG)pPropertyPage == 0xffffffff)
|
|
return FALSE;
|
|
if (!pPropertyPage) {
|
|
SetWindowLong(hwnd, GWL_USERDATA, (LONG)s_pLastPageCreated);
|
|
pPropertyPage = s_pLastPageCreated;
|
|
pPropertyPage->m_hwnd = hwnd;
|
|
}
|
|
|
|
ASSERT(pPropertyPage, "Uh oh. Got a window, but no CpropertyPage for it!");
|
|
|
|
// just call the user dialog proc and see if they want to do anything.
|
|
//
|
|
return pPropertyPage->DialogProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::FirstControl
|
|
//=--------------------------------------------------------------------------=
|
|
// returns the first controlish object that we are showing ourselves for.
|
|
// returns a cookie that must be passed in for Next ...
|
|
//
|
|
// Parameters:
|
|
// DWORD * - [out] cookie to be used for Next
|
|
//
|
|
// Output:
|
|
// IUnknown *
|
|
//
|
|
// Notes:
|
|
//
|
|
IUnknown *CPropertyPage::FirstControl
|
|
(
|
|
DWORD *pdwCookie
|
|
)
|
|
{
|
|
// just use the implementation of NEXT.
|
|
//
|
|
*pdwCookie = 0;
|
|
return NextControl(pdwCookie);
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::NextControl
|
|
//=--------------------------------------------------------------------------=
|
|
// returns the next control in the chain of people to work with given a cookie
|
|
//
|
|
// Parameters:
|
|
// DWORD * - [in/out] cookie to get next from, and new cookie.
|
|
//
|
|
// Output:
|
|
// IUnknown *
|
|
//
|
|
// Notes:
|
|
//
|
|
IUnknown *CPropertyPage::NextControl
|
|
(
|
|
DWORD *pdwCookie
|
|
)
|
|
{
|
|
UINT i;
|
|
|
|
// go looking through all the objects that we've got, and find the
|
|
// first non-null one.
|
|
//
|
|
for (i = *pdwCookie; i < m_cObjects; i++) {
|
|
if (!m_ppUnkObjects[i]) continue;
|
|
|
|
*pdwCookie = i + 1; // + 1 so we start at next item next time
|
|
return m_ppUnkObjects[i];
|
|
}
|
|
|
|
// couldn't find it .
|
|
//
|
|
*pdwCookie = 0xffffffff;
|
|
return NULL;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::NewObjects [helper]
|
|
//=--------------------------------------------------------------------------=
|
|
// Sends PPM_NEWOBJECTS message to the property page dialog, so that
|
|
// it can initialize its dialog fields.
|
|
//
|
|
// Notes:
|
|
//
|
|
HRESULT CPropertyPage::NewObjects()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SendMessage(m_hwnd, PPM_NEWOBJECTS, 0, (LPARAM) &hr);
|
|
|
|
// Clear the dirty bit and make sure the Apply button gets disabled.
|
|
//
|
|
if (m_fDirty)
|
|
{
|
|
m_fDirty = FALSE;
|
|
|
|
ASSERT(m_fDeactivating == FALSE, "We're being deactivated?");
|
|
if (m_pPropertyPageSite)
|
|
m_pPropertyPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::MakeDirty [helper, callable]
|
|
//=--------------------------------------------------------------------------=
|
|
// marks a page as dirty.
|
|
//
|
|
// Notes:
|
|
//
|
|
void CPropertyPage::MakeDirty
|
|
(
|
|
void
|
|
)
|
|
{
|
|
m_fDirty = TRUE;
|
|
|
|
// Need to make sure we have a page site and we're not being deactivated
|
|
// IE 4.0 will crash if we attempt to call OnStatusChange while we're being
|
|
// deactivated
|
|
//
|
|
if (m_pPropertyPageSite && !m_fDeactivating)
|
|
m_pPropertyPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY|PROPPAGESTATUS_VALIDATE);
|
|
}
|
|
|
|
|
|
// from Globals.C
|
|
//
|
|
extern HINSTANCE g_hInstResources;
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CPropertyPage::GetResourceHandle [helper, callable]
|
|
//=--------------------------------------------------------------------------=
|
|
// returns current resource handle, based on pagesites ambient LCID.
|
|
//
|
|
// Output:
|
|
// HINSTANCE
|
|
//
|
|
// Notes:
|
|
//
|
|
HINSTANCE CPropertyPage::GetResourceHandle
|
|
(
|
|
void
|
|
)
|
|
{
|
|
if (!g_fSatelliteLocalization)
|
|
return g_hInstance;
|
|
|
|
// if we've already got it, then there's not all that much to do.
|
|
// don't need to crit sect this one right here since even if they do fall
|
|
// into the ::GetResourceHandle call, it'll properly deal with things.
|
|
//
|
|
if (g_hInstResources)
|
|
return g_hInstResources;
|
|
|
|
// we'll get the ambient localeid from the host, and pass that on to the
|
|
// automation object.
|
|
//
|
|
// enter a critical section for g_lcidLocale and g_fHavelocale
|
|
//
|
|
ENTERCRITICALSECTION1(&g_CriticalSection);
|
|
if (!g_fHaveLocale) {
|
|
if (m_pPropertyPageSite) {
|
|
m_pPropertyPageSite->GetLocaleID(&g_lcidLocale);
|
|
g_fHaveLocale = TRUE;
|
|
}
|
|
}
|
|
LEAVECRITICALSECTION1(&g_CriticalSection);
|
|
|
|
return ::GetResourceHandle();
|
|
}
|
|
|
|
|