1086 lines
30 KiB
C++
1086 lines
30 KiB
C++
|
//=--------------------------------------------------------------------------=
|
||
|
// ControlMisc.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.
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
//
|
||
|
// things that aren't elsewhere, such as property pages, and connection
|
||
|
// points.
|
||
|
//
|
||
|
#include "pch.h"
|
||
|
#include "CtrlObj.H"
|
||
|
#include "CtlHelp.H"
|
||
|
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
// for ASSERT and FAIL
|
||
|
//
|
||
|
SZTHISFILE
|
||
|
|
||
|
// this is used in our window proc so that we can find out who was last created
|
||
|
//
|
||
|
static COleControl *s_pLastControlCreated;
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::COleControl
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// constructor
|
||
|
//
|
||
|
// Parameters:
|
||
|
// IUnknown * - [in] controlling Unknown
|
||
|
// int - [in] type of primary dispatch interface OBJECT_TYPE_*
|
||
|
// void * - [in] pointer to entire object
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
COleControl::COleControl
|
||
|
(
|
||
|
IUnknown *pUnkOuter,
|
||
|
int iPrimaryDispatch,
|
||
|
void *pMainInterface
|
||
|
)
|
||
|
: CAutomationObjectWEvents(pUnkOuter, iPrimaryDispatch, pMainInterface)
|
||
|
{
|
||
|
// initialize all our variables -- we decided against using a memory-zeroing
|
||
|
// memory allocator, so we sort of have to do this work now ...
|
||
|
//
|
||
|
m_pClientSite = NULL;
|
||
|
m_pControlSite = NULL;
|
||
|
m_pInPlaceSite = NULL;
|
||
|
m_pInPlaceFrame = NULL;
|
||
|
m_pInPlaceUIWindow = NULL;
|
||
|
|
||
|
|
||
|
m_pInPlaceSiteWndless = NULL;
|
||
|
|
||
|
// certain hosts don't like 0,0 as your initial size, so we're going to set
|
||
|
// our initial size to 100,50 [so it's at least sort of visible on the screen]
|
||
|
//
|
||
|
m_Size.cx = 100;
|
||
|
m_Size.cy = 50;
|
||
|
memset(&m_rcLocation, 0, sizeof(m_rcLocation));
|
||
|
|
||
|
m_hwnd = NULL;
|
||
|
m_hwndParent = NULL;
|
||
|
m_hwndReflect = NULL;
|
||
|
m_fHostReflects = TRUE;
|
||
|
m_fCheckedReflecting = FALSE;
|
||
|
|
||
|
m_pSimpleFrameSite = NULL;
|
||
|
m_pOleAdviseHolder = NULL;
|
||
|
m_pViewAdviseSink = NULL;
|
||
|
m_pDispAmbient = NULL;
|
||
|
|
||
|
m_fDirty = FALSE;
|
||
|
m_fModeFlagValid = FALSE;
|
||
|
m_fInPlaceActive = FALSE;
|
||
|
m_fInPlaceVisible = FALSE;
|
||
|
m_fUIActive = FALSE;
|
||
|
m_fSaveSucceeded = FALSE;
|
||
|
m_fViewAdvisePrimeFirst = FALSE;
|
||
|
m_fViewAdviseOnlyOnce = FALSE;
|
||
|
m_fRunMode = FALSE;
|
||
|
m_fChangingExtents = FALSE;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::~COleControl
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// "We are all of us resigned to death; it's life we aren't resigned to."
|
||
|
// - Graham Greene (1904-91)
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
COleControl::~COleControl()
|
||
|
{
|
||
|
ASSERT(!m_hwnd, "We shouldn't have a window any more!");
|
||
|
|
||
|
if (m_hwndReflect) {
|
||
|
SetWindowLong(m_hwndReflect, GWL_USERDATA, 0);
|
||
|
DestroyWindow(m_hwndReflect);
|
||
|
}
|
||
|
|
||
|
// clean up all the pointers we're holding around.
|
||
|
//
|
||
|
QUICK_RELEASE(m_pClientSite);
|
||
|
QUICK_RELEASE(m_pControlSite);
|
||
|
QUICK_RELEASE(m_pInPlaceSite);
|
||
|
QUICK_RELEASE(m_pInPlaceFrame);
|
||
|
QUICK_RELEASE(m_pInPlaceUIWindow);
|
||
|
QUICK_RELEASE(m_pSimpleFrameSite);
|
||
|
QUICK_RELEASE(m_pOleAdviseHolder);
|
||
|
QUICK_RELEASE(m_pViewAdviseSink);
|
||
|
QUICK_RELEASE(m_pDispAmbient);
|
||
|
|
||
|
QUICK_RELEASE(m_pInPlaceSiteWndless);
|
||
|
}
|
||
|
|
||
|
#ifndef DEBUG
|
||
|
#pragma optimize("t", on)
|
||
|
#endif // DEBUG
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::InternalQueryInterface
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// derived-controls should delegate back to this when they decide to support
|
||
|
// additional interfaces
|
||
|
//
|
||
|
// Parameters:
|
||
|
// REFIID - [in] interface they want
|
||
|
// void ** - [out] where they want to put the resulting object ptr.
|
||
|
//
|
||
|
// Output:
|
||
|
// HRESULT - S_OK, E_NOINTERFACE
|
||
|
//
|
||
|
// Notes:
|
||
|
// - NOTE: this function is speed critical!!!!
|
||
|
//
|
||
|
HRESULT COleControl::InternalQueryInterface
|
||
|
(
|
||
|
REFIID riid,
|
||
|
void **ppvObjOut
|
||
|
)
|
||
|
{
|
||
|
switch (riid.Data1)
|
||
|
{
|
||
|
// private interface for prop page support
|
||
|
QI_INHERITS(this, IOleControl);
|
||
|
QI_INHERITS(this, IPointerInactive);
|
||
|
QI_INHERITS(this, IQuickActivate);
|
||
|
QI_INHERITS(this, IOleObject);
|
||
|
QI_INHERITS((IPersistStorage *)this, IPersist);
|
||
|
QI_INHERITS(this, IPersistStreamInit);
|
||
|
QI_INHERITS(this, IOleInPlaceObject);
|
||
|
QI_INHERITS(this, IOleInPlaceObjectWindowless);
|
||
|
QI_INHERITS((IOleInPlaceActiveObject *)this, IOleWindow);
|
||
|
QI_INHERITS(this, IOleInPlaceActiveObject);
|
||
|
QI_INHERITS(this, IViewObject);
|
||
|
QI_INHERITS(this, IViewObject2);
|
||
|
QI_INHERITS(this, IViewObjectEx);
|
||
|
QI_INHERITS(this, ISpecifyPropertyPages);
|
||
|
QI_INHERITS(this, IPersistStorage);
|
||
|
QI_INHERITS(this, IPersistPropertyBag);
|
||
|
QI_INHERITS(this, IProvideClassInfo);
|
||
|
QI_INHERITS(this, IControlPrv);
|
||
|
|
||
|
default:
|
||
|
goto NoInterface;
|
||
|
}
|
||
|
|
||
|
// we like the interface, so addref and return
|
||
|
//
|
||
|
((IUnknown *)(*ppvObjOut))->AddRef();
|
||
|
return S_OK;
|
||
|
|
||
|
NoInterface:
|
||
|
// delegate to super-class for automation interfaces, etc ...
|
||
|
//
|
||
|
return CAutomationObjectWEvents::InternalQueryInterface(riid, ppvObjOut);
|
||
|
}
|
||
|
|
||
|
#ifndef DEBUG
|
||
|
#pragma optimize("s", on)
|
||
|
#endif // DEBUG
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::BeforeDestroyObject [overridden]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// if we're in the process of shutting down and destroying ourselves, then we
|
||
|
// need to trash our window here so we can avoid pure virtual calls from
|
||
|
// within colecontol's destructor
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
void COleControl::BeforeDestroyObject
|
||
|
(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
if (m_hwnd) {
|
||
|
DestroyWindow(m_hwnd);
|
||
|
m_hwnd = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::GetPages [ISpecifyPropertyPages]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// returns a counted array with the guids for our property pages.
|
||
|
//
|
||
|
// parameters:
|
||
|
// CAUUID * - [out] where to put the counted array.
|
||
|
//
|
||
|
// Output:
|
||
|
// HRESULT
|
||
|
//
|
||
|
// NOtes:
|
||
|
//
|
||
|
STDMETHODIMP COleControl::GetPages
|
||
|
(
|
||
|
CAUUID *pPages
|
||
|
)
|
||
|
{
|
||
|
const GUID **pElems;
|
||
|
void *pv;
|
||
|
WORD x;
|
||
|
|
||
|
// if there are no property pages, this is actually pretty easy.
|
||
|
//
|
||
|
if (!CPROPPAGESOFCONTROL(m_ObjectType)) {
|
||
|
pPages->cElems = 0;
|
||
|
pPages->pElems = NULL;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
// fill out the Counted array, using IMalloc'd memory.
|
||
|
//
|
||
|
pPages->cElems = CPROPPAGESOFCONTROL(m_ObjectType);
|
||
|
pv = CoTaskMemAlloc(sizeof(GUID) * (pPages->cElems));
|
||
|
RETURN_ON_NULLALLOC(pv);
|
||
|
pPages->pElems = (GUID *)pv;
|
||
|
|
||
|
// loop through our array of pages and get 'em.
|
||
|
//
|
||
|
pElems = PPROPPAGESOFCONTROL(m_ObjectType);
|
||
|
for (x = 0; x < pPages->cElems; x++)
|
||
|
pPages->pElems[x] = *(pElems[x]);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::CreateInPlaceWindow
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// creates the window with which we will be working.
|
||
|
// yay.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// int - [in] left
|
||
|
// int - [in] top
|
||
|
// BOOL - [in] can we skip redrawing?
|
||
|
//
|
||
|
// Output:
|
||
|
// HWND
|
||
|
//
|
||
|
// Notes:
|
||
|
// - DANGER! DANGER! this function is protected so that anybody can call it
|
||
|
// from their control. however, people should be extremely careful of when
|
||
|
// and why they do this. preferably, this function would only need to be
|
||
|
// called by an end-control writer in design mode to take care of some
|
||
|
// hosting/painting issues. otherwise, the framework should be left to
|
||
|
// call it when it wants.
|
||
|
//
|
||
|
HWND COleControl::CreateInPlaceWindow
|
||
|
(
|
||
|
int x,
|
||
|
int y,
|
||
|
BOOL fNoRedraw
|
||
|
)
|
||
|
{
|
||
|
BOOL fVisible;
|
||
|
DWORD dwWindowStyle, dwExWindowStyle;
|
||
|
char szWindowTitle[128];
|
||
|
|
||
|
// if we've already got a window, do nothing.
|
||
|
//
|
||
|
if (m_hwnd)
|
||
|
return m_hwnd;
|
||
|
|
||
|
// get the user to register the class if it's not already
|
||
|
// been done. we have to critical section this since more than one thread
|
||
|
// can be trying to create this control
|
||
|
//
|
||
|
ENTERCRITICALSECTION1(&g_CriticalSection);
|
||
|
if (!CTLWNDCLASSREGISTERED(m_ObjectType)) {
|
||
|
if (!RegisterClassData()) {
|
||
|
LEAVECRITICALSECTION1(&g_CriticalSection);
|
||
|
return NULL;
|
||
|
} else
|
||
|
CTLWNDCLASSREGISTERED(m_ObjectType) = TRUE;
|
||
|
}
|
||
|
LEAVECRITICALSECTION1(&g_CriticalSection);
|
||
|
|
||
|
// let the user set up things like the window title, the
|
||
|
// style, and anything else they feel interested in fiddling
|
||
|
// with.
|
||
|
//
|
||
|
dwWindowStyle = dwExWindowStyle = 0;
|
||
|
szWindowTitle[0] = '\0';
|
||
|
if (!BeforeCreateWindow(&dwWindowStyle, &dwExWindowStyle, szWindowTitle))
|
||
|
return NULL;
|
||
|
|
||
|
dwWindowStyle |= (WS_CHILD | WS_CLIPSIBLINGS);
|
||
|
|
||
|
// create window visible if parent hidden (common case)
|
||
|
// otherwise, create hidden, then shown. this is a little subtle, but
|
||
|
// it makes sense eventually.
|
||
|
//
|
||
|
if (!m_hwndParent)
|
||
|
m_hwndParent = GetParkingWindow();
|
||
|
|
||
|
fVisible = IsWindowVisible(m_hwndParent);
|
||
|
|
||
|
// This one kinda sucks -- if a control is subclassed, and we're in
|
||
|
// a host that doesn't support Message Reflecting, we have to create
|
||
|
// the user window in another window which will do all the reflecting.
|
||
|
// VERY blech. [don't however, bother in design mode]
|
||
|
//
|
||
|
if (SUBCLASSWNDPROCOFCONTROL(m_ObjectType) && (m_hwndParent != GetParkingWindow())) {
|
||
|
// determine if the host supports message reflecting.
|
||
|
//
|
||
|
if (!m_fCheckedReflecting) {
|
||
|
VARIANT_BOOL f;
|
||
|
if (!GetAmbientProperty(DISPID_AMBIENT_MESSAGEREFLECT, VT_BOOL, &f) || !f)
|
||
|
m_fHostReflects = FALSE;
|
||
|
m_fCheckedReflecting = TRUE;
|
||
|
}
|
||
|
|
||
|
// if the host doesn't support reflecting, then we have to create
|
||
|
// an extra window around the control window, and then parent it
|
||
|
// off that.
|
||
|
//
|
||
|
if (!m_fHostReflects) {
|
||
|
ASSERT(m_hwndReflect == NULL, "Where'd this come from?");
|
||
|
m_hwndReflect = CreateReflectWindow(!fVisible, m_hwndParent, x, y, &m_Size);
|
||
|
if (!m_hwndReflect)
|
||
|
return NULL;
|
||
|
SetWindowLong(m_hwndReflect, GWL_USERDATA, (long)this);
|
||
|
dwWindowStyle |= WS_VISIBLE;
|
||
|
}
|
||
|
} else {
|
||
|
if (!fVisible)
|
||
|
dwWindowStyle |= WS_VISIBLE;
|
||
|
}
|
||
|
|
||
|
// we have to mutex the entire create window process since we need to use
|
||
|
// the s_pLastControlCreated to pass in the object pointer. nothing too
|
||
|
// serious
|
||
|
//
|
||
|
ENTERCRITICALSECTION2(&g_CriticalSection);
|
||
|
s_pLastControlCreated = this;
|
||
|
m_fCreatingWindow = TRUE;
|
||
|
|
||
|
// finally, go create the window, parenting it as appropriate.
|
||
|
//
|
||
|
m_hwnd = CreateWindowEx(dwExWindowStyle,
|
||
|
WNDCLASSNAMEOFCONTROL(m_ObjectType),
|
||
|
szWindowTitle,
|
||
|
dwWindowStyle,
|
||
|
(m_hwndReflect) ? 0 : x,
|
||
|
(m_hwndReflect) ? 0 : y,
|
||
|
m_Size.cx, m_Size.cy,
|
||
|
(m_hwndReflect) ? m_hwndReflect : m_hwndParent,
|
||
|
NULL, g_hInstance, NULL);
|
||
|
|
||
|
// clean up some variables, and leave the critical section
|
||
|
//
|
||
|
m_fCreatingWindow = FALSE;
|
||
|
s_pLastControlCreated = NULL;
|
||
|
LEAVECRITICALSECTION2(&g_CriticalSection);
|
||
|
|
||
|
if (m_hwnd) {
|
||
|
// let the derived-control do something if they so desire
|
||
|
//
|
||
|
if (!AfterCreateWindow()) {
|
||
|
DestroyWindow(m_hwnd);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// if we didn't create the window visible, show it now.
|
||
|
//
|
||
|
if (fVisible)
|
||
|
SetWindowPos(m_hwnd, NULL, 0, 0, 0, 0,
|
||
|
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW | ((fNoRedraw) ? SWP_NOREDRAW : 0));
|
||
|
}
|
||
|
|
||
|
return m_hwnd;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::SetInPlaceParent [helper]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// sets up the parent window for our control.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// HWND - [in] new parent window
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
void COleControl::SetInPlaceParent
|
||
|
(
|
||
|
HWND hwndParent
|
||
|
)
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
HWND hwndOld;
|
||
|
DWORD dw;
|
||
|
#endif
|
||
|
|
||
|
ASSERT(!m_pInPlaceSiteWndless, "This routine should only get called for windowed OLE controls");
|
||
|
|
||
|
if (m_hwndParent == hwndParent)
|
||
|
return;
|
||
|
|
||
|
m_hwndParent = hwndParent;
|
||
|
if (m_hwnd)
|
||
|
{
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
hwndOld =
|
||
|
#endif
|
||
|
SetParent(GetOuterWindow(), hwndParent);
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
if (hwndOld == NULL)
|
||
|
{
|
||
|
dw = GetLastError();
|
||
|
ASSERT(dw == 0, "SetParent failed");
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::ControlWindowProc
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// default window proc for an OLE Control. controls will have their own
|
||
|
// window proc called from this one, after some processing is done.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// - see win32sdk docs.
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
LRESULT CALLBACK COleControl::ControlWindowProc
|
||
|
(
|
||
|
HWND hwnd,
|
||
|
UINT msg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam
|
||
|
)
|
||
|
{
|
||
|
COleControl *pCtl = ControlFromHwnd(hwnd);
|
||
|
HRESULT hr;
|
||
|
LRESULT lResult = 0;
|
||
|
DWORD dwCookie;
|
||
|
BYTE fSimpleFrame = FALSE;
|
||
|
|
||
|
// if the value isn't a positive value, then it's in some special
|
||
|
// state [creation or destruction] this is safe because under win32,
|
||
|
// the upper 2GB of an address space aren't available.
|
||
|
//
|
||
|
if ((LONG)pCtl == 0) {
|
||
|
pCtl = s_pLastControlCreated;
|
||
|
SetWindowLong(hwnd, GWL_USERDATA, (LONG)pCtl);
|
||
|
pCtl->m_hwnd = hwnd;
|
||
|
} else if ((ULONG)pCtl == 0xffffffff) {
|
||
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
// this is unfortunate. if the control gets destroyed while processing a
|
||
|
// message [ie, 'End' in an event, etc ....], we need to be able to
|
||
|
// contine through to the end of this routine, past the post-processing.
|
||
|
// to do this, we need to force a ref count on the control to keep it
|
||
|
// around. blech
|
||
|
//
|
||
|
pCtl->ExternalAddRef();
|
||
|
|
||
|
// message preprocessing
|
||
|
//
|
||
|
if (pCtl->m_pSimpleFrameSite) {
|
||
|
hr = pCtl->m_pSimpleFrameSite->PreMessageFilter(hwnd, msg, wParam, lParam, &lResult, &dwCookie);
|
||
|
if (hr == S_FALSE) goto Done;
|
||
|
}
|
||
|
|
||
|
// for certain messages, do not call the user window proc. instead,
|
||
|
// we have something else we'd like to do.
|
||
|
//
|
||
|
switch (msg) {
|
||
|
case WM_PAINT:
|
||
|
{
|
||
|
// call the user's OnDraw routine.
|
||
|
//
|
||
|
PAINTSTRUCT ps;
|
||
|
RECT rc;
|
||
|
HDC hdc;
|
||
|
|
||
|
// if we're given an HDC, then use it
|
||
|
//
|
||
|
if (!wParam)
|
||
|
{
|
||
|
hdc = BeginPaint(hwnd, &ps);
|
||
|
}
|
||
|
else
|
||
|
hdc = (HDC)wParam;
|
||
|
|
||
|
GetClientRect(hwnd, &rc);
|
||
|
pCtl->OnDraw(DVASPECT_CONTENT, hdc, (RECTL *)&rc, NULL, NULL, TRUE);
|
||
|
|
||
|
if (!wParam)
|
||
|
{
|
||
|
EndPaint(hwnd, &ps);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_DESTROY:
|
||
|
pCtl->BeforeDestroyWindow();
|
||
|
|
||
|
// fall through so that controls will send this to the parent window class.
|
||
|
|
||
|
default:
|
||
|
// call the derived-control's window proc
|
||
|
//
|
||
|
lResult = pCtl->WindowProc(msg, wParam, lParam);
|
||
|
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
// message postprocessing
|
||
|
//
|
||
|
switch (msg) {
|
||
|
|
||
|
case WM_NCDESTROY:
|
||
|
|
||
|
// after this point, the window doesn't exist any more
|
||
|
//
|
||
|
SetWindowLong(hwnd, GWL_USERDATA, 0xffffffff);
|
||
|
|
||
|
// We've been destroyed so reset our parent to NULL, so that it gets regenerated when we're recreated
|
||
|
//
|
||
|
pCtl->m_hwndParent = NULL;
|
||
|
pCtl->m_hwnd = NULL;
|
||
|
break;
|
||
|
|
||
|
case WM_SETFOCUS:
|
||
|
case WM_KILLFOCUS:
|
||
|
// give the control site focus notification
|
||
|
//
|
||
|
if (pCtl->m_fInPlaceActive && pCtl->m_pControlSite)
|
||
|
pCtl->m_pControlSite->OnFocus(msg == WM_SETFOCUS);
|
||
|
break;
|
||
|
|
||
|
case WM_SIZE:
|
||
|
// a change in size is a change in view
|
||
|
//
|
||
|
if (!pCtl->m_fCreatingWindow)
|
||
|
pCtl->ViewChanged();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// lastly, simple frame postmessage processing
|
||
|
//
|
||
|
if (pCtl->m_pSimpleFrameSite)
|
||
|
pCtl->m_pSimpleFrameSite->PostMessageFilter(hwnd, msg, wParam, lParam, &lResult, dwCookie);
|
||
|
|
||
|
Done:
|
||
|
pCtl->ExternalRelease();
|
||
|
return lResult;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::SetFocus
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// we have to override this routine to get UI Activation correct.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// BOOL - [in] true means take, false release
|
||
|
//
|
||
|
// Output:
|
||
|
// BOOL
|
||
|
//
|
||
|
// Notes:
|
||
|
// - CONSIDER: this is pretty messy, and it's still not entirely clear
|
||
|
// what the ole control/focus story is.
|
||
|
//
|
||
|
BOOL COleControl::SetFocus
|
||
|
(
|
||
|
BOOL fGrab
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
HWND hwnd;
|
||
|
|
||
|
// first thing to do is check out UI Activation state, and then set
|
||
|
// focus [either with windows api, or via the host for windowless
|
||
|
// controls]
|
||
|
//
|
||
|
if (m_pInPlaceSiteWndless) {
|
||
|
if (!m_fUIActive && fGrab)
|
||
|
if (FAILED(InPlaceActivate(OLEIVERB_UIACTIVATE))) return FALSE;
|
||
|
|
||
|
hr = m_pInPlaceSiteWndless->SetFocus(fGrab);
|
||
|
return (hr == S_OK) ? TRUE : FALSE;
|
||
|
} else {
|
||
|
|
||
|
// we've got a window.
|
||
|
//
|
||
|
if (m_fInPlaceActive) {
|
||
|
hwnd = (fGrab) ? m_hwnd : m_hwndParent;
|
||
|
if (!m_fUIActive && fGrab)
|
||
|
return SUCCEEDED(InPlaceActivate(OLEIVERB_UIACTIVATE));
|
||
|
else
|
||
|
return (::SetFocus(hwnd) == hwnd);
|
||
|
} else
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// dead code
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// ReflectOcmMessage
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// Reflects window messages on to the child window.
|
||
|
//
|
||
|
// Parameters and Output:
|
||
|
// - see win32 sdk docs
|
||
|
//
|
||
|
// Returns: TRUE if an OCM_ message was reflect
|
||
|
// FALSE if no OCM_ message was reflected
|
||
|
//
|
||
|
// The return value from SendMessage is stored is returned in pLResult
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
BOOL COleControl::ReflectOcmMessage
|
||
|
(
|
||
|
HWND hwnd,
|
||
|
UINT msg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam,
|
||
|
LRESULT *pLResult
|
||
|
)
|
||
|
{
|
||
|
COleControl *pCtl;
|
||
|
|
||
|
ASSERT(pLResult, "RESULT pointer is NULL");
|
||
|
*pLResult = 0;
|
||
|
|
||
|
switch(msg)
|
||
|
{
|
||
|
case WM_COMMAND:
|
||
|
case WM_NOTIFY:
|
||
|
case WM_CTLCOLORBTN:
|
||
|
case WM_CTLCOLORDLG:
|
||
|
case WM_CTLCOLOREDIT:
|
||
|
case WM_CTLCOLORLISTBOX:
|
||
|
case WM_CTLCOLORMSGBOX:
|
||
|
case WM_CTLCOLORSCROLLBAR:
|
||
|
case WM_CTLCOLORSTATIC:
|
||
|
case WM_DRAWITEM:
|
||
|
case WM_MEASUREITEM:
|
||
|
case WM_DELETEITEM:
|
||
|
case WM_VKEYTOITEM:
|
||
|
case WM_CHARTOITEM:
|
||
|
case WM_COMPAREITEM:
|
||
|
case WM_HSCROLL:
|
||
|
case WM_VSCROLL:
|
||
|
case WM_PARENTNOTIFY:
|
||
|
pCtl = (COleControl *)GetWindowLong(hwnd, GWL_USERDATA);
|
||
|
if (pCtl)
|
||
|
{
|
||
|
*pLResult = SendMessage(pCtl->m_hwnd, OCM__BASE + msg, wParam, lParam);
|
||
|
return TRUE;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::ReflectWindowProc
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// reflects window messages on to the child window.
|
||
|
//
|
||
|
// Parameters and Output:
|
||
|
// - see win32 sdk docs
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
LRESULT CALLBACK COleControl::ReflectWindowProc
|
||
|
(
|
||
|
HWND hwnd,
|
||
|
UINT msg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam
|
||
|
)
|
||
|
{
|
||
|
LRESULT lResult;
|
||
|
COleControl *pCtl;
|
||
|
|
||
|
switch (msg) {
|
||
|
|
||
|
case WM_SETFOCUS:
|
||
|
pCtl = (COleControl *)GetWindowLong(hwnd, GWL_USERDATA);
|
||
|
if (pCtl)
|
||
|
{
|
||
|
return pCtl->SetFocus(TRUE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_SIZE:
|
||
|
pCtl = (COleControl *)GetWindowLong(hwnd, GWL_USERDATA);
|
||
|
if (pCtl != NULL)
|
||
|
::MoveWindow(pCtl->m_hwnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
|
||
|
// continue with default processing
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// If the message is reflected then return the result of the OCM_ message
|
||
|
//
|
||
|
if (ReflectOcmMessage(hwnd, msg, wParam, lParam, &lResult))
|
||
|
return lResult;
|
||
|
|
||
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::GetAmbientProperty [callable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// returns the value of an ambient property
|
||
|
//
|
||
|
// Parameters:
|
||
|
// DISPID - [in] property to get
|
||
|
// VARTYPE - [in] type of desired data
|
||
|
// void * - [out] where to put the data
|
||
|
//
|
||
|
// Output:
|
||
|
// BOOL - FALSE means didn't work.
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
BOOL COleControl::GetAmbientProperty
|
||
|
(
|
||
|
DISPID dispid,
|
||
|
VARTYPE vt,
|
||
|
void *pData
|
||
|
)
|
||
|
{
|
||
|
DISPPARAMS dispparams;
|
||
|
VARIANT v, v2;
|
||
|
HRESULT hr;
|
||
|
|
||
|
v.vt = VT_EMPTY;
|
||
|
v.lVal = 0;
|
||
|
v2.vt = VT_EMPTY;
|
||
|
v2.lVal = 0;
|
||
|
|
||
|
// get a pointer to the source of ambient properties.
|
||
|
//
|
||
|
if (!m_pDispAmbient) {
|
||
|
if (m_pClientSite)
|
||
|
m_pClientSite->QueryInterface(IID_IDispatch, (void **)&m_pDispAmbient);
|
||
|
|
||
|
if (!m_pDispAmbient)
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// now go and get the property into a variant.
|
||
|
//
|
||
|
memset(&dispparams, 0, sizeof(DISPPARAMS));
|
||
|
hr = m_pDispAmbient->Invoke(dispid, IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams,
|
||
|
&v, NULL, NULL);
|
||
|
if (FAILED(hr)) return FALSE;
|
||
|
|
||
|
// we've got the variant, so now go an coerce it to the type that the user
|
||
|
// wants. if the types are the same, then this will copy the stuff to
|
||
|
// do appropriate ref counting ...
|
||
|
//
|
||
|
hr = VariantChangeType(&v2, &v, 0, vt);
|
||
|
if (FAILED(hr)) {
|
||
|
VariantClear(&v);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// copy the data to where the user wants it
|
||
|
//
|
||
|
CopyMemory(pData, &(v2.lVal), g_rgcbDataTypeSize[vt]);
|
||
|
VariantClear(&v);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::GetAmbientFont [callable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// gets the current font for the user.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// IFont ** - [out] where to put the font.
|
||
|
//
|
||
|
// Output:
|
||
|
// BOOL - FALSE means couldn't get it.
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
BOOL COleControl::GetAmbientFont
|
||
|
(
|
||
|
IFont **ppFont
|
||
|
)
|
||
|
{
|
||
|
IDispatch *pFontDisp;
|
||
|
|
||
|
// we don't have to do much here except get the ambient property and QI
|
||
|
// it for the user.
|
||
|
//
|
||
|
*ppFont = NULL;
|
||
|
if (!GetAmbientProperty(DISPID_AMBIENT_FONT, VT_DISPATCH, &pFontDisp))
|
||
|
return FALSE;
|
||
|
|
||
|
pFontDisp->QueryInterface(IID_IFont, (void **)ppFont);
|
||
|
pFontDisp->Release();
|
||
|
return (*ppFont) ? TRUE : FALSE;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::DesignMode
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// returns TRUE if we're in Design mode.
|
||
|
//
|
||
|
// Output:
|
||
|
// BOOL - true is design mode, false is run mode
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
BOOL COleControl::DesignMode
|
||
|
(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
VARIANT_BOOL f;
|
||
|
|
||
|
// if we don't already know our run mode, go and get it. we'll assume
|
||
|
// it's true unless told otherwise
|
||
|
//
|
||
|
if (!m_fModeFlagValid) {
|
||
|
f = TRUE;
|
||
|
if (!GetAmbientProperty(DISPID_AMBIENT_USERMODE, VT_BOOL, &f))
|
||
|
return FALSE;
|
||
|
m_fModeFlagValid = TRUE;
|
||
|
m_fRunMode = f;
|
||
|
}
|
||
|
|
||
|
return !m_fRunMode;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::AfterCreateWindow [overridable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// something the user can pay attention to
|
||
|
//
|
||
|
// Output:
|
||
|
// BOOL - false means fatal error, can't continue
|
||
|
// Notes:
|
||
|
//
|
||
|
BOOL COleControl::AfterCreateWindow
|
||
|
(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::BeforeCreateWindow [overridable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// called just before we create a window. the user should register their
|
||
|
// window class here, and set up any other things, such as the title of
|
||
|
// the window, and/or sytle bits, etc ...
|
||
|
//
|
||
|
// Parameters:
|
||
|
// DWORD * - [out] dwWindowFlags
|
||
|
// DWORD * - [out] dwExWindowFlags
|
||
|
// LPSTR - [out] name of window to create
|
||
|
//
|
||
|
// Output:
|
||
|
// BOOL - false means fatal error, can't continue
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
BOOL COleControl::BeforeCreateWindow
|
||
|
(
|
||
|
DWORD *pdwWindowStyle,
|
||
|
DWORD *pdwExWindowStyle,
|
||
|
LPSTR pszWindowTitle
|
||
|
)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::InvalidateControl [callable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
void COleControl::InvalidateControl
|
||
|
(
|
||
|
LPCRECT lpRect
|
||
|
)
|
||
|
{
|
||
|
if (m_fInPlaceActive)
|
||
|
OcxInvalidateRect(lpRect, TRUE);
|
||
|
else
|
||
|
ViewChanged();
|
||
|
|
||
|
// CONSIDER: one might want to call pOleAdviseHolder->OnDataChanged() here
|
||
|
// if there was support for IDataObject
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::SetControlSize [callable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// sets the control size. they'll give us the size in pixels. we've got to
|
||
|
// convert them back to HIMETRIC before passing them on!
|
||
|
//
|
||
|
// Parameters:
|
||
|
// SIZEL * - [in] new size
|
||
|
//
|
||
|
// Output:
|
||
|
// BOOL
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
BOOL COleControl::SetControlSize
|
||
|
(
|
||
|
SIZEL *pSize
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
SIZEL slHiMetric;
|
||
|
|
||
|
PixelToHiMetric(pSize, &slHiMetric);
|
||
|
hr = SetExtent(DVASPECT_CONTENT, &slHiMetric);
|
||
|
return (FAILED(hr)) ? FALSE : TRUE;
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::RecreateControlWindow [callable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// called by a [subclassed, typically] control to recreate it's control
|
||
|
// window.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// none
|
||
|
//
|
||
|
// Output:
|
||
|
// HRESULT
|
||
|
//
|
||
|
// Notes:
|
||
|
// - NOTE: USE ME EXTREMELY SPARINGLY! THIS IS AN EXTREMELY EXPENSIVE
|
||
|
// OPERATION!
|
||
|
//
|
||
|
HRESULT COleControl::RecreateControlWindow
|
||
|
(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
HWND hwndPrev;
|
||
|
BYTE fUIActive = m_fUIActive;
|
||
|
|
||
|
// we need to correctly preserve the control's position within the
|
||
|
// z-order here.
|
||
|
//
|
||
|
if (m_hwnd)
|
||
|
hwndPrev = ::GetWindow(m_hwnd, GW_HWNDPREV);
|
||
|
|
||
|
// if we're in place active, then we have to deactivate, and reactivate
|
||
|
// ourselves with the new window ...
|
||
|
//
|
||
|
if (m_fInPlaceActive) {
|
||
|
|
||
|
hr = InPlaceDeactivate();
|
||
|
RETURN_ON_FAILURE(hr);
|
||
|
hr = InPlaceActivate((fUIActive) ? OLEIVERB_UIACTIVATE : OLEIVERB_INPLACEACTIVATE);
|
||
|
RETURN_ON_FAILURE(hr);
|
||
|
|
||
|
} else if (m_hwnd) {
|
||
|
DestroyWindow(m_hwnd);
|
||
|
if (m_hwndReflect) {
|
||
|
DestroyWindow(m_hwndReflect);
|
||
|
m_hwndReflect = NULL;
|
||
|
}
|
||
|
|
||
|
CreateInPlaceWindow(0, 0, FALSE);
|
||
|
}
|
||
|
|
||
|
// restore z-order position
|
||
|
//
|
||
|
if (m_hwnd)
|
||
|
SetWindowPos(m_hwnd, hwndPrev, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
|
||
|
|
||
|
return m_hwnd ? S_OK : E_FAIL;
|
||
|
}
|
||
|
|
||
|
// from Globals.C. don't need to mutex it here since we only read it.
|
||
|
//
|
||
|
extern HINSTANCE g_hInstResources;
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::GetResourceHandle [callable]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// gets the HINSTANCE of the DLL where the control should get resources
|
||
|
// from. implemented in such a way to support satellite DLLs.
|
||
|
//
|
||
|
// Output:
|
||
|
// HINSTANCE
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
HINSTANCE COleControl::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.
|
||
|
//
|
||
|
// crit sect this for apartment threading support.
|
||
|
//
|
||
|
ENTERCRITICALSECTION1(&g_CriticalSection);
|
||
|
if (!g_fHaveLocale)
|
||
|
// if we can't get the ambient locale id, then we'll just continue
|
||
|
// with the globally set up value.
|
||
|
//
|
||
|
if (!GetAmbientProperty(DISPID_AMBIENT_LOCALEID, VT_I4, &g_lcidLocale))
|
||
|
goto Done;
|
||
|
|
||
|
g_fHaveLocale = TRUE;
|
||
|
|
||
|
Done:
|
||
|
LEAVECRITICALSECTION1(&g_CriticalSection);
|
||
|
return ::GetResourceHandle();
|
||
|
}
|
||
|
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// COleControl::GetControl [IControlPrv]
|
||
|
//=--------------------------------------------------------------------------=
|
||
|
// Returns a pointer to the COleControl class
|
||
|
//
|
||
|
HRESULT COleControl::GetControl(COleControl **ppOleControl)
|
||
|
{
|
||
|
CHECK_POINTER(ppOleControl);
|
||
|
*ppOleControl = this;
|
||
|
(*ppOleControl)->AddRef();
|
||
|
return S_OK;
|
||
|
}
|