mirror of https://github.com/tongzx/nt5src
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.
1710 lines
46 KiB
1710 lines
46 KiB
#include "priv.h"
|
|
#include "sccls.h"
|
|
|
|
#include <mluisupp.h>
|
|
|
|
#define IPSMSG(psz) TraceMsg(TF_SHDCONTROL, "she TR-IPS::%s called", psz)
|
|
#define IPSMSG2(psz, hres) TraceMsg(TF_SHDCONTROL, "she TR-IPS::%s %x", psz, hres)
|
|
#define IPSMSG3(pszName, psz) TraceMsg(TF_SHDCONTROL, "she TR-IPS::%s:%s called", pszName,psz)
|
|
#define IOOMSG(psz) TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s called", psz)
|
|
#define IOOMSGX(psz, hres) TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s returning %x", psz, hres)
|
|
#define IOOMSG2(psz, i) TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s called with (%d)", psz, i)
|
|
#define IOOMSG3(psz, i, j) TraceMsg(TF_SHDCONTROL, "she TR-IOO::%s called with (%d, %d)", psz, i, j)
|
|
#define IVOMSG(psz) TraceMsg(TF_SHDCONTROL, "she TR-IVO::%s called", psz)
|
|
#define IVOMSG2(psz, i) TraceMsg(TF_SHDCONTROL, "she TR-IVO::%s called with (%d)", psz, i)
|
|
#define IVOMSG3(psz, i, j) TraceMsg(TF_SHDCONTROL, "she TR-IVO::%s with (%d, %d)", psz, i, j)
|
|
#define CCDMSG(psz, punk) TraceMsg(TF_SHDCONTROL, "she TR-CSV::%s called punk=%x", psz, punk)
|
|
#define IDTMSG(psz) TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s called", psz)
|
|
#define IDTMSG2(psz, i) TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s called with %d", psz, i)
|
|
#define IDTMSG3(psz, x) TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s %x", psz, x)
|
|
#define IDTMSG4(psz, i, j) TraceMsg(TF_SHDCONTROL, "she TR-IDT::%s called with %x,%x", psz, i, j)
|
|
#define IIPMSG(psz) TraceMsg(TF_SHDCONTROL, "she TR-IOIPO::%s called", psz)
|
|
#define IIAMSG(psz) TraceMsg(TF_SHDCONTROL, "she TR-IOIPAO::%s called", psz)
|
|
#define IEVMSG(psz, i, j, ps) TraceMsg(TF_SHDCONTROL, "she TR-IEV::%s called celt=%d, _iCur=%d, %x", psz, i, j, ps)
|
|
|
|
const TCHAR c_szShellEmbedding[] = TEXT("Shell Embedding");
|
|
|
|
//
|
|
// A special lindex value to be passed to ::Draw member indicating
|
|
// that it is an internal call from ::GetData
|
|
//
|
|
#define LINDEX_INTERNAL 12345
|
|
|
|
// REVIEW: We may want to use the functions in UTIL.C -- they look more efficient...
|
|
//
|
|
//=========================================================================
|
|
// Helper functions
|
|
//=========================================================================
|
|
|
|
#define HIM_PER_IN 2540
|
|
|
|
int g_iXppli = 0;
|
|
int g_iYppli = 0;
|
|
|
|
void GetLogPixels()
|
|
{
|
|
HDC hdc = GetDC(NULL);
|
|
if (hdc)
|
|
{
|
|
g_iXppli = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
g_iYppli = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
}
|
|
|
|
// Scalar conversion of MM_HIMETRIC to MM_TEXT
|
|
void MetricToPixels(SIZEL* psize)
|
|
{
|
|
ASSERT(g_iXppli);
|
|
|
|
psize->cx = MulDiv(psize->cx, g_iXppli, HIM_PER_IN);
|
|
psize->cy = MulDiv(psize->cy, g_iYppli, HIM_PER_IN);
|
|
}
|
|
|
|
// Scalar conversion of MM_TEXT to MM_HIMETRIC
|
|
void PixelsToMetric(SIZEL* psize)
|
|
{
|
|
ASSERT(g_iYppli);
|
|
|
|
psize->cx = MulDiv(psize->cx, HIM_PER_IN, g_iXppli);
|
|
psize->cy = MulDiv(psize->cy, HIM_PER_IN, g_iYppli);
|
|
}
|
|
|
|
|
|
//=========================================================================
|
|
// CShellEmbedding implementaiton
|
|
//=========================================================================
|
|
HRESULT CShellEmbedding::v_InternalQueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CShellEmbedding, IPersist),
|
|
QITABENT(CShellEmbedding, IOleObject),
|
|
QITABENT(CShellEmbedding, IViewObject2),
|
|
QITABENTMULTI(CShellEmbedding, IViewObject, IViewObject2),
|
|
QITABENT(CShellEmbedding, IDataObject),
|
|
QITABENT(CShellEmbedding, IOleInPlaceObject),
|
|
QITABENTMULTI(CShellEmbedding, IOleWindow, IOleInPlaceObject),
|
|
QITABENT(CShellEmbedding, IOleInPlaceActiveObject),
|
|
QITABENT(CShellEmbedding, IInternetSecurityMgrSite),
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
CShellEmbedding::CShellEmbedding(IUnknown* punkOuter, LPCOBJECTINFO poi, const OLEVERB* pverbs)
|
|
: _pverbs(pverbs)
|
|
, _nActivate(OC_DEACTIVE)
|
|
, CAggregatedUnknown(punkOuter)
|
|
{
|
|
TraceMsg(TF_SHDCONTROL, "ctor CShellEmbedding %x", this);
|
|
|
|
DllAddRef();
|
|
_RegisterWindowClass();
|
|
_pObjectInfo = poi;
|
|
_size.cx = 50;
|
|
_size.cy = 20;
|
|
|
|
// make sure some globals are set
|
|
GetLogPixels();
|
|
|
|
// let our logical size match our physical size
|
|
_sizeHIM = _size;
|
|
PixelsToMetric(&_sizeHIM);
|
|
}
|
|
|
|
CShellEmbedding::~CShellEmbedding()
|
|
{
|
|
ASSERT(_hwnd==NULL);
|
|
// IE v 4.1 bug 44541. In an Office 97 user form, we were seeing this destructor get entered
|
|
// with a non-null hwnd, which would cause a fault the next time the hwnd received a message.
|
|
//
|
|
if (_hwnd)
|
|
{
|
|
DestroyWindow(_hwnd);
|
|
_hwnd = NULL;
|
|
}
|
|
ASSERT(_hwndChild==NULL);
|
|
ASSERT(_pcli==NULL);
|
|
ASSERT(_pipsite==NULL);
|
|
ASSERT(_pipframe==NULL);
|
|
ASSERT(_pipui==NULL);
|
|
|
|
//
|
|
// WARNING: Don't call any of virtual functions of this object
|
|
// itself for clean-up purpose. The Vtable is alreadly adjusted
|
|
// and we won't be able to perform any full clean up. Do it
|
|
// right before you delete in CShellEmbedding::CSVInner::Release.
|
|
//
|
|
TraceMsg(TF_SHDCONTROL, "dtor CShellEmbedding %x", this);
|
|
|
|
// Warning: if the client site has not been released do not release the advise
|
|
// object as some applications like VC5 will fault on this...
|
|
if (_padv) {
|
|
_padv->OnClose();
|
|
if (!_pcli)
|
|
ATOMICRELEASE(_padv);
|
|
}
|
|
|
|
if (!_pcli)
|
|
{
|
|
ATOMICRELEASE(_pdah);
|
|
ATOMICRELEASE(_poah);
|
|
}
|
|
ATOMICRELEASE(_pstg);
|
|
ATOMICRELEASE(_pcliHold);
|
|
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
// **************** IPersist ****************
|
|
HRESULT CShellEmbedding::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSIDOFOBJECT(this);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL CShellEmbedding::_ShouldDraw(LONG lindex)
|
|
{
|
|
// Don't draw if the window is visible.
|
|
return ! (_pipsite && lindex!=LINDEX_INTERNAL);
|
|
}
|
|
|
|
// **************** IViewObject ****************
|
|
HRESULT CShellEmbedding::Draw(
|
|
DWORD dwDrawAspect,
|
|
LONG lindex,
|
|
void *pvAspect,
|
|
DVTARGETDEVICE *ptd,
|
|
HDC hdcTargetDev,
|
|
HDC hdcDraw,
|
|
LPCRECTL lprcBounds,
|
|
LPCRECTL lprcWBounds,
|
|
BOOL ( __stdcall *pfnContinue )(ULONG_PTR dwContinue),
|
|
ULONG_PTR dwContinue)
|
|
{
|
|
IVOMSG3(TEXT("Draw called"), lprcBounds->top, lprcBounds->bottom);
|
|
|
|
// WARNING: this looks wrong to me -- I think we should always respond
|
|
// to a Draw request, as the hdcDraw may not be the screen!
|
|
//
|
|
// Don't draw if the window is visible.
|
|
if (!_ShouldDraw(lindex)) {
|
|
return S_OK;
|
|
}
|
|
|
|
if (_hwnd) {
|
|
int iDC = SaveDC(hdcDraw);
|
|
RECTL rcBounds = *lprcBounds;
|
|
::LPtoDP(hdcDraw, (LPPOINT)&rcBounds, 2);
|
|
IVOMSG3(TEXT("Draw DP=="), rcBounds.top, rcBounds.bottom);
|
|
TraceMsg(TF_SHDCONTROL, "she Draw cx=%d cy=%d", rcBounds.right-rcBounds.left, rcBounds.bottom-rcBounds.top);
|
|
|
|
SetMapMode(hdcDraw, MM_TEXT); // make it 1:1
|
|
SetMapMode(hdcDraw, MM_ANISOTROPIC); // inherit call from MM_TEXT
|
|
POINT pt;
|
|
SetViewportOrgEx(hdcDraw, rcBounds.left, rcBounds.top, &pt);
|
|
|
|
// APPCOMPAT: WordPad does a GetExtent to get the size and passes that in as lprcBounds
|
|
// *without* doing a SetExtent, so when we resize larger (due to a BROWSE verb) _hwnd
|
|
// is still the old size. So we IntersectClipRect to _hwnd but WordPad draws the border
|
|
// to rcBounds. Ugly.
|
|
|
|
RECT rc;
|
|
GetClientRect(_hwnd, &rc);
|
|
IntersectClipRect(hdcDraw, 0, 0, rc.right, rc.bottom);
|
|
SendMessage(_hwnd, WM_PRINT, (WPARAM)hdcDraw,
|
|
PRF_NONCLIENT|PRF_CLIENT|PRF_CHILDREN|PRF_ERASEBKGND);
|
|
|
|
SetViewportOrgEx(hdcDraw, pt.x, pt.y, NULL);
|
|
RestoreDC(hdcDraw, iDC);
|
|
return S_OK;
|
|
}
|
|
|
|
return OLE_E_BLANK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetColorSet(
|
|
DWORD dwDrawAspect,
|
|
LONG lindex,
|
|
void *pvAspect,
|
|
DVTARGETDEVICE *ptd,
|
|
HDC hicTargetDev,
|
|
LOGPALETTE **ppColorSet)
|
|
{
|
|
IVOMSG(TEXT("GetColorSet"));
|
|
return S_FALSE; // Indicating that the object doesn't care
|
|
}
|
|
|
|
HRESULT CShellEmbedding::Freeze(
|
|
DWORD dwDrawAspect,
|
|
LONG lindex,
|
|
void *pvAspect,
|
|
DWORD *pdwFreeze)
|
|
{
|
|
IVOMSG(TEXT("Freeze"));
|
|
*pdwFreeze = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::Unfreeze(DWORD dwFreeze)
|
|
{
|
|
IVOMSG(TEXT("Unfreeze"));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetAdvise(
|
|
DWORD aspects,
|
|
DWORD advf,
|
|
IAdviseSink *pAdvSink)
|
|
{
|
|
IVOMSG2(TEXT("SetAdvise"), pAdvSink);
|
|
|
|
if (advf & ~(ADVF_ONLYONCE | ADVF_PRIMEFIRST))
|
|
return E_INVALIDARG;
|
|
|
|
if (pAdvSink != _padv)
|
|
{
|
|
ATOMICRELEASE(_padv);
|
|
|
|
if (pAdvSink)
|
|
{
|
|
_padv = pAdvSink;
|
|
_padv->AddRef();
|
|
}
|
|
}
|
|
|
|
_asp = aspects;
|
|
_advf = advf;
|
|
|
|
if (advf & ADVF_PRIMEFIRST)
|
|
_SendAdvise(OBJECTCODE_VIEWCHANGED);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetAdvise(
|
|
DWORD *pAspects,
|
|
DWORD *pAdvf,
|
|
IAdviseSink **ppAdvSink)
|
|
{
|
|
IVOMSG(TEXT("GetAdvise"));
|
|
if (pAspects) {
|
|
*pAspects = _asp;
|
|
}
|
|
|
|
if (pAdvf) {
|
|
*pAdvf = _advf;
|
|
}
|
|
|
|
if (ppAdvSink) {
|
|
*ppAdvSink = _padv;
|
|
if (_padv) {
|
|
_padv->AddRef();
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// **************** IViewObject2 ****************
|
|
HRESULT CShellEmbedding::GetExtent(
|
|
DWORD dwDrawAspect,
|
|
LONG lindex,
|
|
DVTARGETDEVICE *ptd,
|
|
LPSIZEL lpsizel)
|
|
{
|
|
TraceMsg(TF_SHDCONTROL, "she GetExtent cx=%d cy=%d", _size.cx, _size.cy);
|
|
lpsizel->cx = _size.cx;
|
|
lpsizel->cy = _size.cy;
|
|
PixelsToMetric(lpsizel);
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// **************** IOleObject ****************
|
|
//
|
|
|
|
void CShellEmbedding::_OnSetClientSite()
|
|
{
|
|
if (_pcli)
|
|
{
|
|
IOleInPlaceSite* pipsite;
|
|
if (SUCCEEDED(_pcli->QueryInterface(IID_IOleInPlaceSite, (LPVOID*)&pipsite)))
|
|
{
|
|
_CreateWindowOrSetParent(pipsite);
|
|
pipsite->Release();
|
|
}
|
|
}
|
|
else if (_hwnd)
|
|
{
|
|
DestroyWindow(_hwnd);
|
|
_hwnd = NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetClientSite(IOleClientSite *pClientSite)
|
|
{
|
|
IOOMSG2(TEXT("SetClientSite"), pClientSite);
|
|
|
|
// If I have a client site on hold, get rid of it.
|
|
//
|
|
ATOMICRELEASE(_pcliHold);
|
|
|
|
if (_pcli == pClientSite)
|
|
{
|
|
// mshtml is hitting their initialization code twice
|
|
// no need for us to do anything here.
|
|
}
|
|
else
|
|
{
|
|
ATOMICRELEASE(_pcli);
|
|
ATOMICRELEASE(_pipsite);
|
|
ATOMICRELEASE(_pipframe);
|
|
ATOMICRELEASE(_pipui);
|
|
|
|
_pcli = pClientSite;
|
|
|
|
if (_pcli)
|
|
_pcli->AddRef();
|
|
|
|
_OnSetClientSite();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// This function create _hwnd (the parent of this embedding) if it not
|
|
// created yet. Otherwise, it simply SetParent appropriately.
|
|
//
|
|
// NOTE: When this object is embedded in PowerPoint 95, the first
|
|
// CreateWindowEx fails when this function if called from SetClientSite
|
|
// for some unknown reason.
|
|
// It, however, succeeds when it is called from DoVerb. We should find
|
|
// it out.
|
|
//
|
|
HRESULT CShellEmbedding::_CreateWindowOrSetParent(IOleWindow* pwin)
|
|
{
|
|
HWND hwndParent = NULL;
|
|
HRESULT hres = S_OK;
|
|
|
|
//
|
|
// NOTES: Unlike IE3.0, we don't fail even if pwin->GetWindow fails.
|
|
// It allows Trident to SetClientSite (and Navigate) before they
|
|
// are In-place Activated. In that case (hwndParent==NULL), we
|
|
// create a top-level window hidden and use it for navigation.
|
|
// When we are being InPlaceActivated, we hit this function again
|
|
// and set the parent (and window styles) correctly. Notice that
|
|
// we need to set WS_POPUP to avoid the Window Manager automatically
|
|
// add other random styles for verlapped window.
|
|
//
|
|
pwin->GetWindow(&hwndParent);
|
|
#ifdef DEBUG
|
|
// Pretend that GetWindow failed here.
|
|
if (_hwnd==NULL && (g_dwPrototype & 0x00000200))
|
|
{
|
|
TraceMsg(DM_TRACE, "CSE::_CreateWindowOrSetParent pretend unsuccessful GetWindow");
|
|
hwndParent = NULL;
|
|
}
|
|
#endif
|
|
|
|
_fOpen = TRUE;
|
|
|
|
if (_hwnd)
|
|
{
|
|
SetParentHwnd(_hwnd, hwndParent);
|
|
|
|
}
|
|
else
|
|
{
|
|
_hwnd = SHNoFusionCreateWindowEx(
|
|
WS_EX_WINDOWEDGE,
|
|
c_szShellEmbedding, NULL,
|
|
(hwndParent ?
|
|
(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP)
|
|
: (WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP)),
|
|
0, 0, _rcPos.right - _rcPos.left, _rcPos.bottom - _rcPos.top,
|
|
hwndParent,
|
|
(HMENU)0,
|
|
HINST_THISDLL,
|
|
(LPVOID)SAFECAST(this, CImpWndProc*));
|
|
|
|
if (!_hwnd)
|
|
{
|
|
hres = E_FAIL;
|
|
TraceMsg(TF_SHDCONTROL, "sdv TR-IOO::_CreateWindowOrSetParent CreateWindowEx failed (%d)", GetLastError());
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
HRESULT CShellEmbedding::GetClientSite(IOleClientSite **ppClientSite)
|
|
{
|
|
IOOMSG(TEXT("GetClientSite"));
|
|
*ppClientSite = _pcli;
|
|
|
|
if (_pcli) {
|
|
_pcli->AddRef();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetHostNames(
|
|
LPCOLESTR szContainerApp,
|
|
LPCOLESTR szContainerObj)
|
|
{
|
|
IOOMSG(TEXT("SetHostNames"));
|
|
// We are not interested in host name
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// A container application calls IOleObject::Close when it wants
|
|
// to move the object from a running to a loaded state. Following
|
|
// such a call, the object still appears in its container but is
|
|
// not open for editing. Calling IOleObject::Close on an object
|
|
// that is loaded but not running has no effect.
|
|
//
|
|
HRESULT CShellEmbedding::Close(DWORD dwSaveOption)
|
|
{
|
|
IOOMSG2(TEXT("Close"), dwSaveOption);
|
|
// Change the state of object back to TEXT("loaded") state.
|
|
|
|
BOOL fSave = FALSE;
|
|
if (_fDirty &&
|
|
((OLECLOSE_SAVEIFDIRTY==dwSaveOption)
|
|
|| (dwSaveOption==OLECLOSE_PROMPTSAVE))) {
|
|
fSave = TRUE;
|
|
}
|
|
|
|
if (fSave) {
|
|
_SendAdvise(OBJECTCODE_SAVEOBJECT);
|
|
_SendAdvise(OBJECTCODE_SAVED);
|
|
}
|
|
|
|
_SendAdvise(OBJECTCODE_CLOSED);
|
|
_fOpen = FALSE;
|
|
|
|
// "loaded but not running" is confusing wording... If you look
|
|
// at the OLEIVERB_HIDE comment in _OnActivateChange, it mentions
|
|
// that OLEIVERB_HIDE puts it in the state "just after loading"
|
|
// and puts us in OC_DEACTIVE state. Let's do that here as well.
|
|
//
|
|
// it just came to my awareness that OCs UIDeactivate,
|
|
// not IPDeactivate...
|
|
_DoActivateChange(NULL, OC_DEACTIVE, FALSE);
|
|
|
|
// It seems like some containers (Trident frame set) don't
|
|
// do a SetClientSite(NULL) on us, so do it here. Old code here
|
|
// did a DestroyWindow(_hwnd), which SetClientSite(NULL) will do.
|
|
// NOTE: VB does call SetClientSite, but they do it after Close.
|
|
|
|
// If we already have one on hold, release it.
|
|
//
|
|
ATOMICRELEASE(_pcliHold);
|
|
|
|
// Hold onto our client site. We may need it if we're DoVerbed, as Office tends to do.
|
|
//
|
|
|
|
IOleClientSite *pOleClientSite = _pcli;
|
|
if (pOleClientSite)
|
|
{
|
|
pOleClientSite->AddRef();
|
|
}
|
|
|
|
SetClientSite(NULL);
|
|
|
|
_pcliHold = pOleClientSite;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetMoniker(
|
|
DWORD dwWhichMoniker,
|
|
IMoniker *pmk)
|
|
{
|
|
IOOMSG(TEXT("SetMoniker"));
|
|
// We are not interested in moniker.
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetMoniker(
|
|
DWORD dwAssign,
|
|
DWORD dwWhichMoniker,
|
|
IMoniker **ppmk)
|
|
{
|
|
IOOMSG(TEXT("GetMoniker"));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::InitFromData(
|
|
IDataObject *pDataObject,
|
|
BOOL fCreation,
|
|
DWORD dwReserved)
|
|
{
|
|
IOOMSG(TEXT("InitFromData"));
|
|
// LATER: We may want to implement this later.
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetClipboardData(
|
|
DWORD dwReserved,
|
|
IDataObject **ppDataObject)
|
|
{
|
|
IOOMSG(TEXT("GetClipboardData"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::DoVerb(
|
|
LONG iVerb,
|
|
LPMSG lpmsg,
|
|
IOleClientSite *pActiveSite,
|
|
LONG lindex,
|
|
HWND hwndParent,
|
|
LPCRECT lprcPosRect)
|
|
{
|
|
IOOMSG2(TEXT("DoVerb"), iVerb);
|
|
HRESULT hres = S_OK;
|
|
|
|
// If I don't have a client site, but I have one on "hold", I need to set it up again.
|
|
//
|
|
if (_pcli == NULL
|
|
&& _pcliHold)
|
|
{
|
|
IOleClientSite *pOleClientSite = _pcliHold;
|
|
_pcliHold = NULL;
|
|
SetClientSite(pOleClientSite);
|
|
pOleClientSite->Release();
|
|
}
|
|
|
|
switch(iVerb)
|
|
{
|
|
case OLEIVERB_HIDE:
|
|
hres = _DoActivateChange(NULL, OC_DEACTIVE, FALSE);
|
|
break;
|
|
|
|
case OLEIVERB_OPEN:
|
|
hres = E_FAIL;
|
|
break;
|
|
|
|
case OLEIVERB_PRIMARY:
|
|
case OLEIVERB_SHOW:
|
|
if (_pipsite) {
|
|
return S_OK;
|
|
}
|
|
// Fall through
|
|
case OLEIVERB_UIACTIVATE:
|
|
hres = _DoActivateChange(pActiveSite, OC_UIACTIVE, TRUE); //TRUE => We want to force UIACTIVE even if we are already active.
|
|
break;
|
|
|
|
case OLEIVERB_INPLACEACTIVATE:
|
|
hres = _DoActivateChange(pActiveSite, OC_INPLACEACTIVE, FALSE);
|
|
break;
|
|
|
|
default:
|
|
hres = E_FAIL; // OLEOBJ_S_INVALDVERB;
|
|
break;
|
|
}
|
|
|
|
IOOMSGX(TEXT("DoVerb"), hres);
|
|
return hres;
|
|
}
|
|
|
|
//
|
|
// fForce == TRUE indicates that we need to call _OnActivateChange even if we
|
|
// are already in OC_UIACITVE state.
|
|
//
|
|
HRESULT CShellEmbedding::_DoActivateChange(IOleClientSite* pActiveSite, UINT uState, BOOL fForce)
|
|
{
|
|
if (uState == _nActivate)
|
|
{
|
|
// in general, we have nothing to do if we're already in
|
|
// the correct state. HOWEVER, OLEIVERB_UIACTIVATE is supposed
|
|
// to set focus if we (or our children?) don't currently have it.
|
|
// Fall into _OnActivateChange so CWebBrowserOC can tell the
|
|
// base browser to go uiactive.
|
|
//
|
|
if ((uState != OC_UIACTIVE) || !fForce)
|
|
return S_OK;
|
|
}
|
|
|
|
#define STATETOSTRING(n) (n==OC_DEACTIVE ? TEXT("OC_DEACTIVE") : (n==OC_INPLACEACTIVE ? TEXT("OC_INPLACEACTIVE") : (n== OC_UIACTIVE ? TEXT("OC_UIACTIVE") : TEXT("ERROR"))))
|
|
TraceMsg(TF_SHDCONTROL, "she _DoActivateChange from %s to %s", STATETOSTRING(_nActivate), STATETOSTRING(uState));
|
|
|
|
return _OnActivateChange(pActiveSite, uState);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::_OnActivateChange(IOleClientSite* pActiveSite, UINT uState)
|
|
{
|
|
if (uState != _nActivate)
|
|
{
|
|
// mark us in our new state immediately. this avoids recursion death with bad containers (ipstool)
|
|
UINT uOldState = _nActivate;
|
|
_nActivate = uState;
|
|
|
|
if (uOldState == OC_DEACTIVE) // going from deactive to IP or UI active
|
|
{
|
|
if (pActiveSite==NULL)
|
|
{
|
|
_nActivate = uOldState;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ASSERT(!_pipsite); // always true, so why check below?
|
|
if (!_pipsite)
|
|
{
|
|
HRESULT hres = pActiveSite->QueryInterface(IID_IOleInPlaceSite, (LPVOID*)&_pipsite);
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
_nActivate = uOldState;
|
|
return hres;
|
|
}
|
|
|
|
hres = _pipsite->CanInPlaceActivate();
|
|
if (hres != S_OK) {
|
|
ATOMICRELEASE(_pipsite);
|
|
TraceMsg(TF_SHDCONTROL, "she - CanInPlaceActivate returned %x", hres);
|
|
_nActivate = uOldState;
|
|
return E_FAIL;
|
|
}
|
|
|
|
_OnInPlaceActivate(); // do it
|
|
}
|
|
}
|
|
else if (uOldState == OC_UIACTIVE) // going from UIActive to IPActive or deactive
|
|
{
|
|
_OnUIDeactivate();
|
|
}
|
|
|
|
if (uState == OC_UIACTIVE) // going to UIActive
|
|
{
|
|
_OnUIActivate();
|
|
}
|
|
else if (uState == OC_DEACTIVE) // going to Deactive
|
|
{
|
|
// We fail creation (OLEIVERB_PRIMARY, OLEIVERB_SHOW,
|
|
// OLEIVERB_UIACTIVATE, or OLEIVERB_INPLACEACTIVATE) if we don't
|
|
// get a pipsite, so we should never hit this case.
|
|
ASSERT(_pipsite);
|
|
// OLEIVERB_HIDE should ... return it to the visual state just after
|
|
// initial creation or reloading, before OLEIVERB_SHOW or OLEIVERB_OPEN
|
|
// is sent. Which is what _InPlaceDeactivate does. What's the point of this?
|
|
// htmlobj calls OLEIVERB_HIDE and then ::InPlaceDeactivate
|
|
_OnInPlaceDeactivate();
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// move from de-active to in-place-active
|
|
void CShellEmbedding::_OnInPlaceActivate()
|
|
{
|
|
//
|
|
// Set the appropriate parent window.
|
|
//
|
|
_CreateWindowOrSetParent(_pipsite);
|
|
|
|
_pipsite->OnInPlaceActivate();
|
|
ASSERT(_pipframe == NULL);
|
|
ASSERT(_pipui == NULL);
|
|
_finfo.cb = sizeof(OLEINPLACEFRAMEINFO);
|
|
_pipsite->GetWindowContext(&_pipframe, &_pipui,
|
|
&_rcPos, &_rcClip, &_finfo);
|
|
|
|
TraceMsg(TF_SHDCONTROL, "she::_OnInPlaceActivate x=%d y=%d cx=%d cy=%d (_cx=%d _cy=%d)", _rcPos.left, _rcPos.top, _rcPos.right-_rcPos.left, _rcPos.bottom-_rcPos.top, _size.cx, _size.cy);
|
|
SetWindowPos(_hwnd, 0,
|
|
_rcPos.left, _rcPos.top,
|
|
_rcPos.right-_rcPos.left,
|
|
_rcPos.bottom-_rcPos.top,
|
|
SWP_SHOWWINDOW | SWP_NOZORDER);
|
|
|
|
_SendAdvise(OBJECTCODE_SHOWOBJECT); // just like OLE 2nd ed (p.1074)
|
|
}
|
|
|
|
// Move from in-place-active to de-active
|
|
void CShellEmbedding::_OnInPlaceDeactivate(void)
|
|
{
|
|
if (_hwnd) {
|
|
ShowWindow(_hwnd, SW_HIDE);
|
|
|
|
// re-parent our _hwnd... when we're not active we can't rely on
|
|
// what our parent window is doing. The container can even destroy it!
|
|
//
|
|
// FEATURE: the standard thing to do here is DESTROY our HWND and
|
|
// recreate it if/when we are reactivated. This may break our hosted
|
|
// IShellView and draw code. Investigate this.
|
|
//
|
|
|
|
// APPCOMPAT: this has been taken out by CDturner, MikeSH assures me we don't need it, and
|
|
// this is causing our app to lose activation and regain it which causes the
|
|
// palette to flash on 256 colour machines...y
|
|
// SetParentHwnd(_hwnd, NULL);
|
|
}
|
|
|
|
if (_pipsite) {
|
|
_pipsite->OnInPlaceDeactivate();
|
|
ATOMICRELEASE(_pipsite);
|
|
}
|
|
|
|
ATOMICRELEASE(_pipframe);
|
|
ATOMICRELEASE(_pipui);
|
|
|
|
//
|
|
// We need to tell the container to update the cached metafile, if any.
|
|
//
|
|
_SendAdvise(OBJECTCODE_DATACHANGED);
|
|
|
|
}
|
|
|
|
// move from in-place-active to ui-active
|
|
void CShellEmbedding::_OnUIActivate(void)
|
|
{
|
|
if (_pipsite) {
|
|
_pipsite->OnUIActivate();
|
|
}
|
|
|
|
//
|
|
// HACK: When we are in Excel, _pipui->SetActiveObject sets the focus
|
|
// to us (for some unknown reason -- trying to be nice?). Since _hwnd
|
|
// simply forward the focus to the _hwndChild, setting focus to _hwnd
|
|
// twice causes this:
|
|
//
|
|
// 1. SetFocus(_hwnd) by us (if we call SetFocus(_hwnd))
|
|
// 2. SetFocus(_hwndChild) in _hwnd's wndproc
|
|
// 3. SetFocus(_hwnd) by Excel
|
|
// 4. SetFocus(_hwndChild) in _hwnd's wndproc
|
|
//
|
|
// If _hwndChild is a control, it notifies to the parent that it
|
|
// lost the focus. Then, we think "oh, we lost the focus. We should
|
|
// deactivate this object". To avoid it, we don't call SetFocus before
|
|
// we call _pipui->SetActiveObject and do some tricky thing below.
|
|
//
|
|
// SetFocus(_hwnd);
|
|
|
|
//
|
|
// RDuke suggest us to change the second parameter to NULL (instead of
|
|
// "FOO" in IE3, but we don't know the side effect of it. I'm changing
|
|
// it to "item" for IE4. (SatoNa)
|
|
//
|
|
if (_pipframe) {
|
|
_pipframe->SetActiveObject(SAFECAST(this, IOleInPlaceActiveObject*), L"item");
|
|
}
|
|
|
|
if (_pipui) {
|
|
_pipui->SetActiveObject(SAFECAST(this, IOleInPlaceActiveObject*), L"item");
|
|
}
|
|
|
|
//
|
|
// We don't have any menu, so tell the container to use its own menu.
|
|
//
|
|
if (_pipframe) {
|
|
_pipframe->SetMenu(NULL, NULL, _hwnd);
|
|
}
|
|
|
|
// Find-out if one of our child window has the input focus.
|
|
for (HWND hwndFocus = GetFocus();
|
|
hwndFocus && hwndFocus!=_hwnd;
|
|
hwndFocus = GetParent(hwndFocus))
|
|
{}
|
|
|
|
// If not, set it.
|
|
if (hwndFocus==NULL) {
|
|
SetFocus(_hwnd);
|
|
}
|
|
|
|
// If this UIActivate came from below (i.e., our hosted DocObject), then we need to inform
|
|
// our container. We do this by calling IOleControlSite::OnFocus. VB5 and Visual FoxPro
|
|
// (at least) rely on this call being made for proper focus handling.
|
|
//
|
|
IUnknown_OnFocusOCS(_pcli, TRUE);
|
|
}
|
|
|
|
void CShellEmbedding::_OnUIDeactivate(void)
|
|
{
|
|
//
|
|
// We don't have any shared menu or tools to clean up.
|
|
//
|
|
|
|
if (_pipframe) {
|
|
_pipframe->SetActiveObject(NULL, NULL);
|
|
}
|
|
|
|
if (_pipui) {
|
|
_pipui->SetActiveObject(NULL, NULL);
|
|
}
|
|
|
|
if (_pipsite) {
|
|
_pipsite->OnUIDeactivate(FALSE);
|
|
}
|
|
// If this UIDeactivate came from below (i.e., our hosted DocObject), then we need to inform
|
|
// our container. We do this by calling IOleControlSite::OnFocus. VB5 and Visual FoxPro
|
|
// (at least) rely on this call being made for proper focus handling.
|
|
//
|
|
IUnknown_OnFocusOCS(_pcli, FALSE);
|
|
}
|
|
|
|
|
|
|
|
HRESULT CShellEmbedding::EnumVerbs(
|
|
IEnumOLEVERB **ppEnumOleVerb)
|
|
{
|
|
IOOMSG(TEXT("EnumVerbs"));
|
|
*ppEnumOleVerb = new CSVVerb(_pverbs);
|
|
return *ppEnumOleVerb ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::Update( void)
|
|
{
|
|
IOOMSG(TEXT("Update"));
|
|
// Always up-to-date
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::IsUpToDate( void)
|
|
{
|
|
IOOMSG(TEXT("IsUpToDate"));
|
|
// Always up-to-date
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetUserClassID(CLSID *pClsid)
|
|
{
|
|
IOOMSG(TEXT("GetUserClassID"));
|
|
return GetClassID(pClsid);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetUserType(DWORD dwFormOfType, LPOLESTR *pszUserType)
|
|
{
|
|
return OleRegGetUserType(CLSIDOFOBJECT(this), dwFormOfType, pszUserType);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetExtent(DWORD dwDrawAspect, SIZEL *psizel)
|
|
{
|
|
// SetExtent sets the LOGICAL size of an object. SetObjectRects determins
|
|
// the size of the object on the screen. If we cared about zooming, we'd
|
|
// keep track of this and do some sort of scaling. But we don't.
|
|
// We still need to remember this value so we return it on GetExtent.
|
|
//
|
|
_sizeHIM = *psizel;
|
|
|
|
// HOWEVER, IE3 shipped a SetExtent that changed the physical size of the
|
|
// object. For compat (AOL uses SetExtent to change the size), if we're the
|
|
// old WebBrowser, continue to resize.
|
|
//
|
|
if (_pObjectInfo->pclsid == &CLSID_WebBrowser_V1)
|
|
{
|
|
RECT rc;
|
|
HDC hdc;
|
|
int mmOld;
|
|
POINT pt;
|
|
|
|
// Make sure a container doesn't do anything strange like
|
|
// make us negative size
|
|
//
|
|
// APPCOMPAT: this breaks Trident because it sizes us negative
|
|
// and we fail that sizing and they get confused...
|
|
//
|
|
//ASSERT(psizel->cx >= 0 && psizel->cy <= 0);
|
|
//if (psizel->cx < 0 || psizel->cy > 0)
|
|
// return E_FAIL;
|
|
|
|
// We only support DVASPECT_CONTENT
|
|
if (dwDrawAspect != DVASPECT_CONTENT)
|
|
return E_NOTIMPL;
|
|
|
|
// Map this to a SetObjectRects call -- that way superclasses
|
|
// only have to watch one function for size changes
|
|
//
|
|
|
|
int nScaleFactorX = 1, nScaleFactorY = 1;
|
|
|
|
pt.x = psizel->cx;
|
|
pt.y = psizel->cy;
|
|
|
|
hdc = GetDC(NULL);
|
|
|
|
if (hdc)
|
|
{
|
|
mmOld = SetMapMode(hdc, MM_HIMETRIC);
|
|
|
|
if (!g_fRunningOnNT) // if running on Win95
|
|
{
|
|
// Win95 doesn't like coordinates over 32K
|
|
|
|
// SHRT_MIN and SHRT_MAX defined in $NT/public/sdk/inc/crt/limits.h
|
|
|
|
while (pt.x > SHRT_MAX || pt.x < SHRT_MIN)
|
|
{
|
|
pt.x >>= 1;
|
|
nScaleFactorX <<= 1;
|
|
}
|
|
while (pt.y > SHRT_MAX || pt.y < SHRT_MIN)
|
|
{
|
|
pt.y >>= 1;
|
|
nScaleFactorY <<= 1;
|
|
}
|
|
}
|
|
|
|
LPtoDP(hdc, &pt, 1);
|
|
|
|
if (!g_fRunningOnNT)
|
|
{
|
|
pt.x *= nScaleFactorX;
|
|
pt.y *= nScaleFactorY;
|
|
}
|
|
|
|
pt.y = -pt.y;
|
|
SetMapMode(hdc, mmOld);
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
|
|
rc.left = _rcPos.left;
|
|
rc.right = rc.left + pt.x;
|
|
rc.top = _rcPos.top;
|
|
rc.bottom = rc.top + pt.y;
|
|
|
|
// Assume that using SetExtent adjusts both the pos and clip rects
|
|
return SetObjectRects(&rc, NULL);
|
|
}
|
|
else
|
|
{
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetExtent(DWORD dwDrawAspect, SIZEL *psizel)
|
|
{
|
|
*psizel = _sizeHIM;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection)
|
|
{
|
|
IOOMSG2(TEXT("Advise"), pAdvSink);
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (!pdwConnection)
|
|
return hr;
|
|
|
|
*pdwConnection = NULL; // set out params to NULL
|
|
|
|
if (!_poah)
|
|
hr = ::CreateOleAdviseHolder(&_poah);
|
|
else
|
|
hr = NOERROR;
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = _poah->Advise(pAdvSink, pdwConnection);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::Unadvise(DWORD dwConnection)
|
|
{
|
|
IOOMSG(TEXT("Unadvise"));
|
|
HRESULT hr;
|
|
|
|
if (!_poah)
|
|
return(OLE_E_NOCONNECTION);
|
|
|
|
hr = _poah->Unadvise(dwConnection);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::EnumAdvise(IEnumSTATDATA **ppenumAdvise)
|
|
{
|
|
IOOMSG(TEXT("EnumAdvise"));
|
|
HRESULT hr;
|
|
|
|
if (!ppenumAdvise)
|
|
return(E_INVALIDARG);
|
|
|
|
if (!_poah)
|
|
{
|
|
*ppenumAdvise = NULL;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = _poah->EnumAdvise(ppenumAdvise);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus)
|
|
{
|
|
IOOMSG(TEXT("GetMiscStatus"));
|
|
|
|
*pdwStatus = OLEMISCFLAGSOFCONTROL(this);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetColorScheme(LOGPALETTE *pLogpal)
|
|
{
|
|
IOOMSG(TEXT("GetColorScheme"));
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Helper function to create an HDC from an OLE DVTARGETDEVICE structure.
|
|
// Very useful for metafile drawing, where the Metafile DC will be the
|
|
// actual "draw to" dc, and the TargetDC, if present, will describe the ultimate output device.
|
|
//
|
|
HDC CShellEmbedding::_OleStdCreateDC(DVTARGETDEVICE *ptd)
|
|
{
|
|
HDC hdc = NULL;
|
|
LPDEVNAMES lpDevNames = NULL;
|
|
LPDEVMODEA lpDevMode = NULL;
|
|
LPSTR lpszDriverName = NULL;
|
|
LPSTR lpszDeviceName = NULL;
|
|
LPSTR lpszPortName = NULL;
|
|
|
|
if (ptd)
|
|
{
|
|
lpDevNames = (LPDEVNAMES) ptd;
|
|
if (ptd->tdExtDevmodeOffset)
|
|
{
|
|
lpDevMode = (LPDEVMODEA) ( (LPSTR) ptd + ptd->tdExtDevmodeOffset);
|
|
}
|
|
|
|
lpszDriverName = (LPSTR) lpDevNames + ptd->tdDriverNameOffset;
|
|
lpszDeviceName = (LPSTR) lpDevNames + ptd->tdDeviceNameOffset;
|
|
lpszPortName = (LPSTR) lpDevNames + ptd->tdPortNameOffset;
|
|
|
|
hdc = CreateDCA(lpszDriverName, lpszDeviceName, lpszPortName, lpDevMode);
|
|
}
|
|
return hdc;
|
|
}
|
|
|
|
// *** IDataObject ***
|
|
//
|
|
// WARNING:
|
|
// It is well-known fact that Word and Excel (in Office95) does not call
|
|
// IViewObject::Draw to draw embedding. Instead, they GetData(CF_METAFILEPICT).
|
|
// If we don't offer it, Word will fail to embed it and Excel will draw
|
|
// white rectangle when our object is deactivated. To be embedded correctly
|
|
// on those apps, we must support CF_METAFILEPICT. (SatoNa)
|
|
//
|
|
HRESULT CShellEmbedding::GetData(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
|
|
{
|
|
IDTMSG4(TEXT("GetData"), pformatetcIn->cfFormat, pformatetcIn->tymed);
|
|
HRESULT hres = DV_E_FORMATETC;
|
|
HDC hdcTargetDevice = NULL;
|
|
HENHMETAFILE hemf = NULL;
|
|
|
|
// If a Target device is specified in the FORMATETC structure, create a DC for it.
|
|
// This gets passed to CreateEnhMetaFile and IViewObject::Draw.
|
|
//
|
|
if (pformatetcIn->ptd)
|
|
{
|
|
hdcTargetDevice = _OleStdCreateDC(pformatetcIn->ptd);
|
|
if (!hdcTargetDevice)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
// Enhanced metafiles need special processing.
|
|
//
|
|
if (pformatetcIn->cfFormat == CF_ENHMETAFILE
|
|
&& (pformatetcIn->tymed & TYMED_ENHMF))
|
|
{
|
|
if (_hwnd)
|
|
{
|
|
RECTL rectBounds = { 0, 0, _sizeHIM.cx, _sizeHIM.cy };
|
|
|
|
//
|
|
// Call the "A" version since we're not passing in strings and
|
|
// this needs to work on W95.
|
|
HDC hdc = CreateEnhMetaFileA(hdcTargetDevice, NULL, (RECT*)&rectBounds, NULL);
|
|
IDTMSG3(TEXT("_EnhMetafileFromWindow CreateEnhMetaFile returned"), hdc);
|
|
if (hdc)
|
|
{
|
|
SetMapMode(hdc, MM_HIMETRIC);
|
|
rectBounds.bottom = -rectBounds.bottom;
|
|
|
|
Draw(DVASPECT_CONTENT, LINDEX_INTERNAL, NULL, pformatetcIn->ptd,
|
|
hdcTargetDevice, hdc, &rectBounds, NULL, NULL, 0);
|
|
|
|
hemf = CloseEnhMetaFile(hdc);
|
|
IDTMSG3(TEXT("_EnhMetafileFromWindow CloseEnhMetaFile returned"), hemf);
|
|
}
|
|
}
|
|
|
|
pmedium->hEnhMetaFile = hemf;
|
|
if (pmedium->hEnhMetaFile)
|
|
{
|
|
pmedium->tymed = TYMED_ENHMF;
|
|
pmedium->pUnkForRelease = NULL;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hres = E_FAIL;
|
|
}
|
|
}
|
|
|
|
// Create a standard metafile
|
|
//
|
|
else if (pformatetcIn->cfFormat == CF_METAFILEPICT
|
|
&& (pformatetcIn->tymed & TYMED_MFPICT))
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
HGLOBAL hmem = GlobalAlloc(GPTR, sizeof(METAFILEPICT));
|
|
if (hmem)
|
|
{
|
|
LPMETAFILEPICT pmf = (LPMETAFILEPICT) hmem;
|
|
RECTL rectBounds = { 0, 0, _sizeHIM.cx, _sizeHIM.cy };
|
|
|
|
HDC hdc = CreateMetaFile(NULL);
|
|
if (hdc)
|
|
{
|
|
SetMapMode(hdc, MM_HIMETRIC);
|
|
rectBounds.bottom = -rectBounds.bottom;
|
|
|
|
SetWindowOrgEx(hdc, 0, 0, NULL);
|
|
SetWindowExtEx(hdc, _sizeHIM.cx, _sizeHIM.cy, NULL);
|
|
|
|
Draw(DVASPECT_CONTENT, LINDEX_INTERNAL, NULL,
|
|
pformatetcIn->ptd, hdcTargetDevice,
|
|
hdc, &rectBounds, &rectBounds, NULL, 0);
|
|
|
|
pmf->hMF = CloseMetaFile(hdc);
|
|
|
|
if (pmf->hMF)
|
|
{
|
|
pmf->mm = MM_ANISOTROPIC;
|
|
pmf->xExt = _sizeHIM.cx;
|
|
pmf->yExt = _sizeHIM.cy;
|
|
TraceMsg(TF_SHDCONTROL, "sdv TR ::GetData (%d,%d)-(%d,%d)",
|
|
_size.cx, _size.cy, _sizeHIM.cx, _sizeHIM.cy);
|
|
|
|
pmedium->tymed = TYMED_MFPICT;
|
|
pmedium->hMetaFilePict = hmem;
|
|
pmedium->pUnkForRelease = NULL;
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
GlobalFree(hmem);
|
|
hmem = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hdcTargetDevice)
|
|
DeleteDC(hdcTargetDevice);
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
|
|
{
|
|
IDTMSG2(TEXT("GetDataHere"), pformatetc->cfFormat);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::QueryGetData(FORMATETC *pformatetc)
|
|
{
|
|
IDTMSG2(TEXT("QueryGetData"), pformatetc->cfFormat);
|
|
HRESULT hres = S_FALSE;
|
|
if (pformatetc->cfFormat == CF_ENHMETAFILE
|
|
&& (pformatetc->tymed & TYMED_ENHMF))
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
else if (pformatetc->cfFormat == CF_METAFILEPICT
|
|
&& (pformatetc->tymed & TYMED_MFPICT))
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::GetCanonicalFormatEtc(FORMATETC *pformatetcIn, FORMATETC *pformatetcOut)
|
|
{
|
|
IDTMSG2(TEXT("GetCanonicalFormatEtc"), pformatetcIn->cfFormat);
|
|
*pformatetcOut = *pformatetcIn;
|
|
return DATA_S_SAMEFORMATETC;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease)
|
|
{
|
|
IDTMSG(TEXT("SetData"));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc)
|
|
{
|
|
IDTMSG(TEXT("EnumFormatEtc"));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
|
|
{
|
|
IDTMSG(TEXT("DAdvise"));
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (!pdwConnection)
|
|
return hr;
|
|
|
|
*pdwConnection = NULL; // set out params to NULL
|
|
|
|
if (!_pdah)
|
|
hr = ::CreateDataAdviseHolder(&_pdah);
|
|
else
|
|
hr = NOERROR;
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = _pdah->Advise(this, pformatetc, advf, pAdvSink, pdwConnection);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::DUnadvise(DWORD dwConnection)
|
|
{
|
|
IDTMSG(TEXT("DUnadvise"));
|
|
HRESULT hr;
|
|
|
|
if (!_pdah)
|
|
return(OLE_E_NOCONNECTION);
|
|
|
|
hr = _pdah->Unadvise(dwConnection);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::EnumDAdvise(IEnumSTATDATA **ppenumAdvise)
|
|
{
|
|
IDTMSG(TEXT("EnumDAdvise"));
|
|
HRESULT hr;
|
|
|
|
if (!ppenumAdvise)
|
|
return(E_INVALIDARG);
|
|
|
|
if (!_pdah)
|
|
{
|
|
*ppenumAdvise = NULL;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = _pdah->EnumAdvise(ppenumAdvise);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// *** IOleWindow ***
|
|
HRESULT CShellEmbedding::GetWindow(HWND * lphwnd)
|
|
{
|
|
*lphwnd = _hwnd;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::ContextSensitiveHelp(BOOL fEnterMode)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// *** IOleInPlaceObject ***
|
|
HRESULT CShellEmbedding::InPlaceDeactivate(void)
|
|
{
|
|
IIPMSG(TEXT("InPlaceDeactivate"));
|
|
return _DoActivateChange(NULL, OC_DEACTIVE, FALSE);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::UIDeactivate(void)
|
|
{
|
|
IIPMSG(TEXT("UIDeactivate"));
|
|
return _DoActivateChange(NULL, OC_INPLACEACTIVE, FALSE);
|
|
}
|
|
|
|
HRESULT CShellEmbedding::SetObjectRects(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
|
|
{
|
|
RECT rcVisible;
|
|
|
|
_rcPos = *lprcPosRect;
|
|
|
|
if (lprcClipRect)
|
|
{
|
|
_rcClip = *lprcClipRect;
|
|
}
|
|
else
|
|
{
|
|
_rcClip = _rcPos;
|
|
}
|
|
|
|
IntersectRect(&rcVisible, &_rcPos, &_rcClip);
|
|
if (EqualRect(&rcVisible, &_rcPos))
|
|
{
|
|
if (_fUsingWindowRgn)
|
|
{
|
|
SetWindowRgn(_hwnd, NULL, TRUE);
|
|
_fUsingWindowRgn = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_fUsingWindowRgn = TRUE;
|
|
OffsetRect(&rcVisible, -_rcPos.left, -_rcPos.top);
|
|
SetWindowRgn(_hwnd,
|
|
CreateRectRgnIndirect(&rcVisible),
|
|
TRUE);
|
|
}
|
|
|
|
// We should consider this equivalent to a SetExtent as well...
|
|
// But only for valid sizes (html viewer gives us invalid
|
|
// sizes during it's reformat routine). Note: we still need
|
|
// the SetWindowPos because we may move off the window.
|
|
int cx = _rcPos.right - _rcPos.left;
|
|
int cy = _rcPos.bottom - _rcPos.top;
|
|
TraceMsg(TF_SHDCONTROL, "she SetObjectRects to x=%d y=%d cx=%d cy=%d (from cx=%d cy=%d)", _rcPos.left, _rcPos.top, cx, cy, _size.cx, _size.cy);
|
|
if (cx >= 0 && cy >= 0)
|
|
{
|
|
_size.cx = cx;
|
|
_size.cy = cy;
|
|
}
|
|
|
|
if (_hwnd)
|
|
{
|
|
SetWindowPos(_hwnd, NULL,
|
|
_rcPos.left, _rcPos.top,
|
|
_size.cx,
|
|
_size.cy,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::ReactivateAndUndo(void)
|
|
{
|
|
IIPMSG(TEXT("ReactivateAndUndo"));
|
|
return INPLACE_E_NOTUNDOABLE;
|
|
}
|
|
|
|
// *** IOleInPlaceActiveObject ***
|
|
HRESULT CShellEmbedding::TranslateAccelerator(LPMSG lpmsg)
|
|
{
|
|
extern BOOL IsVK_TABCycler(MSG * pMsg);
|
|
HRESULT hr = S_FALSE;
|
|
|
|
// IIAMSG(TEXT("TranslateAccelerator"));
|
|
// We have no accelerators (other than TAB, which we must pass up
|
|
// to IOCS::TA to move on to the next control if any)
|
|
|
|
if (IsVK_TABCycler(lpmsg)) {
|
|
// NOTE: grfMods?
|
|
hr = IUnknown_TranslateAcceleratorOCS(_pcli, lpmsg, /*grfMods*/ 0);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::OnFrameWindowActivate(BOOL fActivate)
|
|
{
|
|
IIAMSG(TEXT("OnFrameWindowActivate"));
|
|
|
|
if (fActivate)
|
|
{
|
|
// our frame has been activated and we are the active object
|
|
// make sure we have focus
|
|
SetFocus(_hwnd);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::OnDocWindowActivate(BOOL fActivate)
|
|
{
|
|
IIAMSG(TEXT("OnDocWindowActivate"));
|
|
// We don't care
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::ResizeBorder(LPCRECT prcBorder,
|
|
IOleInPlaceUIWindow *pUIWindow, BOOL fFrameWindow)
|
|
{
|
|
IIAMSG(TEXT("ResizeBorder"));
|
|
// We have no toolbars.
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CShellEmbedding::EnableModeless(BOOL fEnable)
|
|
{
|
|
IIAMSG(TEXT("EnableModeless"));
|
|
// We have no dialogs.
|
|
return S_OK;
|
|
}
|
|
|
|
void CShellEmbedding::_RegisterWindowClass(void)
|
|
{
|
|
WNDCLASS wc = {0};
|
|
wc.style = CS_DBLCLKS;
|
|
wc.lpfnWndProc = s_WndProc ;
|
|
//wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = SIZEOF(CShellEmbedding*) * 2;
|
|
wc.hInstance = g_hinst ;
|
|
//wc.hIcon = NULL ;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW) ;
|
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
//wc.lpszMenuName = NULL ;
|
|
wc.lpszClassName = c_szShellEmbedding;
|
|
|
|
SHRegisterClass(&wc);
|
|
}
|
|
|
|
|
|
LRESULT CShellEmbedding::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
DWORD dwExStyles;
|
|
if ((dwExStyles = GetWindowLong(hwnd, GWL_EXSTYLE)) & RTL_MIRRORED_WINDOW)
|
|
{
|
|
SetWindowLong(hwnd, GWL_EXSTYLE, dwExStyles &~ RTL_MIRRORED_WINDOW);
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_SETFOCUS:
|
|
if (_hwndChild)
|
|
SetFocus(_hwndChild);
|
|
// If this SETFOCUS came from TABbing onto the control, VB5 expects us to call its
|
|
// IOleControlSite::OnFocus. Then it will UIActivate us.
|
|
//
|
|
IUnknown_OnFocusOCS(_pcli, TRUE);
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
// If this KILLFOCUS came from TABbing off the control, VB5 expects us to call its
|
|
// IOleControlSite::OnFocus. Then it will UIDeactivate us.
|
|
//
|
|
IUnknown_OnFocusOCS(_pcli, FALSE);
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGED:
|
|
if (_hwndChild)
|
|
{
|
|
LPWINDOWPOS lpwp = (LPWINDOWPOS)lParam;
|
|
|
|
if (!(lpwp->flags & SWP_NOSIZE))
|
|
{
|
|
SetWindowPos(_hwndChild, NULL,
|
|
0, 0, lpwp->cx, lpwp->cy,
|
|
SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|
|
|
(lpwp->flags&(SWP_NOREDRAW|SWP_NOCOPYBITS)));
|
|
}
|
|
}
|
|
goto DoDefault;
|
|
|
|
#ifdef DEBUG
|
|
// FEATURE: we'll never get this with ShellExplorer OC, but if we did,
|
|
// we'd need to call _DoActivateChange(OC_UIACTIVE, FALSE);
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
TraceMsg(TF_SHDCONTROL, "she ::v_WndProc(WM_xBUTTONDOWN) - we need to UIActivate");
|
|
goto DoDefault;
|
|
#endif
|
|
|
|
default:
|
|
DoDefault:
|
|
return DefWindowProc(_hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
void CShellEmbedding::_ViewChange(DWORD dwAspect, LONG lindex)
|
|
{
|
|
dwAspect &= _asp;
|
|
|
|
if (dwAspect && _padv)
|
|
{
|
|
IAdviseSink *padv = _padv;
|
|
IUnknown *punkRelease;
|
|
|
|
if (_advf & ADVF_ONLYONCE)
|
|
{
|
|
_padv = NULL;
|
|
punkRelease = padv;
|
|
}
|
|
else
|
|
punkRelease = NULL;
|
|
|
|
padv->OnViewChange(dwAspect, lindex);
|
|
|
|
if (punkRelease)
|
|
punkRelease->Release();
|
|
}
|
|
}
|
|
|
|
void CShellEmbedding::_SendAdvise(UINT uCode)
|
|
{
|
|
DWORD dwAspect=DVASPECT_CONTENT | DVASPECT_THUMBNAIL;
|
|
|
|
switch (uCode)
|
|
{
|
|
case OBJECTCODE_SAVED:
|
|
if (NULL!=_poah)
|
|
_poah->SendOnSave();
|
|
break;
|
|
|
|
case OBJECTCODE_CLOSED:
|
|
if (NULL!=_poah)
|
|
_poah->SendOnClose();
|
|
break;
|
|
|
|
case OBJECTCODE_RENAMED:
|
|
//Call IOleAdviseHolder::SendOnRename (later)
|
|
break;
|
|
|
|
case OBJECTCODE_SAVEOBJECT:
|
|
if (_fDirty && NULL!=_pcli)
|
|
_pcli->SaveObject();
|
|
|
|
_fDirty=FALSE;
|
|
break;
|
|
|
|
case OBJECTCODE_DATACHANGED:
|
|
// _fDirty=TRUE;
|
|
|
|
//No flags are necessary here.
|
|
if (NULL!=_pdah)
|
|
_pdah->SendOnDataChange(this, 0, 0);
|
|
//
|
|
// fall through
|
|
//
|
|
case OBJECTCODE_VIEWCHANGED:
|
|
_ViewChange(dwAspect, -1);
|
|
break;
|
|
|
|
case OBJECTCODE_SHOWOBJECT:
|
|
if (NULL!=_pcli)
|
|
_pcli->ShowObject();
|
|
break;
|
|
}
|
|
}
|
|
|
|
HRESULT CSVVerb::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
if (IsEqualIID(riid, IID_IEnumOLEVERB) || IsEqualIID(riid, IID_IUnknown))
|
|
{
|
|
*ppvObj = SAFECAST(this, IEnumOLEVERB*);
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
_cRef++;
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG CSVVerb::AddRef()
|
|
{
|
|
return ++_cRef;
|
|
}
|
|
|
|
ULONG CSVVerb::Release()
|
|
{
|
|
if (--_cRef > 0) {
|
|
return _cRef;
|
|
}
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CSVVerb::Next(
|
|
/* [in] */ ULONG celt,
|
|
/* [out] */ LPOLEVERB rgelt,
|
|
/* [out] */ ULONG *pceltFetched)
|
|
{
|
|
HRESULT hres = S_FALSE;
|
|
ULONG celtFetched = 0;
|
|
|
|
|
|
// We need to enumerate the predefined verbs we support,
|
|
// or some containers will never call them. This list
|
|
// of verbs comes from our ::DoVerb function
|
|
//
|
|
static const OLEVERB rgVerbs[5] =
|
|
{
|
|
{OLEIVERB_PRIMARY, NULL, 0, 0},
|
|
{OLEIVERB_INPLACEACTIVATE, NULL, 0, 0},
|
|
{OLEIVERB_UIACTIVATE, NULL, 0, 0},
|
|
{OLEIVERB_SHOW, NULL, 0, 0},
|
|
{OLEIVERB_HIDE, NULL, 0, 0}
|
|
};
|
|
if (_iCur < ARRAYSIZE(rgVerbs))
|
|
{
|
|
IEVMSG(TEXT("Next"), celt, _iCur, TEXT("OLEIVERB_..."));
|
|
|
|
*rgelt = rgVerbs[_iCur++];
|
|
hres = S_OK;
|
|
}
|
|
else if (_pverbs)
|
|
{
|
|
int iCur = _iCur - ARRAYSIZE(rgVerbs);
|
|
|
|
IEVMSG(TEXT("Next"), celt, _iCur, _pverbs[iCur].lpszVerbName);
|
|
|
|
//
|
|
// FEATURE: Should we do while(celt--)?
|
|
//
|
|
if (_pverbs[iCur].lpszVerbName)
|
|
{
|
|
*rgelt = _pverbs[_iCur++];
|
|
WCHAR* pwszVerb = (WCHAR *)CoTaskMemAlloc(128 * sizeof(WCHAR));
|
|
if (pwszVerb)
|
|
{
|
|
MLLoadStringW(PtrToUint(_pverbs[iCur].lpszVerbName), pwszVerb, 128);
|
|
rgelt->lpszVerbName = pwszVerb;
|
|
celtFetched++;
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pceltFetched) {
|
|
*pceltFetched = celtFetched;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CSVVerb::Skip(ULONG celt)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSVVerb::Reset( void)
|
|
{
|
|
_iCur = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSVVerb::Clone(IEnumOLEVERB **ppenum)
|
|
{
|
|
*ppenum = new CSVVerb(_pverbs);
|
|
return *ppenum ? S_OK : E_OUTOFMEMORY;
|
|
}
|