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.
2981 lines
73 KiB
2981 lines
73 KiB
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module COLEOBJ.CPP OLE Object management class implemenation |
|
|
*
|
|
* Author: alexgo 10/24/95
|
|
*
|
|
* Note: Much of this code is a port from RichEdit 1.0 sources
|
|
* (cleaned up a bit, ported to C++, etc.) So if there's any
|
|
* bit of strangeness, it's probably there for a reason.
|
|
*
|
|
* Copyright (c) 1995-1998, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "_common.h"
|
|
#include "_edit.h"
|
|
#include "_coleobj.h"
|
|
#include "_objmgr.h"
|
|
#include "_select.h"
|
|
#include "_rtext.h"
|
|
#include "_disp.h"
|
|
#include "_dispprt.h"
|
|
#include "_antievt.h"
|
|
#include "_dxfrobj.h"
|
|
|
|
ASSERTDATA
|
|
|
|
//
|
|
// data private to this file
|
|
//
|
|
static const OLECHAR szSiteFlagsStm[] = OLESTR("RichEditFlags");
|
|
|
|
//
|
|
// EXCEL clsid's. We have to make some special purpose hacks
|
|
// for XL.
|
|
const CLSID rgclsidExcel[] =
|
|
{
|
|
{ 0x00020810L, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46} }, // Excel Worksheet
|
|
{ 0x00020811L, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46} }, // Excel Chart
|
|
{ 0x00020812L, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46} }, // Excel App1
|
|
{ 0x00020841L, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46} }, // Excel App2
|
|
};
|
|
const INT cclsidExcel = sizeof(rgclsidExcel) / sizeof(rgclsidExcel[0]);
|
|
|
|
|
|
//
|
|
// WordArt CLSID for more special purpose hacks.
|
|
//
|
|
const GUID CLSID_WordArt =
|
|
{ 0x000212F0L, 0, 0, {0xC0, 0, 0, 0, 0, 0, 0, 0x46} };
|
|
const GUID CLSID_PaintbrushPicture =
|
|
{ 0x0003000AL, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
|
|
const GUID CLSID_BitmapImage =
|
|
{ 0xD3E34B21L, 0x9D75, 0x101A, { 0x8C, 0x3D, 0x00, 0xAA, 0x00, 0x1A, 0x16, 0x52 } };
|
|
|
|
|
|
#define dxyHandle (6) // Object frame handle size
|
|
#define dxyFrameDefault (1) // Object frame width
|
|
|
|
//
|
|
// utility functions
|
|
//
|
|
|
|
/*
|
|
* IsExcelCLSID (clsid)
|
|
*
|
|
* @func checks to see if the given clsid is one of XL's
|
|
*
|
|
* @rdesc TRUE/FALSE
|
|
*/
|
|
BOOL IsExcelCLSID(
|
|
REFGUID clsid)
|
|
{
|
|
for(LONG i = 0; i < cclsidExcel; i++)
|
|
{
|
|
if(IsEqualCLSID(clsid, rgclsidExcel[i]))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// PUBLIC methods
|
|
//
|
|
|
|
/*
|
|
* COleObject::QueryInterface(ridd, ppv)
|
|
*
|
|
* @mfunc the standard OLE QueryInterface
|
|
*
|
|
* @rdesc NOERROR <nl>
|
|
* E_NOINTERFACE
|
|
*/
|
|
STDMETHODIMP COleObject::QueryInterface(
|
|
REFIID riid, //@parm Requested interface ID
|
|
void ** ppv) //@parm Out parm for result
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::QueryInterface");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if(!ppv)
|
|
return E_INVALIDARG;
|
|
else
|
|
*ppv = NULL;
|
|
|
|
if(IsEqualIID(riid, IID_IUnknown))
|
|
*ppv = (IUnknown *)(IOleClientSite *)this;
|
|
|
|
else if(IsEqualIID(riid, IID_IOleClientSite))
|
|
*ppv = (IOleClientSite *)this;
|
|
|
|
else if(IsEqualIID(riid, IID_IOleInPlaceSite))
|
|
*ppv = (IOleInPlaceSite *)this;
|
|
|
|
else if(IsEqualIID(riid, IID_IAdviseSink))
|
|
*ppv = (IAdviseSink *)this;
|
|
|
|
else if(IsEqualIID(riid, IID_IOleWindow))
|
|
*ppv = (IOleWindow *)this;
|
|
|
|
else if(IsEqualIID(riid, IID_IRichEditOleCallback))
|
|
{
|
|
// NB!! Returning this pointer in our QI is
|
|
// phenomenally bogus; it breaks fundamental COM
|
|
// identity rules (granted, not many understand them!).
|
|
// Anyway, RichEdit 1.0 did this, so we better.
|
|
|
|
TRACEWARNSZ("Returning IRichEditOleCallback interface, COM "
|
|
"identity rules broken!");
|
|
|
|
*ppv = _ped->GetRECallback();
|
|
}
|
|
else
|
|
hr = E_NOINTERFACE;
|
|
|
|
if(*ppv)
|
|
(*(IUnknown **)ppv)->AddRef();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* COleObject::AddRef()
|
|
*
|
|
* @mfunc Increments reference count
|
|
*
|
|
* @rdesc New reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) COleObject::AddRef()
|
|
{
|
|
ULONG cRef;
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::AddRef");
|
|
|
|
cRef = SafeAddRef();
|
|
|
|
return cRef;
|
|
}
|
|
|
|
/*
|
|
* COleObject::Release ()
|
|
*
|
|
* @mfunc Decrements reference count
|
|
*
|
|
* @rdesc New reference count
|
|
*/
|
|
STDMETHODIMP_(ULONG) COleObject::Release()
|
|
{
|
|
ULONG cRef;
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::Release");
|
|
|
|
cRef = SafeRelease();
|
|
|
|
return cRef;
|
|
}
|
|
|
|
/*
|
|
* COleObject::SaveObject ()
|
|
*
|
|
* @mfunc implemtenation of IOleClientSite::SaveObject
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::SaveObject()
|
|
{
|
|
CCallMgr callmgr(_ped);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::SaveObject");
|
|
|
|
return SafeSaveObject();
|
|
}
|
|
|
|
/*
|
|
* COleObject::SafeSaveObject ()
|
|
*
|
|
* @mfunc implemtenation of IOleClientSite::SaveObject for internal consumption
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::SafeSaveObject()
|
|
{
|
|
IPersistStorage *pps;
|
|
HRESULT hr;
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::SafeSaveObject");
|
|
|
|
if(!_punkobj || !_pstg)
|
|
{
|
|
TRACEWARNSZ("SaveObject called on invalid object");
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
hr = _punkobj->QueryInterface(IID_IPersistStorage, (void **)&pps);
|
|
|
|
TESTANDTRACEHR(hr);
|
|
|
|
if(hr == NOERROR)
|
|
{
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
SavePrivateState();
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
hr = OleSave(pps, _pstg, TRUE);
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
TESTANDTRACEHR(hr);
|
|
|
|
// note that SaveCompleted is called even if OleSave fails.
|
|
// If both OleSave and SaveCompleted succeed, then go ahead
|
|
// and commit the changes
|
|
|
|
if(pps->SaveCompleted(NULL) == NOERROR && hr == NOERROR)
|
|
{
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
hr = _pstg->Commit(STGC_DEFAULT);
|
|
|
|
TESTANDTRACEHR(hr);
|
|
}
|
|
pps->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* COleObject::GetMoniker (dwAssign, dwWhichMoniker, ppmk)
|
|
*
|
|
* @mfunc implementation of IOleClientSite::GetMoniker
|
|
*
|
|
* @rdesc E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP COleObject::GetMoniker(
|
|
DWORD dwAssign, //@parm Force an assignment?
|
|
DWORD dwWhichMoniker, //@parm Kind of moniker to get
|
|
IMoniker ** ppmk) //@parm Out parm for result
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::GetMoniker");
|
|
|
|
TRACEWARNSZ("method not implemented!");
|
|
|
|
if(ppmk)
|
|
*ppmk = NULL;
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* COleObject::GetContainer(ppcont)
|
|
*
|
|
* @mfunc implementation of IOleClientSite::GetContainer
|
|
*
|
|
* @rdesc E_NOINTERFACE
|
|
*/
|
|
STDMETHODIMP COleObject::GetContainer(
|
|
IOleContainer **ppcont) //@parm Out parm for result
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::GetContainer");
|
|
|
|
TRACEWARNSZ("method not implemented!");
|
|
|
|
if(ppcont)
|
|
*ppcont = NULL;
|
|
|
|
// richedit 1.0 returns E_NOINTERFACE instead of E_NOTIMPL. Do
|
|
// the same.
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/*
|
|
* COleObject::ShowObject()
|
|
*
|
|
* @mfunc Implementation of IOleClientSite::ShowObject.
|
|
*
|
|
* @rdesc E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP COleObject::ShowObject()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::ShowObject");
|
|
|
|
TRACEWARNSZ("method not implemented!");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnShowWindow (fShow)
|
|
*
|
|
* @mfunc
|
|
* implementation of IOleClientSite::OnShowWindow -- notifies
|
|
* the client site that the object is or is not being shown in its
|
|
* own application window. This governs whether or not hatching
|
|
* should appear around the object in richedit.
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::OnShowWindow(
|
|
BOOL fShow) //@parm If TRUE, object is being drawn in its own window
|
|
{
|
|
DWORD dwFlags = _pi.dwFlags;
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnShowWindow");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
_pi.dwFlags &= ~REO_OPEN;
|
|
if(fShow)
|
|
_pi.dwFlags |= REO_OPEN;
|
|
|
|
// If something changed, redraw object
|
|
if(dwFlags != _pi.dwFlags)
|
|
{
|
|
// Invalidate rect that we're in.
|
|
_ped->TxInvalidateRect(&_rcPos, FALSE);
|
|
|
|
// We're not allowed to call invalidate rect by itself without
|
|
// terminating it with a call to update window.However, we don't
|
|
// care at this point if things are redrawn right away.
|
|
_ped->TxUpdateWindow();
|
|
|
|
// COMPATIBILITY ISSUE: (alexgo) the RE1.0 code did some funny
|
|
// stuff with undo here. I don't believe it's necessary to
|
|
// repeat that code with our multi-level undo model,
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::RequestNewObjectLayout ()
|
|
*
|
|
* @mfunc Implementation of IOleClientSite::RequestNewObjectLayout
|
|
*
|
|
* @rdesc E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP COleObject::RequestNewObjectLayout()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN,
|
|
"COleObject::RequestNewObjectLayout");
|
|
|
|
TRACEWARNSZ("method not implemented!");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* COleObject::GetWindow(phwnd)
|
|
*
|
|
* @mfunc Implementation of IOleInPlaceSite::GetWindow
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::GetWindow(
|
|
HWND *phwnd) //@parm Where to put window
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::GetWindow");
|
|
|
|
// NB! this method is not stabilized.
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if(phwnd)
|
|
return _ped->TxGetWindow(phwnd);
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
/*
|
|
* COleObject::ContextSensitiveHelp(fEnterMode)
|
|
*
|
|
* @mfunc Implemenation of IOleInPlaceSite::ContextSensitiveHelp
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::ContextSensitiveHelp(
|
|
BOOL fEnterMode) //@parm, If TRUE, then we're in help mode
|
|
{
|
|
IRichEditOleCallback *precall;
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN,
|
|
"COleObject::ContextSensitiveHelp");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// If the mode changes
|
|
if(_ped->GetObjectMgr()->GetHelpMode() != fEnterMode)
|
|
{
|
|
_ped->GetObjectMgr()->SetHelpMode(fEnterMode);
|
|
|
|
precall = _ped->GetRECallback();
|
|
if(precall)
|
|
return precall->ContextSensitiveHelp(fEnterMode);
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::CanInPlaceActivate()
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::CanInPlaceActivate
|
|
*
|
|
* @rdesc NOERROR or S_FALSE
|
|
*/
|
|
STDMETHODIMP COleObject::CanInPlaceActivate()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN,
|
|
"COleObject::CanInPlaceActivate");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// If we have a callback && the object is willing to show
|
|
// content, then we can in-place activate
|
|
|
|
if(_ped->GetRECallback() && _pi.dvaspect == DVASPECT_CONTENT)
|
|
return NOERROR;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnInPlaceActivate()
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::OnInPlaceActivate
|
|
*
|
|
* @rdesc noerror
|
|
*/
|
|
STDMETHODIMP COleObject::OnInPlaceActivate()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnInPlaceActivate");
|
|
// assume that in-place objects can never be blank.
|
|
_pi.dwFlags &= ~REO_BLANK;
|
|
_fInPlaceActive = TRUE;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnUIActivate ()
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::OnUIActivate. Notifies
|
|
* the container that the object is about to be activated in
|
|
* place with UI elements suchs as merged menus
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::OnUIActivate()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnUIActivate");
|
|
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CObjectMgr * pobjmgr = _ped->GetObjectMgr();
|
|
IRichEditOleCallback *precall = pobjmgr->GetRECallback();
|
|
|
|
if(precall)
|
|
{
|
|
// Force this object to be selected, if it isn't already
|
|
// Update the selection before making the outgoing call
|
|
if(!(_pi.dwFlags & REO_SELECTED))
|
|
{
|
|
CTxtSelection *psel = _ped->GetSel();
|
|
if(psel)
|
|
psel->SetSelection(_cp, _cp + 1);
|
|
}
|
|
precall->ShowContainerUI(FALSE);
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// This is an optimization for activating multiple
|
|
pobjmgr->SetShowUIPending(FALSE);
|
|
|
|
// RAID 7212
|
|
// We don't want to set the in place active object if we are already in the process of activating the pbject.
|
|
// Otherwise, there will be bad interactions with the code in TxDraw for out of process servers.
|
|
// Note : it may be possible to always set this in ActivateObj but I left this here for those cases wher
|
|
// OnUIActivate may be called directly.
|
|
if (!_fActivateCalled)
|
|
{
|
|
Assert(!pobjmgr->GetInPlaceActiveObject());
|
|
pobjmgr->SetInPlaceActiveObject(this);
|
|
_pi.dwFlags |= REO_INPLACEACTIVE;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
/*
|
|
* COleObject::GetWindowContext(ppipframe, ppipuidoc, prcPos, prcClip, pipfinfo)
|
|
*
|
|
* @mfunc Implementation of IOleInPlaceSite::GetWindowContext.
|
|
* Enables the in-place object to retrieve the window
|
|
* interfaces that form the window object hierarchy.
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::GetWindowContext(
|
|
IOleInPlaceFrame **ppipframe, //@parm Where to put in-place frame
|
|
IOleInPlaceUIWindow **ppipuidoc,//@parm Where to put ui window
|
|
LPRECT prcPos, //@parm Position rect
|
|
LPRECT prcClip, //@parm Clipping rect
|
|
LPOLEINPLACEFRAMEINFO pipfinfo) //@parm Accelerator information
|
|
{
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::GetWindowContext");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Let container verify other parameters; we don't use them
|
|
if(!prcPos || !prcClip)
|
|
return E_INVALIDARG;
|
|
|
|
IRichEditOleCallback *precall = _ped->GetRECallback();
|
|
if(precall)
|
|
{
|
|
// Recall that there are two rects here in client coordiantes:
|
|
// the rect for this object (_rcPos) and the rect for
|
|
// our main display;
|
|
*prcPos = _rcPos;
|
|
|
|
// FUTURE (alexgo); we may need to get this from the
|
|
// display instead to handle the inactive state if we ever
|
|
// want to support embedded objects with the inactive state.
|
|
_ped->TxGetClientRect(prcClip);
|
|
return precall->GetInPlaceContext(ppipframe, ppipuidoc, pipfinfo);
|
|
}
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
/*
|
|
* COleObject::Scroll(sizeScroll)
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::Scroll
|
|
*
|
|
* @rdesc E_NOTIMPL;
|
|
*/
|
|
STDMETHODIMP COleObject::Scroll(
|
|
SIZE sizeScroll) //@parm Amount to scroll
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::Scroll");
|
|
|
|
TRACEWARNSZ("method not implemented!");
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnUIDeactivate (fUndoable)
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::OnUIDeactivate. Notifies
|
|
* the container that it should re-install its user interface
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::OnUIDeactivate(
|
|
BOOL fUndoable) //@parm Whether you can undo anything here
|
|
{
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnUIDeactivate");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
CObjectMgr *pobjmgr = _ped->GetObjectMgr();
|
|
IRichEditOleCallback *precall = _ped->GetRECallback();
|
|
|
|
if(_fIsPaintBrush)
|
|
{
|
|
// Hack for RAID 3293. Bitmap object disappears after editing.
|
|
// Apparently paint only triggers OnUIDeactivate and not OnInPlaceDeactivate
|
|
// assume that in-place objects can never be blank.
|
|
_fInPlaceActive = FALSE;
|
|
// Reset REO_INPLACEACTIVE
|
|
_pi.dwFlags &= ~REO_INPLACEACTIVE;
|
|
}
|
|
|
|
if(!precall)
|
|
return E_UNEXPECTED;
|
|
|
|
if(_ped->TxIsDoubleClickPending())
|
|
_ped->GetObjectMgr()->SetShowUIPending(TRUE);
|
|
else
|
|
{
|
|
// Ignore any errors; the old code did.
|
|
precall->ShowContainerUI(TRUE);
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
}
|
|
|
|
pobjmgr->SetInPlaceActiveObject(NULL);
|
|
|
|
if (!_fDeactivateCalled)
|
|
{
|
|
// We got here without DeActiveObj. Since to shutdown correctly
|
|
// we need to call this, we do so here.
|
|
DeActivateObj();
|
|
}
|
|
|
|
// Get focus back
|
|
_ped->TxSetFocus();
|
|
|
|
#ifdef DEBUG
|
|
// the OLE undo model is not very compatible with multi-level undo.
|
|
// For simplicity, just ignore stuff.
|
|
if(fUndoable)
|
|
{
|
|
TRACEWARNSZ("Ignoring a request to keep undo from an OLE object");
|
|
}
|
|
#endif
|
|
|
|
// Some objects draw outside the
|
|
// areas they are supposed to. So we need to
|
|
// just invalidate everything and redraw.
|
|
|
|
_ped->TxInvalidateRect(NULL, TRUE);
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnInPlaceDeactivate ()
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::OnInPlaceDeactivate
|
|
*
|
|
* @rdesc NOERROR
|
|
*/
|
|
STDMETHODIMP COleObject::OnInPlaceDeactivate()
|
|
{
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN,
|
|
"COleObject::OnInPlaceDeactivate");
|
|
|
|
_fInPlaceActive = FALSE;
|
|
|
|
//Reset REO_INPLACEACTIVE
|
|
_pi.dwFlags &= ~REO_INPLACEACTIVE;
|
|
|
|
if(!_punkobj)
|
|
return E_UNEXPECTED;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Apparently, WordArt 2.0 had some sizing problems. The original
|
|
// code has this call to GetExtent-->SetExtent, so I've kept it here.
|
|
|
|
if(_fIsWordArt2)
|
|
{
|
|
// Ignore errors. If anything fails, too bad.
|
|
FetchObjectExtents(); // this will reset _sizel
|
|
SetExtent(SE_NOTACTIVATING);
|
|
}
|
|
|
|
// Some objects draw outside the
|
|
// areas they are supposed to. So we need to
|
|
// just invalidate everything and redraw.
|
|
|
|
// Note that we do this in UIDeactivate as well; however, the
|
|
// double invalidation is necessary to cover some re-entrancy
|
|
// cases where we might be painted before everything is ready.
|
|
|
|
_ped->TxInvalidateRect(NULL, TRUE);
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::DiscardUndoState ()
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::DiscardUndoState.
|
|
*
|
|
* @rdesc NOERROR
|
|
*/
|
|
STDMETHODIMP COleObject::DiscardUndoState()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN,
|
|
"COleObject::DiscardUndoState");
|
|
|
|
// Nothing to do here; we don't keep any OLE-undo state as it's
|
|
// not very compatible with multi-level undo.
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::DeactivateAndUndo ()
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::DeactivateAndUndo--
|
|
* called by an active object when the user invokes undo
|
|
* in the active object
|
|
*
|
|
* @rdesc NOERROR (yep, richedit1.0 ignored all the errors here)
|
|
*/
|
|
STDMETHODIMP COleObject::DeactivateAndUndo()
|
|
{
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::DeactivateAndUndo");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Ignore error
|
|
_ped->InPlaceDeactivate();
|
|
|
|
// COMPATIBILITY ISSUE: we don't bother doing any undo here, as
|
|
// a multi-level undo model is incompatible with OLE undo.
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnPosRectChange (prcPos)
|
|
*
|
|
* @mfunc implementation of IOleInPlaceSite::OnPosRectChange. This
|
|
* method is called by an in-place object when its extents have
|
|
* changed
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP COleObject::OnPosRectChange(
|
|
LPCRECT prcPos)
|
|
{
|
|
IOleInPlaceObject *pipo;
|
|
RECT rcClip;
|
|
RECT rcNewPos;
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnPosRectChange");
|
|
|
|
if(!prcPos)
|
|
return E_INVALIDARG;
|
|
|
|
if(!_punkobj)
|
|
return E_UNEXPECTED;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if(!_ped->fInplaceActive())
|
|
return E_UNEXPECTED;
|
|
|
|
// Check to see if the rect moved; we don't allow this, but
|
|
// do allow the object to keep the new size
|
|
|
|
rcNewPos = *prcPos;
|
|
|
|
if(prcPos->left != _rcPos.left || prcPos->top != _rcPos.top)
|
|
{
|
|
rcNewPos.right = rcNewPos.left + (prcPos->right - prcPos->left);
|
|
rcNewPos.bottom = rcNewPos.top + (prcPos->bottom - prcPos->top);
|
|
}
|
|
|
|
_ped->TxGetClientRect(&rcClip);
|
|
|
|
HRESULT hr = _punkobj->QueryInterface(IID_IOleInPlaceObject, (void **)&pipo);
|
|
if(hr == NOERROR)
|
|
{
|
|
hr = pipo->SetObjectRects(&rcNewPos, &rcClip);
|
|
pipo->Release();
|
|
|
|
// bug fix 6073
|
|
// Need to set viewchange flag so we resize the ole object properly on ITextServices::TxDraw
|
|
CObjectMgr * pobjmgr = _ped->GetObjectMgr();
|
|
if (pobjmgr && pobjmgr->GetInPlaceActiveObject() == this)
|
|
_fViewChange = TRUE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnDataChange (pformatetc, pmedium)
|
|
*
|
|
* @mfunc implementation of IAdviseSink::OnDataChange
|
|
*
|
|
* @rdesc NOERROR
|
|
*/
|
|
STDMETHODIMP_(void) COleObject::OnDataChange(
|
|
FORMATETC *pformatetc, //@parm Format of data that changed
|
|
STGMEDIUM *pmedium) //@parm Data that changed
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnDataChange");
|
|
CCallMgr callmgr(_ped);
|
|
|
|
if(IsZombie())
|
|
return;
|
|
_pi.dwFlags &= ~REO_BLANK;
|
|
// this will also set the modified flag
|
|
_ped->GetCallMgr()->SetChangeEvent(CN_GENERIC);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnViewChange(dwAspect, lindex)
|
|
*
|
|
* @mfunc implementation of IAdviseSink::OnViewChange. Notifies
|
|
* us that the object's view has changed.
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
STDMETHODIMP_(void) COleObject::OnViewChange(
|
|
DWORD dwAspect, //@parm Aspect that has changed
|
|
LONG lindex) //@parm unused
|
|
{
|
|
CStabilize stabilize(this);
|
|
CCallMgr callmgr(_ped);
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnViewChange");
|
|
|
|
if(!_punkobj)
|
|
return; // E_UNEXPECTED
|
|
|
|
if(IsZombie())
|
|
return;
|
|
|
|
if (_fInUndo) // This object has been deleted, forget view change
|
|
return;
|
|
|
|
_pi.dwFlags &= ~REO_BLANK;
|
|
// Richedit 1.0 ignored errors on getting object extents
|
|
|
|
FetchObjectExtents();
|
|
|
|
// bug fix 6073
|
|
// Need to set viewchange flag so we resize the ole object properly on ITextServices::TxDraw
|
|
CObjectMgr * pobjmgr = _ped->GetObjectMgr();
|
|
if (pobjmgr && pobjmgr->GetInPlaceActiveObject() == this)
|
|
_fViewChange = TRUE;
|
|
|
|
CDisplay *pdp = _ped->_pdp;
|
|
if(pdp)
|
|
pdp->OnPostReplaceRange(CP_INFINITE, 0, 0, _cp, _cp + 1);
|
|
|
|
_ped->GetCallMgr()->SetChangeEvent(CN_GENERIC);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnRename (pmk)
|
|
*
|
|
* @mfunc implementation of IAdviseSink::OnRename. Notifies the container
|
|
* that the object has been renamed
|
|
*
|
|
* @rdesc E_NOTIMPL
|
|
*/
|
|
STDMETHODIMP_(void) COleObject::OnRename(
|
|
IMoniker *pmk) //@parm Object's new name
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnRename");
|
|
|
|
TRACEWARNSZ("IAdviseSink::OnRename not implemented!");
|
|
|
|
return; // E_NOTIMPL;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnSave ()
|
|
*
|
|
* @mfunc implementation of IAdviseSink::OnSave. Notifies the container
|
|
* that an object has been saved
|
|
*
|
|
* @rdesc NOERROR
|
|
*/
|
|
STDMETHODIMP_(void) COleObject::OnSave()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnSave");
|
|
_pi.dwFlags &= ~REO_BLANK;
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnClose ()
|
|
*
|
|
* @mfunc implementation of IAdviseSink::OnClose. Notifies the container
|
|
* that an object has been closed.
|
|
*
|
|
* @rdesc NOERROR
|
|
*/
|
|
STDMETHODIMP_(void) COleObject::OnClose()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::OnClose");
|
|
|
|
if(IsZombie())
|
|
return;
|
|
|
|
// If the object is blank (i.e. no data in it), we don't want to leave
|
|
// it in the backing store--there is nothing for us to draw && therefore
|
|
// nothing for the user to click on! So just delete the object with
|
|
// a space. Note that 1.0 actually deleted the object; we'll just
|
|
// replace it with a space to make everything work out right.
|
|
if(_pi.dwFlags & REO_BLANK)
|
|
{
|
|
CCallMgr callmgr(_ped);
|
|
CStabilize stabilize(this);
|
|
CRchTxtPtr rtp(_ped, _cp);
|
|
|
|
// We don't want the delete of this object to go on the undo
|
|
// stack. We use a space so that cp's will work out right for
|
|
// other undo actions.
|
|
rtp.ReplaceRange(1, 1, L" ", NULL, -1);
|
|
}
|
|
_ped->TxSetForegroundWindow();
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnPreReplaceRange
|
|
*
|
|
* @mfunc implementation of ITxNotify::OnPreReplaceRange
|
|
* called before changes are made to the backing store
|
|
*/
|
|
void COleObject::OnPreReplaceRange(
|
|
LONG cp, //@parm cp of the changes
|
|
LONG cchDel, //@parm #of chars deleted
|
|
LONG cchNew, //@parm # of chars added
|
|
LONG cpFormatMin, //@parm min cp of formatting changes
|
|
LONG cpFormatMax) //@parm max cp of formatting changes
|
|
{
|
|
Assert(_fInUndo == FALSE);
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnPostReplaceRange
|
|
*
|
|
* @mfunc implementation of ITxNotify::OnPostReplaceRange
|
|
* called after changes are made to the backing store
|
|
*
|
|
* @comm we use this method to keep our cp's up-to-date
|
|
*/
|
|
void COleObject::OnPostReplaceRange(
|
|
LONG cp, //@parm cp of the changes
|
|
LONG cchDel, //@parm #of chars deleted
|
|
LONG cchNew, //@parm # of chars added
|
|
LONG cpFormatMin, //@parm min cp of formatting changes
|
|
LONG cpFormatMax) //@parm max cp of formatting changes
|
|
{
|
|
// The only case we need to worry about is when changes
|
|
// come before our object
|
|
|
|
Assert(_fInUndo == FALSE);
|
|
|
|
_fDraw = TRUE;
|
|
if(cp <= _cp)
|
|
{
|
|
if(cp + cchDel > _cp)
|
|
{
|
|
_fDraw = FALSE;
|
|
return;
|
|
}
|
|
_cp += (cchNew - cchDel);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* COleObject::Zombie ()
|
|
*
|
|
* @mfunc
|
|
* Turn this object into a zombie
|
|
*
|
|
*/
|
|
void COleObject::Zombie ()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::Zombie");
|
|
|
|
_ped = NULL;
|
|
}
|
|
|
|
/*
|
|
* COleObject::COleObject(ped)
|
|
*
|
|
* @mfunc constructor
|
|
*/
|
|
COleObject::COleObject(
|
|
CTxtEdit *ped) //@parm context for this object
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::COleObject");
|
|
|
|
AddRef();
|
|
|
|
// Most values will be NULL by virtue of the allocator
|
|
_ped = ped;
|
|
|
|
CNotifyMgr *pnm = ped->GetNotifyMgr();
|
|
if(pnm)
|
|
pnm->Add((ITxNotify *)this);
|
|
}
|
|
|
|
/*
|
|
* COleObject::GetObjectData(preobj, dwFlags)
|
|
*
|
|
* @mfunc fills out an REOBJECT structure with information relevant
|
|
* to this object
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
HRESULT COleObject::GetObjectData(
|
|
REOBJECT *preobj, //@parm Struct to fill out
|
|
DWORD dwFlags) //@parm Indicate what data is requested
|
|
{
|
|
IOleObject *poo = NULL;
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::GetObjectData");
|
|
|
|
Assert(preobj);
|
|
Assert(_punkobj);
|
|
|
|
preobj->cp = _cp;
|
|
|
|
if(_punkobj->QueryInterface(IID_IOleObject, (void **)&poo) == NOERROR)
|
|
{
|
|
// Don't worry about failures here
|
|
poo->GetUserClassID(&(preobj->clsid));
|
|
}
|
|
|
|
preobj->dwFlags = _pi.dwFlags;
|
|
preobj->dvaspect = _pi.dvaspect;
|
|
preobj->dwUser = _pi.dwUser;
|
|
preobj->sizel = _sizel;
|
|
|
|
if(dwFlags & REO_GETOBJ_POLEOBJ)
|
|
{
|
|
preobj->poleobj = poo;
|
|
if(poo)
|
|
poo->AddRef();
|
|
}
|
|
else
|
|
preobj->poleobj = NULL;
|
|
|
|
if(poo)
|
|
poo->Release();
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if(dwFlags & REO_GETOBJ_PSTG)
|
|
{
|
|
preobj->pstg = _pstg;
|
|
if(_pstg)
|
|
_pstg->AddRef();
|
|
}
|
|
else
|
|
preobj->pstg = NULL;
|
|
|
|
if(dwFlags & REO_GETOBJ_POLESITE)
|
|
{
|
|
// COMPATIBILITY HACK!! Note that we don't 'release' any pointer that
|
|
// may already be in the stored in the site. RichEdit1.0 always sets
|
|
// the value, consequently several apps pass in garbage for the site.
|
|
//
|
|
// If the site was previously set, we will get a reference counting
|
|
// bug, so be sure that doesn't happen!
|
|
|
|
preobj->polesite = (IOleClientSite *)this;
|
|
AddRef();
|
|
}
|
|
else
|
|
preobj->polesite = NULL;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::IsLink()
|
|
*
|
|
* @mfunc returns TRUE if the object is a link
|
|
*
|
|
* @rdesc BOOL
|
|
*/
|
|
BOOL COleObject::IsLink()
|
|
{
|
|
return !!(_pi.dwFlags & REO_LINK);
|
|
}
|
|
|
|
/*
|
|
* COleObject::InitFromREOBJECT(cp, preobj)
|
|
*
|
|
* @mfunc initializes this object's state from the given
|
|
* REOBJECT data structure
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
HRESULT COleObject::InitFromREOBJECT(
|
|
LONG cp, //@parm cp for the object
|
|
REOBJECT *preobj) //@parm Data to use for initialization
|
|
{
|
|
IOleLink *plink;
|
|
HRESULT hr = E_INVALIDARG;
|
|
CRchTxtPtr rtp(_ped, 0);
|
|
POINT pt;
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::InitFromREOBJECT");
|
|
|
|
Assert(_punkobj == NULL);
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
_cp = cp;
|
|
|
|
if(preobj->poleobj)
|
|
hr = preobj->poleobj->QueryInterface(IID_IUnknown, (void **)&_punkobj);
|
|
else
|
|
{
|
|
_punkobj = (IOleClientSite *) this;
|
|
AddRef();
|
|
hr = NOERROR;
|
|
}
|
|
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
_pstg = preobj->pstg;
|
|
if(_pstg)
|
|
_pstg->AddRef();
|
|
|
|
_pi.dwFlags = preobj->dwFlags & REO_READWRITEMASK;
|
|
_pi.dwUser = preobj->dwUser;
|
|
_pi.dvaspect = preobj->dvaspect;
|
|
|
|
_sizel = preobj->sizel; // COMPATIBILITY ISSUE: the RE 1.0 code had
|
|
// some stuff to deal with REO_DYNAMICSIZE
|
|
// here. We do not currently support that.
|
|
|
|
if(_punkobj->QueryInterface(IID_IOleLink, (void **)&plink) == NOERROR)
|
|
{
|
|
_pi.dwFlags |= REO_LINK | REO_LINKAVAILABLE;
|
|
plink->Release();
|
|
}
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
if (IsEqualCLSID(preobj->clsid, CLSID_StaticMetafile) ||
|
|
IsEqualCLSID(preobj->clsid, CLSID_StaticDib) ||
|
|
IsEqualCLSID(preobj->clsid, CLSID_Picture_EnhMetafile))
|
|
{
|
|
_pi.dwFlags |= REO_STATIC;
|
|
}
|
|
else if(IsExcelCLSID(preobj->clsid))
|
|
_pi.dwFlags |= REO_GETMETAFILE;
|
|
|
|
else if(IsEqualCLSID(preobj->clsid, CLSID_WordArt))
|
|
_fIsWordArt2 = TRUE;
|
|
|
|
else if(IsEqualCLSID(preobj->clsid, CLSID_PaintbrushPicture) ||
|
|
IsEqualCLSID(preobj->clsid, CLSID_BitmapImage))
|
|
{
|
|
_fIsPaintBrush = TRUE;
|
|
|
|
// These calls will initialize the flag, _fPBUseLocalSizel, which
|
|
// indicates that for this PB object, SetExtent calls are not
|
|
// acknowledged by the object, and we are to use our local value
|
|
// of _sizel as the object extents.
|
|
FetchObjectExtents();
|
|
SetExtent(SE_NOTACTIVATING);
|
|
}
|
|
|
|
hr = ConnectObject();
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// This is a bit non-intuitive, but we need to figure out
|
|
// where the object would be so that it can inplace activate correctly.
|
|
|
|
if(cp)
|
|
cp--;
|
|
|
|
rtp.SetCp(cp);
|
|
|
|
pt.x = pt.y = 0;
|
|
if (!_ped->_pdp->IsFrozen())
|
|
_ped->_pdp->PointFromTp(rtp, NULL, FALSE, pt, NULL, TA_TOP);
|
|
_rcPos.top = _rcPos.bottom = pt.y; //bottom will be set below in
|
|
// FetchExtents
|
|
_rcPos.left = _rcPos.right = pt.x;
|
|
|
|
if(preobj->sizel.cx || preobj->sizel.cy)
|
|
_sizel = preobj->sizel;
|
|
else
|
|
FetchObjectExtents();
|
|
|
|
if (!_ped->_pdp->IsFrozen())
|
|
ResetPosRect();
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// We don't do the following anymore although it was originally spec'd as the correct
|
|
// behavior for applications in OLE 2.01. The reason is that no one else seems to and
|
|
// it seems to result in some odd behavior.
|
|
#if 0
|
|
// Finally, lock down Link objects so they we don't try to refetch their
|
|
// extents from the server. After initialization, link object size is
|
|
// entirely determined by the container.
|
|
if(_pi.dwFlags & REO_LINK)
|
|
{
|
|
// so we don't call GetExtents on remeasuring.
|
|
_fSetExtent = TRUE;
|
|
}
|
|
#endif
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::MeasureObj(dypInch, dxpInch, xWidth, yHeight, yDescent)
|
|
*
|
|
* Review: (keithcu) Should yDescentFont be descent of font
|
|
* or descent of entire line? LS does one thing, old measurer
|
|
* does another. I'm hoping that this feature is only used on
|
|
* lines with one font..
|
|
* @mfunc calculates the size of this object in device units
|
|
*/
|
|
void COleObject::MeasureObj(
|
|
long dypInch, //@parm resolution of device
|
|
long dxpInch,
|
|
LONG & xWidth, //@parm Object width
|
|
LONG & yAscent, //@parm Object ascent
|
|
LONG & yDescent, //@parm Object descent
|
|
SHORT yDescentFont) //@parm object's font descent
|
|
{
|
|
xWidth = W32->HimetricXtoDX(_sizel.cx, dxpInch);
|
|
LONG yHeight = W32->HimetricYtoDY(_sizel.cy, dypInch);
|
|
|
|
if (_pi.dwFlags & REO_BELOWBASELINE)
|
|
{
|
|
yDescent = yDescentFont;
|
|
yAscent = max(0, yHeight - yDescent);
|
|
}
|
|
else //The normal case
|
|
{
|
|
yAscent = yHeight;
|
|
yDescent = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* COleObject::InHandle(x, y, &pt)
|
|
*
|
|
* @mfunc See if a point is in the rectangle defined by the handle at
|
|
* the given coordinates.
|
|
*
|
|
* @rdesc True if point is in handle.
|
|
*/
|
|
BOOL COleObject::InHandle(
|
|
int x, //@parm Upper left corner x coordinate of handle box
|
|
int y, //@parm Upper left corner y coordinate of handle box
|
|
const POINT &pt)//@parm Point to check
|
|
{
|
|
RECT rc;
|
|
|
|
rc.left = x;
|
|
rc.top = y;
|
|
|
|
// Add one to bottom right because PtInRect does not consider
|
|
// points on bottom or right to be in rect.
|
|
rc.right = x + dxyHandle + 1;
|
|
rc.bottom = y + dxyHandle + 1;
|
|
return PtInRect(&rc, pt);
|
|
}
|
|
|
|
/*
|
|
* COleObject::CheckForHandleHit(&pt)
|
|
*
|
|
* @mfunc Check for a hit on any of the frame handles.
|
|
*
|
|
* @rdesc NULL if no hit, cursor resource ID if there is a hit.
|
|
*/
|
|
LPTSTR COleObject::CheckForHandleHit(
|
|
const POINT &pt) //@parm POINT containing client coord. of the cursor.
|
|
{
|
|
RECT rc;
|
|
|
|
// If object is not resizeable, no chance of hitting a resize handle!
|
|
if(!(_pi.dwFlags & REO_RESIZABLE))
|
|
return NULL;
|
|
|
|
CopyRect(&rc, &_rcPos);
|
|
|
|
if(!_dxyFrame)
|
|
_dxyFrame = dxyFrameDefault;
|
|
|
|
// Check to see if point is farther into the interior of the
|
|
// object than the handles extent. If it is we can just bail.
|
|
InflateRect(&rc, -(_dxyFrame + dxyHandle), -(_dxyFrame + dxyHandle));
|
|
if(PtInRect(&rc, pt))
|
|
return NULL;
|
|
|
|
// Check to see if point is in any of the handles and
|
|
// return the proper cursor ID if it is.
|
|
InflateRect(&rc, dxyHandle, dxyHandle);
|
|
|
|
if(InHandle(rc.left, rc.top, pt) ||
|
|
InHandle(rc.right-dxyHandle, rc.bottom-dxyHandle, pt))
|
|
{
|
|
return IDC_SIZENWSE;
|
|
}
|
|
if(InHandle(rc.left, rc.top+(rc.bottom-rc.top-dxyHandle)/2, pt) ||
|
|
InHandle(rc.right-dxyHandle,
|
|
rc.top+(rc.bottom-rc.top-dxyHandle)/2, pt))
|
|
{
|
|
return IDC_SIZEWE;
|
|
}
|
|
if(InHandle(rc.left, rc.bottom-dxyHandle, pt) ||
|
|
InHandle(rc.right-dxyHandle, rc.top, pt))
|
|
{
|
|
return IDC_SIZENESW;
|
|
}
|
|
if(InHandle(rc.left+(rc.right-rc.left-dxyHandle)/2, rc.top, pt) ||
|
|
InHandle(rc.left+(rc.right-rc.left-dxyHandle)/2,
|
|
rc.bottom-dxyHandle, pt))
|
|
{
|
|
return IDC_SIZENS;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* COleObject::DrawHandle(hdc, x, y)
|
|
*
|
|
* @mfunc Draw a handle on the object frame at the specified coordinate
|
|
*/
|
|
void COleObject::DrawHandle(
|
|
HDC hdc, //@parm HDC to be drawn into
|
|
int x, //@parm x coordinate of upper-left corner of handle box
|
|
int y) //@parm y coordinate of upper-left corner of handle box
|
|
{
|
|
RECT rc;
|
|
|
|
// Draw handle by inverting
|
|
rc.left = x;
|
|
rc.top = y;
|
|
rc.right = x + dxyHandle;
|
|
rc.bottom = y + dxyHandle;
|
|
InvertRect(hdc, (LPRECT)&rc);
|
|
}
|
|
|
|
/*
|
|
* COleObject::DrawFrame(pdp, hdc, prc)
|
|
*
|
|
* @mfunc Draw a frame around the object. Invert if required and
|
|
* include handles if required.
|
|
*/
|
|
void COleObject::DrawFrame(
|
|
const CDisplay *pdp, //@parm the display to draw to
|
|
HDC hdc, //@parm the device context
|
|
RECT *prc) //@parm the rect around which to draw
|
|
{
|
|
if(_pi.dwFlags & REO_OWNERDRAWSELECT)
|
|
return;
|
|
|
|
RECT rc;
|
|
CopyRect(&rc, prc);
|
|
|
|
if(_pi.dwFlags & REO_INVERTEDSELECT)
|
|
InvertRect(hdc, &rc); //Invert entire object
|
|
|
|
else
|
|
{
|
|
// Just the border, so use a null brush
|
|
SaveDC(hdc);
|
|
SetROP2(hdc, R2_NOT);
|
|
SelectObject(hdc, GetStockObject(NULL_BRUSH));
|
|
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
|
|
RestoreDC(hdc, -1);
|
|
}
|
|
|
|
if(_pi.dwFlags & REO_RESIZABLE)
|
|
{
|
|
int bkmodeOld;
|
|
HPEN hpen;
|
|
LOGPEN logpen;
|
|
|
|
bkmodeOld = SetBkMode(hdc, TRANSPARENT);
|
|
|
|
// Get frame width
|
|
_dxyFrame = dxyFrameDefault;
|
|
hpen = (HPEN)GetCurrentObject(hdc, OBJ_PEN);
|
|
if(W32->GetObject(hpen, sizeof(LOGPEN), &logpen))
|
|
{
|
|
if(logpen.lopnWidth.x)
|
|
_dxyFrame = (SHORT)logpen.lopnWidth.x;
|
|
}
|
|
|
|
// Draw handles inside rectangle boundary
|
|
InflateRect(&rc, -_dxyFrame, -_dxyFrame);
|
|
|
|
LONG x = rc.left;
|
|
|
|
DrawHandle(hdc, x, rc.top);
|
|
DrawHandle(hdc, x, rc.top + (rc.bottom - rc.top - dxyHandle)/2);
|
|
DrawHandle(hdc, x, rc.bottom - dxyHandle);
|
|
|
|
x = rc.left + (rc.right - rc.left - dxyHandle)/2;
|
|
DrawHandle(hdc, x, rc.top);
|
|
DrawHandle(hdc, x, rc.bottom - dxyHandle);
|
|
|
|
x = rc.right - dxyHandle;
|
|
DrawHandle(hdc, x, rc.top);
|
|
DrawHandle(hdc, x, rc.top + (rc.bottom - rc.top - dxyHandle)/2);
|
|
DrawHandle(hdc, x, rc.bottom - dxyHandle);
|
|
|
|
SetBkMode(hdc, bkmodeOld);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* COleObject::CreateDib (hdc)
|
|
*
|
|
* @mfunc Create DIB for Windows CE display
|
|
*/
|
|
void COleObject::CreateDib(
|
|
HDC hdc)
|
|
{
|
|
int nCol = 0;
|
|
BYTE *pbDib;
|
|
HGLOBAL hnew = NULL;
|
|
BYTE *pbSrcBits;
|
|
LPBITMAPINFO pbmi = (LPBITMAPINFO) GlobalLock(_hdata);
|
|
DWORD dwPixelsPerRow = 0;
|
|
DWORD dwPixels = 0;
|
|
|
|
if(pbmi->bmiHeader.biBitCount <= 8)
|
|
{
|
|
nCol = 1 << pbmi->bmiHeader.biBitCount;
|
|
|
|
// Calculate the number of pixels. Account for DWORD alignment
|
|
DWORD dwPixelsPerByte = 8 / pbmi->bmiHeader.biBitCount;
|
|
DWORD dwBitsPerRow = pbmi->bmiHeader.biWidth * pbmi->bmiHeader.biBitCount;
|
|
dwBitsPerRow = (dwBitsPerRow + 7) & ~7; // Round up to byte boundary
|
|
DWORD dwBytesPerRow = dwBitsPerRow / 8;
|
|
dwBytesPerRow = (dwBytesPerRow + 3) & ~3; // Round up to DWORD
|
|
dwPixelsPerRow = dwBytesPerRow * dwPixelsPerByte;
|
|
|
|
// Double check with original
|
|
#ifdef DEBUG
|
|
DWORD dwBlockSize = GlobalSize(_hdata);
|
|
DWORD dwBitMapBytes = dwBlockSize - sizeof(BITMAPINFOHEADER) - (nCol * sizeof(RGBQUAD));
|
|
DWORD dwBitMapPixels = dwBitMapBytes * dwPixelsPerByte;
|
|
dwPixels = dwPixelsPerRow * pbmi->bmiHeader.biHeight;
|
|
Assert(dwPixels == dwBitMapPixels);
|
|
#endif
|
|
}
|
|
else
|
|
dwPixelsPerRow = pbmi->bmiHeader.biWidth;
|
|
|
|
dwPixels = dwPixelsPerRow * pbmi->bmiHeader.biHeight;
|
|
|
|
pbSrcBits = (BYTE*)(pbmi) + sizeof(BITMAPINFOHEADER) + (nCol * sizeof(RGBQUAD));
|
|
|
|
#ifdef TARGET_NT
|
|
|
|
// For NT viewing convert four color bitmaps to 16 color bitmap
|
|
if(nCol == 4)
|
|
{
|
|
// First let's figure out how big the new memory block needs to be.
|
|
DWORD cb = sizeof(BITMAPINFOHEADER) + (16 * sizeof(RGBQUAD));
|
|
cb += dwPixels / 2;
|
|
hnew = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, cb);
|
|
|
|
// Now locate the interesting places
|
|
LPBITMAPINFO pNewBmi = (LPBITMAPINFO) GlobalLock(hnew);
|
|
BYTE *pNewBits = (BYTE*)(pNewBmi) + sizeof(BITMAPINFOHEADER) + (16 * sizeof(RGBQUAD));
|
|
|
|
// Modify the header
|
|
pNewBmi->bmiHeader = pbmi->bmiHeader;
|
|
pNewBmi->bmiHeader.biBitCount = 4;
|
|
pNewBmi->bmiHeader.biClrUsed = 4;
|
|
|
|
// Set up the DIB RGB Colors.
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
BYTE data = 0;
|
|
switch (i % 4)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
data = 0x55;
|
|
break;
|
|
case 2:
|
|
data = 0xAA;
|
|
break;
|
|
case 3:
|
|
data = 0xFF;
|
|
break;
|
|
}
|
|
pNewBmi->bmiColors[i].rgbBlue = data;
|
|
pNewBmi->bmiColors[i].rgbGreen = data;
|
|
pNewBmi->bmiColors[i].rgbRed = data;
|
|
pNewBmi->bmiColors[i].rgbReserved = 0;
|
|
}
|
|
|
|
// Convert the byte array.
|
|
for (DWORD j = 0; j < dwPixels; j++)
|
|
{
|
|
int iSrcByte = j / 4;
|
|
|
|
BYTE bits = pbSrcBits[iSrcByte];
|
|
bits >>= 6 - (j%4) * 2;
|
|
bits &= 0x3;
|
|
int iDstByte = j / 2;
|
|
bits <<= 4 - (j%2) * 4;
|
|
pNewBits[iDstByte] |= bits;
|
|
}
|
|
GlobalUnlock(pbmi);
|
|
pbmi = pNewBmi;
|
|
pbSrcBits = pNewBits;
|
|
}
|
|
#endif
|
|
|
|
_hdib = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&pbDib, NULL, 0);
|
|
if(_hdib == NULL)
|
|
{
|
|
_ped->GetCallMgr()->SetOutOfMemory();
|
|
|
|
// V-GUYB:
|
|
// Do not attempt to repaint this picture until the user starts typing in the
|
|
// control. This allows the user to dismiss the oom that will appear and then
|
|
// save the document, and then free up some space. If we don't do this here,
|
|
// every time the oom msg is dismissed it will appear again. This doesn't allow
|
|
// the user to save the document unless they can find some memory to free.
|
|
_fDraw = FALSE;
|
|
|
|
TRACEWARNSZ("Out of memory creating DIB");
|
|
return;
|
|
}
|
|
|
|
DWORD nBytes;
|
|
|
|
if(nCol)
|
|
{
|
|
DWORD nPixelsPerByte = 8 / pbmi->bmiHeader.biBitCount;
|
|
nBytes = dwPixels / nPixelsPerByte;
|
|
}
|
|
else
|
|
nBytes = dwPixels * 4; // Each pixel occupies 4 bytes in DIB
|
|
CopyMemory(pbDib, pbSrcBits, nBytes);
|
|
|
|
GlobalUnlock(pbmi);
|
|
GlobalFree(hnew);
|
|
}
|
|
|
|
/*
|
|
* COleObject::DrawDib (hdc, prc)
|
|
*
|
|
* @mfunc Auxiliary function that draws the dib in the given dc
|
|
*/
|
|
void COleObject::DrawDib(
|
|
HDC hdc,
|
|
RECT *prc)
|
|
{
|
|
if(!_hdib)
|
|
CreateDib(hdc);
|
|
|
|
// If _hdib is still NULL, just return. Maybe out of memory.
|
|
if(!_hdib)
|
|
return;
|
|
|
|
HDC hdcMem = CreateCompatibleDC(hdc);
|
|
LPBITMAPINFO pbmi = (LPBITMAPINFO) LocalLock(_hdata);
|
|
SelectObject(hdcMem, _hdib);
|
|
StretchBlt(hdc, prc->left, prc->top,
|
|
prc->right - prc->left, prc->bottom - prc->top,
|
|
hdcMem, 0, 0, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight, SRCCOPY);
|
|
GlobalUnlock(pbmi);
|
|
DeleteDC(hdcMem);
|
|
}
|
|
|
|
/*
|
|
* COleObject::DrawObj (pdp, hdc, fMetafile, ppt, prcRender)
|
|
*
|
|
* @mfunc draws the object
|
|
*/
|
|
void COleObject::DrawObj(
|
|
const CDisplay *pdp, //@parm Display object for the view
|
|
LONG dypInch, //@parm Resolution of device
|
|
LONG dxpInch,
|
|
HDC hdc, //@parm Drawing HDC (can differ from display's)
|
|
BOOL fMetafile, //@parm Whether the HDC is a metafile
|
|
POINT * ppt, //@parm Top left corner of where to draw
|
|
RECT * prcRender, //@parm Pointer to render rectangle
|
|
LONG yBaselineLine,
|
|
LONG yDescentMaxCur)
|
|
{
|
|
LONG adjust = 0;
|
|
BOOL fMultipleSelect = FALSE;
|
|
CObjectMgr * pobjmgr = _ped->GetObjectMgr();
|
|
IViewObject * pvo;
|
|
CDisplayPrinter *pdpPrint;
|
|
RECT rc, rc1;
|
|
LONG cpMin, cpMost;
|
|
_ped->GetSelRangeForRender(&cpMin, &cpMost);
|
|
BOOL fSelected = _cp >= cpMin && _cp < cpMost;
|
|
|
|
SaveDC(hdc);
|
|
|
|
if (fSelected)
|
|
SetBkMode(hdc, OPAQUE);
|
|
|
|
if(pdp->IsMain() && !(_pi.dwFlags & REO_OWNERDRAWSELECT))
|
|
{
|
|
if(fSelected)
|
|
{
|
|
if(cpMost - cpMin > 1)
|
|
fMultipleSelect = TRUE;
|
|
|
|
// The following overwrites the selection colors currently
|
|
// selected into the hdc. We do this for RE 2.0 compatibility,
|
|
// e.g., else selected name links in Outlook appear yellow
|
|
// after the InvertRect() in DrawFrame() (altho the semicolon
|
|
// blanks appear in selection colors). Note: we could define
|
|
// REO_OWNERDRAWSELECT, which would bypass the following 2 lines
|
|
// and suppress the InvertRect below and in DrawFrame(). Then
|
|
// Outlook's From:, To:, and CC: would have correct selection
|
|
// colors throughout.
|
|
::SetTextColor(hdc, _ped->TxGetForeColor());
|
|
::SetBkColor (hdc, _ped->TxGetBackColor());
|
|
}
|
|
}
|
|
|
|
if(_fInPlaceActive || !_fDraw)
|
|
{
|
|
// If we're inplace active, don't do anything; the server is
|
|
// drawing for us. We also don't do anything prior to the fDraw
|
|
// property being set
|
|
return;
|
|
}
|
|
|
|
//REVIEW (keithcu) Do we need this?
|
|
//Worse case, update only if pdp->IsMain(). This whole thing is ugly.
|
|
if (pdp->IsMain())
|
|
ResetPosRect(&adjust); // Update position rectangle
|
|
|
|
// Draw object where we are asked within rendering rectangle
|
|
rc.left = ppt->x;
|
|
rc.right = rc.left + W32->HimetricXtoDX(_sizel.cx, dxpInch);
|
|
|
|
rc.bottom = ppt->y + yBaselineLine;
|
|
rc.top = rc.bottom - W32->HimetricYtoDY(_sizel.cy, dypInch);
|
|
|
|
if (_pi.dwFlags & REO_BELOWBASELINE)
|
|
OffsetRect(&rc, 0, yDescentMaxCur);
|
|
|
|
SetTextAlign(hdc, TA_TOP);
|
|
|
|
SaveDC(hdc); // calls to OLE object (IViewObject::Draw or OleDraw) might change HDC
|
|
|
|
//Do clipping because OLE doesn't know where to draw
|
|
IntersectClipRect(hdc, prcRender->left, prcRender->top,
|
|
prcRender->right, prcRender->bottom);
|
|
|
|
if(_hdata)
|
|
{
|
|
// This is some Windows CE Dib, let's try the direct approach
|
|
DrawDib(hdc, &rc);
|
|
}
|
|
else if(fMetafile)
|
|
{
|
|
if(_punkobj->QueryInterface(IID_IViewObject, (void **)&pvo)
|
|
== NOERROR)
|
|
{
|
|
pdpPrint = (CDisplayPrinter *)pdp;
|
|
rc1 = pdpPrint->GetPrintPage();
|
|
|
|
// Fix up rc for Draw()
|
|
rc1.bottom = rc1.bottom - rc1.top;
|
|
rc1.right = rc1.right - rc1.left;
|
|
|
|
pvo->Draw(_pi.dvaspect, -1, NULL, NULL, 0, hdc, (RECTL *)&rc,
|
|
(RECTL *)&rc1, NULL, 0);
|
|
pvo->Release();
|
|
}
|
|
}
|
|
else
|
|
OleDraw(_punkobj, _pi.dvaspect, hdc, &rc);
|
|
|
|
RestoreDC(hdc, -1);
|
|
|
|
// Do selection stuff if this is for the main (screen) view.
|
|
if(pdp->IsMain())
|
|
{
|
|
if(_pi.dwFlags & REO_OPEN)
|
|
OleUIDrawShading(&rc, hdc);
|
|
|
|
// If the object has been selected by clicking on it, draw
|
|
// a frame and handles around it. Otherwise, if we are selected
|
|
// as part of a range, invert ourselves.
|
|
if(!fMetafile && pobjmgr->GetSingleSelect() == this)
|
|
DrawFrame(pdp, hdc, &rc);
|
|
|
|
else if(fMultipleSelect)
|
|
InvertRect(hdc, &rc);
|
|
}
|
|
RestoreDC(hdc, -1);
|
|
}
|
|
|
|
/*
|
|
* COleObject::Delete (publdr)
|
|
*
|
|
* @mfunc deletes this object from the backing store _without_
|
|
* making outgoing calls. The commit on generated anti-events
|
|
* will handle the outgoing calls
|
|
*/
|
|
void COleObject::Delete(
|
|
IUndoBuilder *publdr)
|
|
{
|
|
|
|
Assert(_fInUndo == FALSE);
|
|
_fInUndo = TRUE;
|
|
|
|
CNotifyMgr *pnm = _ped->GetNotifyMgr();
|
|
if(pnm)
|
|
pnm->Remove((ITxNotify *)this);
|
|
|
|
if(publdr)
|
|
{
|
|
// The anti-event will take care of calling IOO::Close for us
|
|
IAntiEvent *pae = gAEDispenser.CreateReplaceObjectAE(_ped, this);
|
|
if(pae)
|
|
publdr->AddAntiEvent(pae);
|
|
}
|
|
else
|
|
{
|
|
Close(OLECLOSE_NOSAVE);
|
|
MakeZombie();
|
|
}
|
|
|
|
// If we're being deleted, we can't be selected anymore
|
|
_pi.dwFlags &= ~REO_SELECTED;
|
|
_fDraw = 0;
|
|
}
|
|
|
|
/*
|
|
* COleObject::Restore()
|
|
*
|
|
* @mfunc restores the object from the undo state back into the
|
|
* backing store
|
|
*
|
|
* No outgoing calls will be made
|
|
*/
|
|
void COleObject::Restore()
|
|
{
|
|
Assert(_fInUndo);
|
|
|
|
_fInUndo = FALSE;
|
|
_fDraw = TRUE;
|
|
|
|
CNotifyMgr *pnm = _ped->GetNotifyMgr();
|
|
if(pnm)
|
|
pnm->Add((ITxNotify *)this);
|
|
}
|
|
|
|
/*
|
|
* COleObject::SetREOSELECTED (fSelect)
|
|
*
|
|
* @mfunc cmember set REO_SELECTED state
|
|
*/
|
|
void COleObject::SetREOSELECTED(
|
|
BOOL fSelect)
|
|
{
|
|
_pi.dwFlags &= ~REO_SELECTED;
|
|
if(fSelect)
|
|
_pi.dwFlags |= REO_SELECTED;
|
|
}
|
|
|
|
/*
|
|
* COleObject::Close(dwSave)
|
|
*
|
|
* @mfunc closes this object
|
|
*/
|
|
void COleObject::Close(
|
|
DWORD dwSave) //same as IOleObject::Close
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::Close");
|
|
|
|
if(!_punkobj)
|
|
return;
|
|
|
|
IOleObject *poo;
|
|
if(_punkobj->QueryInterface(IID_IOleObject, (void **)&poo) == NOERROR)
|
|
{
|
|
poo->Close(dwSave);
|
|
poo->Release();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* COleObject::ScrollObject(dx, dy, prcScroll)
|
|
*
|
|
* @mfunc updates _rcPos if we were scrolled
|
|
*/
|
|
void COleObject::ScrollObject(
|
|
LONG dx, //@parm Change in the x direction
|
|
LONG dy, //@parm Change in the y direction
|
|
LPCRECT prcScroll) //@parm Rect that is being scrolled
|
|
{
|
|
RECT rcInter;
|
|
|
|
// If we're inplace active, OnReposition will handle the scrolling
|
|
if(!_fInPlaceActive && !_fGuardPosRect &&
|
|
IntersectRect(&rcInter, &_rcPos, prcScroll))
|
|
{
|
|
OffsetRect(&_rcPos, dx, dy);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// PRIVATE methods
|
|
//
|
|
/*
|
|
* COleObject::~COleObject()
|
|
*
|
|
* @mfunc destructor
|
|
*/
|
|
COleObject::~COleObject()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::~COleObject");
|
|
|
|
CleanupState();
|
|
}
|
|
|
|
/*
|
|
* COleObject::SavePrivateState()
|
|
*
|
|
* @mfunc Saves information such as the aspect and various flags
|
|
* into the object's storage.
|
|
*
|
|
* @devnote This method is used mostly for compatibility with
|
|
* richedit 1.0--we save the same information they did.
|
|
*
|
|
* Also note that this method returns void--even if any particular
|
|
* call failes, we should be able to "recover" and limp along.
|
|
* Richedit 1.0 also had this behavior.
|
|
*/
|
|
void COleObject::SavePrivateState()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::SavePrivateState");
|
|
Assert(_pstg);
|
|
|
|
IStream * pstm;
|
|
HRESULT hr = _pstg->CreateStream(szSiteFlagsStm, STGM_READWRITE |
|
|
STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm);
|
|
if(IsZombie())
|
|
return;
|
|
|
|
if(hr == NOERROR)
|
|
{
|
|
pstm->Write(&_pi, sizeof(PersistedInfo), NULL);
|
|
pstm->Release();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* COleObject::FetchObjectExtents()
|
|
*
|
|
* @mfunc determines the object's size in himetric. Typically, this
|
|
* is achieved via IOleObject::GetExtent, but some error
|
|
* recovery is implemented
|
|
*
|
|
* @rdesc void. _sizel is updated
|
|
*/
|
|
void COleObject::FetchObjectExtents()
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
IOleObject *poo;
|
|
IViewObject2 *pvo;
|
|
CDisplay *pdp;
|
|
|
|
if(IsZombie())
|
|
return;
|
|
|
|
// We _don't_ want to make calls to GetExtent if:
|
|
// (1) We have outstanding updates to _sizel for which we
|
|
// haven't successfully called SetExtent
|
|
// (2) This is a PaintBrush object and the most recent call
|
|
// to SetExtent for this PB object failed
|
|
|
|
if(_fAspectChanged || !(_fSetExtent || (_fIsPaintBrush && _fPBUseLocalSizel)))
|
|
{
|
|
// try IOleObject::GetExtent as long as we shouldn't try for
|
|
// the metafile first.
|
|
|
|
// If this flag was set, it has done its job so turn it off.
|
|
_fAspectChanged = FALSE;
|
|
|
|
if(!(_pi.dwFlags & REO_GETMETAFILE))
|
|
{
|
|
hr = _punkobj->QueryInterface(IID_IOleObject, (void **)&poo);
|
|
if(hr == NOERROR)
|
|
{
|
|
hr = poo->GetExtent(_pi.dvaspect, &_sizel);
|
|
poo->Release();
|
|
}
|
|
if(IsZombie())
|
|
return;
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
if(hr != NOERROR)
|
|
{
|
|
if(_punkobj->QueryInterface(IID_IViewObject2, (void **)&pvo) == NOERROR)
|
|
{
|
|
hr = pvo->GetExtent(_pi.dvaspect, -1, NULL, &_sizel);
|
|
pvo->Release();
|
|
}
|
|
}
|
|
|
|
if(IsZombie())
|
|
return;
|
|
|
|
if(hr != NOERROR || _sizel.cx == 0 || _sizel.cy == 0)
|
|
_sizel.cx = _sizel.cy = 2000;
|
|
}
|
|
// If _fSetExtent==TRUE, we've made a change to _sizel for which
|
|
// we haven't called IOleObject::SetExtent successfully. Therefore
|
|
// fall through with existing _sizel.
|
|
|
|
// Update our position rectangle
|
|
pdp = _ped->_pdp;
|
|
|
|
_rcPos.right = _rcPos.left + pdp->HimetricXtoDX(_sizel.cx);
|
|
_rcPos.bottom = _rcPos.top + pdp->HimetricYtoDY(_sizel.cy);
|
|
}
|
|
|
|
/*
|
|
* COleObject::ConnectObject()
|
|
*
|
|
* @mfunc setup the necessary advises to the embedded object.
|
|
*
|
|
* @rdesc HRESULT
|
|
*
|
|
* @comm This code is similar to ole2ui's OleStdSetupAdvises
|
|
*/
|
|
HRESULT COleObject::ConnectObject()
|
|
{
|
|
IViewObject *pvo;
|
|
IOleObject *poo;
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::ConnectObject");
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
Assert(_punkobj);
|
|
|
|
if(_punkobj->QueryInterface(IID_IViewObject, (void **)&pvo) == NOERROR)
|
|
{
|
|
pvo->SetAdvise(_pi.dvaspect, ADVF_PRIMEFIRST, (IAdviseSink *)this);
|
|
pvo->Release();
|
|
}
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
HRESULT hr = _punkobj->QueryInterface(IID_IOleObject, (void **)&poo);
|
|
if(hr == NOERROR)
|
|
{
|
|
hr = poo->Advise((IAdviseSink *)this, &_dwConn);
|
|
|
|
CObjectMgr *pobjmgr = _ped->GetObjectMgr();
|
|
Assert(pobjmgr);
|
|
|
|
// The doc may be NULL, but not the app. Don't do anything
|
|
// if the app name is NULL
|
|
if(pobjmgr->GetAppName())
|
|
{
|
|
hr = poo->SetHostNames(pobjmgr->GetAppName(),
|
|
pobjmgr->GetDocName());
|
|
}
|
|
poo->Release();
|
|
}
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
OleSetContainedObject(_punkobj, TRUE);
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* COleObject::DisconnectObject
|
|
*
|
|
* @mfunc reverses the connections made in ConnectObject and releases
|
|
* the object. Note that the object's storage is _NOT_
|
|
* released.
|
|
*/
|
|
void COleObject::DisconnectObject()
|
|
{
|
|
IOleObject * poo = NULL;
|
|
IViewObject *pvo = NULL;
|
|
|
|
if(IsZombie())
|
|
return; // Already Disconnected.
|
|
|
|
if(_punkobj->QueryInterface(IID_IOleObject, (void **)&poo) == NOERROR)
|
|
{
|
|
poo->SetClientSite(NULL);
|
|
if(_dwConn)
|
|
poo->Unadvise(_dwConn);
|
|
|
|
poo->Release();
|
|
}
|
|
|
|
if(_punkobj->QueryInterface(IID_IViewObject, (void **)&pvo) == NOERROR)
|
|
{
|
|
pvo->SetAdvise(_pi.dvaspect, ADVF_PRIMEFIRST, NULL);
|
|
pvo->Release();
|
|
}
|
|
|
|
CoDisconnectObject(_punkobj, NULL);
|
|
SafeReleaseAndNULL(&_punkobj);
|
|
}
|
|
|
|
/*
|
|
* COleObject::MakeZombie()
|
|
*
|
|
* @mfunc Force this object to enter a zombie state. This
|
|
* is called when we should be gone but aren't. It cleans
|
|
* up our state and flags us so we don't do nasty things
|
|
* between now and the time were are deleted.
|
|
*
|
|
*/
|
|
void COleObject::MakeZombie()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::MakeZombie");
|
|
|
|
CleanupState();
|
|
Zombie();
|
|
}
|
|
|
|
/*
|
|
* COleObject::CleanupState()
|
|
*
|
|
* @mfunc Called on delete and when we become zombied. It cleans
|
|
* up our member data and any other dependencies that need to
|
|
* be resolved.
|
|
*/
|
|
void COleObject::CleanupState()
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::CleanupState");
|
|
|
|
if(_ped && !_fInUndo)
|
|
{
|
|
CNotifyMgr *pnm = _ped->GetNotifyMgr();
|
|
if(pnm)
|
|
pnm->Remove((ITxNotify *)this);
|
|
|
|
_ped = NULL;
|
|
}
|
|
|
|
DisconnectObject();
|
|
|
|
if(_pstg)
|
|
SafeReleaseAndNULL((IUnknown**)&_pstg);
|
|
|
|
if(_hdib)
|
|
{
|
|
::DeleteObject(_hdib);
|
|
_hdib = NULL;
|
|
}
|
|
GlobalFree(_hdata);
|
|
_hdata = NULL;
|
|
if(_pimageinfo)
|
|
{
|
|
delete _pimageinfo;
|
|
_pimageinfo = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* COleObject::ActivateObj (uiMsg, wParam, lParam)
|
|
*
|
|
* @mfunc Activates the object.
|
|
*
|
|
* @rdesc
|
|
* BOOL Whether object has been activated.
|
|
*/
|
|
BOOL COleObject::ActivateObj(
|
|
UINT uiMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
LPOLEOBJECT poo;
|
|
HWND hwnd;
|
|
MSG msg;
|
|
DWORD dwPos;
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::AcitvateObj");
|
|
|
|
if(_ped->TxGetWindow(&hwnd) != NOERROR)
|
|
return FALSE;
|
|
|
|
ResetPosRect();
|
|
|
|
// Fill in message structure
|
|
msg.hwnd = hwnd;
|
|
msg.message = uiMsg;
|
|
msg.wParam = wParam;
|
|
msg.lParam = lParam;
|
|
msg.time = GetMessageTime();
|
|
dwPos = GetMessagePos();
|
|
msg.pt.x = (LONG) LOWORD(dwPos);
|
|
msg.pt.y = (LONG) HIWORD(dwPos);
|
|
|
|
// Force this object to be selected, if it isn't already
|
|
// Update the selection before making the outgoing call
|
|
if(!(_pi.dwFlags & REO_SELECTED))
|
|
{
|
|
CTxtSelection *psel = _ped->GetSel();
|
|
if(psel)
|
|
psel->SetSelection(_cp, _cp + 1);
|
|
}
|
|
|
|
// Execute the primary verb
|
|
if(_punkobj->QueryInterface(IID_IOleObject, (void **)&poo) == NOERROR)
|
|
{
|
|
_fActivateCalled = TRUE;
|
|
|
|
// Make sure we tell the object its size has changed if we haven't
|
|
// already notified it.
|
|
if(_fSetExtent)
|
|
SetExtent(SE_ACTIVATING);
|
|
|
|
HRESULT hr;
|
|
hr = poo->DoVerb(OLEIVERB_PRIMARY, &msg, (LPOLECLIENTSITE)this, 0, hwnd, &_rcPos);
|
|
|
|
#ifndef MACPORT
|
|
if(FAILED(hr))
|
|
{
|
|
ENOLEOPFAILED enoleopfailed;
|
|
|
|
enoleopfailed.iob = _ped->_pobjmgr->FindIndexForCp(GetCp());
|
|
enoleopfailed.lOper = OLEOP_DOVERB;
|
|
enoleopfailed.hr = hr;
|
|
_ped->TxNotify(EN_OLEOPFAILED, &enoleopfailed);
|
|
}
|
|
#endif
|
|
poo->Release();
|
|
|
|
if(_fInPlaceActive && !(_pi.dwFlags & REO_INPLACEACTIVE))
|
|
{
|
|
CObjectMgr *pobjmgr = _ped->GetObjectMgr();
|
|
Assert(!pobjmgr->GetInPlaceActiveObject());
|
|
pobjmgr->SetInPlaceActiveObject(this);
|
|
_pi.dwFlags |= REO_INPLACEACTIVE;
|
|
}
|
|
_fActivateCalled = FALSE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* COleObject::DeActivateObj
|
|
*
|
|
* @mfunc Deactivates the object.
|
|
*
|
|
*/
|
|
HRESULT COleObject::DeActivateObj(void)
|
|
{
|
|
IOleInPlaceObject * pipo;
|
|
IOleObject *poo;
|
|
MSG msg;
|
|
HRESULT hr = NOERROR;
|
|
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEINTERN, "COleObject::DeActivateObj");
|
|
|
|
if (_fDeactivateCalled)
|
|
{
|
|
// There are multiple paths through the deactive code. The assumption
|
|
// with this logic is that it is more disconcerting for an app to
|
|
// get multiple deactivate calls than the opposite. This might
|
|
// prove to be an incorrect assumption. This go put in because I
|
|
// added a DeActivateObj call in DeActivateObj where there wasn't
|
|
// one before and I was afraid that this could cause problems because
|
|
// apps might get a call that they didn't have before. It is important
|
|
// to note that the opposite might be the case. (a-rsail).
|
|
return NOERROR;
|
|
}
|
|
|
|
_fDeactivateCalled = TRUE;
|
|
|
|
ResetPosRect();
|
|
|
|
if(_punkobj->QueryInterface(IID_IOleInPlaceObject, (void **)&pipo)
|
|
== NOERROR)
|
|
{
|
|
hr =_punkobj->QueryInterface(IID_IOleObject, (void **)&poo);
|
|
if(hr == NOERROR)
|
|
{
|
|
// This code is a bit different from 1.0, but seems to
|
|
// make things work a bit better. Basically, we've taken a leaf
|
|
// from various sample apps and do the most brute force "de-activate"
|
|
// possible (you'd think just one call would be enough ;-)
|
|
|
|
// Don't bother with the error return here.
|
|
pipo->UIDeactivate();
|
|
|
|
// Fake something
|
|
ZeroMemory(&msg, sizeof(MSG));
|
|
msg.message = WM_LBUTTONDOWN;
|
|
_ped->TxGetWindow(&msg.hwnd);
|
|
|
|
// Again, don't bother checking for errors; we need to
|
|
// plow through and get rid of stuff as much as possible.
|
|
poo->DoVerb(OLEIVERB_HIDE, &msg, (IOleClientSite *)this,
|
|
-1, msg.hwnd, &_rcPos);
|
|
|
|
// COMPATIBILITY ISSUE (alexgo): the RE1.0 code did some funny
|
|
// stuff with undo here, but I don't think it's necessary now
|
|
// with our multi-level undo model.
|
|
hr = pipo->InPlaceDeactivate();
|
|
poo->Release();
|
|
}
|
|
pipo->Release();
|
|
}
|
|
|
|
_fDeactivateCalled = FALSE;
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* COleObject::Convert (rclsidNew, lpstrUserTypeNew)
|
|
*
|
|
* @mfunc Converts the object to the specified class. Does reload
|
|
* the object but does NOT force an update (caller must do this).
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
HRESULT COleObject::Convert(
|
|
REFCLSID rclsidNew, //@parm Destination clsid
|
|
LPCSTR lpstrUserTypeNew) //@parm New user type name
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::Convert");
|
|
|
|
CLIPFORMAT cfOld;
|
|
CLSID clsidOld;
|
|
LPOLESTR szUserTypeOld = NULL;
|
|
HRESULT hr;
|
|
HRESULT hrLatest;
|
|
UsesMakeOLESTR;
|
|
|
|
|
|
// If object has no storage, return
|
|
if(!_pstg)
|
|
return ResultFromScode(E_INVALIDARG);
|
|
|
|
// Read the old class, format, and user type in
|
|
if ((hr = ReadClassStg(_pstg, &clsidOld)) ||
|
|
(hr = ReadFmtUserTypeStg(_pstg, &cfOld, &szUserTypeOld)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Unload object
|
|
Close(OLECLOSE_SAVEIFDIRTY);
|
|
_punkobj->Release();
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Write new class and user type, but old format, into storage
|
|
if ((hr = WriteClassStg(_pstg, rclsidNew)) ||
|
|
(hr = WriteFmtUserTypeStg(_pstg, cfOld,
|
|
(LPOLESTR) MakeOLESTR(lpstrUserTypeNew))) ||
|
|
(hr = SetConvertStg(_pstg, TRUE)) ||
|
|
((hr = _pstg->Commit(0)) && (hr = _pstg->Commit(STGC_OVERWRITE))))
|
|
{
|
|
// Uh oh, we're in a bad state; rewrite the original info
|
|
(VOID) WriteClassStg(_pstg, clsidOld);
|
|
(VOID) WriteFmtUserTypeStg(_pstg, cfOld, szUserTypeOld);
|
|
}
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Reload the object and connect. If we can't reload it, delete it.
|
|
hrLatest = OleLoad(_pstg, IID_IOleObject, (LPOLECLIENTSITE) this,
|
|
(void **)&_punkobj);
|
|
|
|
if(hrLatest != NOERROR)
|
|
{
|
|
CRchTxtPtr rtp(_ped, _cp);
|
|
|
|
// we don't want the delete of this object to go on the undo
|
|
// stack. We use a space so that cp's will work out right for
|
|
// other undo actions.
|
|
rtp.ReplaceRange(1, 1, L" ", NULL, -1);
|
|
}
|
|
else
|
|
ConnectObject();
|
|
|
|
// Free the old
|
|
CoTaskMemFree(szUserTypeOld);
|
|
return hr ? hr : hrLatest;
|
|
}
|
|
|
|
/*
|
|
* COleObject::ActivateAs (rclsid, rclsidAs)
|
|
*
|
|
* @mfunc Handles a request by the user to activate all objects of a particular
|
|
* class as objects of another class.
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
HRESULT COleObject::ActivateAs(
|
|
REFCLSID rclsid,
|
|
REFCLSID rclsidAs)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::ActivateAs");
|
|
|
|
IOleObject * poo = NULL;
|
|
CLSID clsid;
|
|
|
|
// Get clsid of object
|
|
HRESULT hr = _punkobj->QueryInterface(IID_IOleObject, (void **)&poo);
|
|
if(hr == NOERROR)
|
|
{
|
|
// NOTE: We are depending on the behavior of GetUserClassID to
|
|
// return the current clsid of the object (not the TreatAs id).
|
|
// This should hold true as long as long as we haven't reloaded
|
|
// it yet. If there are problems with ActivateAs in the future,
|
|
// this might be a suspect.
|
|
hr = poo->GetUserClassID(&clsid);
|
|
poo->Release();
|
|
}
|
|
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Check to see if object clsid matches clsid to be treated as something
|
|
// else. If it is we need to unload and reload the object.
|
|
if(IsEqualCLSID(clsid, rclsid))
|
|
{
|
|
// Unload object
|
|
Close(OLECLOSE_SAVEIFDIRTY);
|
|
_punkobj->Release();
|
|
|
|
if(IsZombie())
|
|
return CO_E_RELEASED;
|
|
|
|
// Reload object and connect. If we can't reload it, delete it.
|
|
hr = OleLoad(_pstg, IID_IOleObject, (LPOLECLIENTSITE) this,
|
|
(void **)&_punkobj);
|
|
|
|
if(hr != NOERROR)
|
|
{
|
|
CRchTxtPtr rtp(_ped, _cp);
|
|
|
|
// We don't want the delete of this object to go on the undo
|
|
// stack. We use a space so that cp's will work out right for
|
|
// other undo actions.
|
|
rtp.ReplaceRange(1, 1, L" ", NULL, -1);
|
|
}
|
|
else
|
|
ConnectObject();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* COleObject::SetLinkAvailable(fAvailable)
|
|
*
|
|
* @mfunc
|
|
* Allows client to tell us whether the link is available or not.
|
|
*
|
|
* @rdesc
|
|
* HRESULT Success code.
|
|
*/
|
|
HRESULT COleObject::SetLinkAvailable(
|
|
BOOL fAvailable) //@parm if TRUE, make object linkable
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::SetLinkAvailable");
|
|
|
|
// If this is not a link, return
|
|
if(!(_pi.dwFlags & REO_LINK))
|
|
return E_INVALIDARG;
|
|
|
|
// Set flag as appropriate
|
|
_pi.dwFlags &= ~REO_LINKAVAILABLE;
|
|
if(fAvailable)
|
|
_pi.dwFlags |= REO_LINKAVAILABLE;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COleObject::WriteTextInfoToEditStream(pes)
|
|
*
|
|
* @mfunc
|
|
* Used for textize support, Tries to determine the text
|
|
* representation for an object and then writes that info
|
|
* to the given stream. The only thing this is particularly useful
|
|
* for is to support richedit1.0's TEXTIZED data format.
|
|
*
|
|
* @rdesc
|
|
* LONG Number of chras written..
|
|
*/
|
|
LONG COleObject::WriteTextInfoToEditStream(
|
|
EDITSTREAM *pes)
|
|
{
|
|
LONG cch;
|
|
LONG cbWritten = 0;
|
|
IOleObject *poo;
|
|
IDataObject *pdataobj = NULL;
|
|
STGMEDIUM med;
|
|
char *pch; //we only deal with ANSI data here
|
|
|
|
HANDLE hGlobal;
|
|
|
|
HRESULT hr = _punkobj->QueryInterface(IID_IOleObject, (void **)&poo);
|
|
if(hr == NOERROR)
|
|
{
|
|
hr = poo->GetClipboardData(0, &pdataobj);
|
|
poo->Release();
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
hr = _punkobj->QueryInterface(IID_IDataObject, (void **)&pdataobj);
|
|
if(FAILED(hr))
|
|
{
|
|
pes->dwError = (DWORD) E_FAIL;
|
|
goto Default;
|
|
}
|
|
}
|
|
|
|
med.tymed = TYMED_HGLOBAL;
|
|
med.pUnkForRelease = NULL;
|
|
med.hGlobal = NULL;
|
|
|
|
hr = pdataobj->GetData(&g_rgFETC[iAnsiFETC], &med);
|
|
if(FAILED(hr))
|
|
pes->dwError = (DWORD)hr;
|
|
else
|
|
{
|
|
hGlobal = med.hGlobal;
|
|
pch = (char *)GlobalLock(hGlobal);
|
|
if(pch)
|
|
{
|
|
for (cch = 0; pch[cch]; cch++);
|
|
pes->dwError = pes->pfnCallback(pes->dwCookie, (BYTE *)pch, cch,
|
|
&cbWritten);
|
|
GlobalUnlock(hGlobal);
|
|
}
|
|
ReleaseStgMedium(&med);
|
|
}
|
|
|
|
Default:
|
|
if(cbWritten <= 0)
|
|
{
|
|
char ch = ' ';
|
|
|
|
pes->pfnCallback(pes->dwCookie, (BYTE *)&ch, sizeof(char), &cbWritten);
|
|
pes->dwError = 0;
|
|
}
|
|
|
|
pdataobj->Release();
|
|
return cbWritten;
|
|
}
|
|
|
|
/*
|
|
* COleObject::SetDvaspect (dvaspect)
|
|
*
|
|
* @mfunc Allows client to tell us which aspect to use and force us
|
|
* to recompute positioning and redraw.
|
|
*/
|
|
void COleObject::SetDvaspect(
|
|
DWORD dvaspect) //@parm the aspect to use
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::SetDvaspect");
|
|
|
|
_pi.dvaspect = dvaspect;
|
|
|
|
// Force FetchObjectExtents to call through
|
|
_fAspectChanged = TRUE;
|
|
|
|
// Cause ourselves to redraw and update
|
|
OnViewChange(dvaspect, (DWORD) -1);
|
|
}
|
|
|
|
/*
|
|
* COleObject::HandsOffStorage
|
|
*
|
|
* @mfunc See IPersistStore::HandsOffStorage.
|
|
*
|
|
*/
|
|
void COleObject::HandsOffStorage(void)
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::HandsOffStorage");
|
|
|
|
// Free storage we currently have, if we have one.
|
|
SafeReleaseAndNULL((IUnknown**)&_pstg);
|
|
}
|
|
|
|
/*
|
|
* COleObject::SaveCompleted
|
|
*
|
|
* @mfunc See IPersistStore::SaveCompleted.
|
|
*/
|
|
void COleObject::SaveCompleted(
|
|
LPSTORAGE lpstg) //@parm new storage
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSOLE, TRCSCOPEEXTERN, "COleObject::SaveCompleted");
|
|
|
|
// Did our caller give us a new storage to remember?
|
|
if(lpstg)
|
|
{
|
|
// Free storage we currently have, if we have one
|
|
if(_pstg)
|
|
SafeReleaseAndNULL((IUnknown**)&_pstg);
|
|
|
|
// Remember storage we are given, since we are given one
|
|
lpstg->AddRef();
|
|
_pstg = lpstg;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SetAllowedResizeDirections
|
|
*
|
|
* @func Resizing helper function
|
|
*
|
|
*/
|
|
static void SetAllowedResizeDirections(
|
|
const POINT & pt,
|
|
const RECT & rc,
|
|
LPTSTR lphand,
|
|
BOOL & fTop,
|
|
BOOL & fBottom,
|
|
BOOL & fLeft,
|
|
BOOL & fRight)
|
|
{
|
|
fTop = abs(pt.y - rc.top) < abs(pt.y - rc.bottom);
|
|
fBottom = !fTop;
|
|
fLeft = abs(pt.x - rc.left) < abs(pt.x - rc.right);
|
|
fRight = !fLeft;
|
|
|
|
if(lphand == IDC_SIZENS)
|
|
fLeft = fRight = FALSE;
|
|
|
|
else if(lphand == IDC_SIZEWE)
|
|
fTop = fBottom = FALSE;
|
|
}
|
|
|
|
/*
|
|
* SetRestriction.
|
|
*
|
|
* @func Resizing helper function determines bounding rectangle for resizing.
|
|
*/
|
|
static void SetRestriction(
|
|
RECT & rc,
|
|
HWND hwnd,
|
|
DWORD dwScroll)
|
|
{
|
|
GetClientRect(hwnd, &rc);
|
|
InflateRect(&rc, -1, -1); // So rectangle is visible
|
|
|
|
// Allow objects to grow larger than the window in the
|
|
// directions which have scrollbars
|
|
if(dwScroll & WS_HSCROLL)
|
|
rc.right = MAXLONG;
|
|
|
|
if(dwScroll & WS_VSCROLL)
|
|
rc.bottom = MAXLONG;
|
|
}
|
|
|
|
/*
|
|
* Restrict (&pt, &rc)
|
|
*
|
|
* @func Resizing helper function bounds a point within a rectangle
|
|
*/
|
|
static void Restrict(
|
|
POINT &pt,
|
|
RECT &rc)
|
|
{
|
|
if(pt.x < rc.left)
|
|
pt.x = rc.left;
|
|
else if(pt.x > rc.right)
|
|
pt.x = rc.right;
|
|
|
|
if(pt.y < rc.top)
|
|
pt.y = rc.top;
|
|
else if(pt.y > rc.bottom)
|
|
pt.y = rc.bottom;
|
|
}
|
|
|
|
/*
|
|
* COleObject::HandleResize (&pt)
|
|
*
|
|
* @mfunc Deal with object resizing.
|
|
*
|
|
* @rdesc BOOL
|
|
*/
|
|
BOOL COleObject::HandleResize(
|
|
const POINT &pt)
|
|
{
|
|
LPTSTR lphand;
|
|
DWORD dwFlags = _pi.dwFlags;
|
|
HWND hwnd;
|
|
RECT rcOld;
|
|
RECT rcRestrict;
|
|
BOOL fTop, fBottom, fLeft, fRight;
|
|
BOOL fEscape;
|
|
CDisplay *pdp = _ped->_pdp;
|
|
|
|
if(!(dwFlags & REO_SELECTED) ||
|
|
!(dwFlags & REO_RESIZABLE) ||
|
|
(lphand = CheckForHandleHit(pt)) == NULL || !pdp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
HDC hdc = pdp->GetDC();
|
|
rcOld = _rcPos; // Save old size
|
|
_ped->TxGetWindow(&hwnd);
|
|
SetCapture(hwnd);
|
|
|
|
SetRestriction(rcRestrict, hwnd, _ped->TxGetScrollBars());
|
|
|
|
SetAllowedResizeDirections(pt, _rcPos, lphand,
|
|
fTop, fBottom, fLeft, fRight);
|
|
|
|
// Erase and redraw frame without handles.
|
|
DrawFrame(pdp, hdc, &_rcPos);
|
|
_pi.dwFlags = REO_NULL;
|
|
DrawFrame(pdp, hdc, &_rcPos);
|
|
|
|
fEscape = FALSE;
|
|
const INT vkey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
|
|
while (GetAsyncKeyState(vkey) & 0x8000)
|
|
{
|
|
POINT ptLast = pt;
|
|
POINT ptCur;
|
|
MSG msg;
|
|
|
|
// Stop if the ESC key has been pressed
|
|
if(GetAsyncKeyState(VK_ESCAPE) & 0x0001)
|
|
{
|
|
fEscape = TRUE;
|
|
break;
|
|
}
|
|
|
|
GetCursorPos(&ptCur);
|
|
ScreenToClient(hwnd, &ptCur);
|
|
|
|
// GetCursorPos() isn't supported on WinCE. We have it hacked to
|
|
// be GetMessagePos() which unfortunately in this case will cause
|
|
// ptCur to never change. By removing this check we end up drawing
|
|
// multiple times when the user pauses during a resize.
|
|
#ifndef UNDER_CE
|
|
// If mouse hasn't moved, try again
|
|
if((ptCur.x == ptLast.x) && (ptCur.y == ptLast.y))
|
|
continue;
|
|
#endif
|
|
|
|
ptLast = ptCur;
|
|
Restrict(ptCur, rcRestrict);
|
|
|
|
// Erase old rectangle, update rectangle, and redraw
|
|
DrawFrame(pdp, hdc, &_rcPos);
|
|
if(fLeft) _rcPos.left = ptCur.x;
|
|
if(fRight) _rcPos.right = ptCur.x;
|
|
if(fTop) _rcPos.top = ptCur.y;
|
|
if(fBottom) _rcPos.bottom = ptCur.y;
|
|
// Keep a minimun width and height
|
|
INT xWidthSys = pdp->GetXWidthSys();
|
|
INT yHeightSys = pdp->GetYHeightSys();
|
|
if(_rcPos.right - _rcPos.left < xWidthSys)
|
|
{
|
|
if(fLeft) _rcPos.left = _rcPos.right - xWidthSys;
|
|
if(fRight) _rcPos.right = _rcPos.left + xWidthSys;
|
|
}
|
|
if(_rcPos.bottom - _rcPos.top < yHeightSys)
|
|
{
|
|
if(fTop) _rcPos.top = _rcPos.bottom - yHeightSys;
|
|
if(fBottom) _rcPos.bottom = _rcPos.top + yHeightSys;
|
|
}
|
|
|
|
DrawFrame(pdp, hdc, &_rcPos);
|
|
// FUTURE: (joseogl): It would be cool if we could do something
|
|
// bettter here, but for now, it appears to be necessary.
|
|
Sleep(100);
|
|
|
|
// Eat input messages
|
|
if (PeekMessage(&msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE) ||
|
|
PeekMessage(&msg, 0, WM_MOUSEFIRST,
|
|
WM_MOUSELAST, PM_REMOVE | PM_NOYIELD) ||
|
|
PeekMessage(&msg, 0, WM_NCMOUSEFIRST,
|
|
WM_NCMOUSELAST, PM_REMOVE | PM_NOYIELD))
|
|
{
|
|
// Break out of the loop if the Escape key was pressed
|
|
if(msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)
|
|
{
|
|
fEscape = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DrawFrame(pdp, hdc, &_rcPos);
|
|
ReleaseCapture();
|
|
RECT rcNew = _rcPos;
|
|
_rcPos = rcOld;
|
|
_pi.dwFlags = dwFlags;
|
|
|
|
// If user aborted, then we skip the resizing stuff
|
|
if(fEscape)
|
|
DrawFrame(pdp, hdc, &_rcPos);
|
|
else
|
|
{
|
|
EnableGuardPosRect();
|
|
Resize(rcNew);
|
|
DrawFrame(pdp, hdc, &_rcPos);
|
|
DisableGuardPosRect();
|
|
}
|
|
pdp->ReleaseDC(hdc);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* COleObject::Resize(rcNew)
|
|
*
|
|
* @mfunc Set new object size. Handle undo details.
|
|
*/
|
|
void COleObject::Resize(
|
|
const RECT &rcNew)
|
|
{
|
|
CDisplay * pdp = _ped->_pdp;
|
|
SIZEL sizelold = _sizel;
|
|
|
|
// Change the size of our internal representation.
|
|
_sizel.cx = pdp->DXtoHimetricX(rcNew.right - rcNew.left);
|
|
_sizel.cy = pdp->DYtoHimetricY(rcNew.bottom - rcNew.top);
|
|
|
|
//If size didn't really change, don't do anything else.
|
|
if(_sizel.cx != sizelold.cx || _sizel.cy != sizelold.cy)
|
|
{
|
|
if(_ped->_fUseUndo)
|
|
{
|
|
CGenUndoBuilder undobldr(_ped, UB_AUTOCOMMIT);
|
|
IAntiEvent *pae;
|
|
|
|
pae = gAEDispenser.CreateResizeObjectAE(_ped, this, _rcPos);
|
|
if(pae)
|
|
undobldr.AddAntiEvent(pae);
|
|
}
|
|
_rcPos.bottom = _rcPos.top + pdp->HimetricYtoDY(_sizel.cy);
|
|
_rcPos.right = _rcPos.left + pdp->HimetricXtoDX(_sizel.cx);
|
|
|
|
SetExtent(SE_NOTACTIVATING);
|
|
|
|
// Force a redraw that will stretch the object.
|
|
pdp->OnPostReplaceRange(CP_INFINITE, 0, 0, _cp, _cp + 1);
|
|
|
|
_ped->GetCallMgr()->SetChangeEvent(CN_GENERIC);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* COleObject::OnReposition (dx, dy)
|
|
*
|
|
* @mfunc Set object's new position. May have changed as a result of scrolling.
|
|
*/
|
|
void COleObject::OnReposition(
|
|
LONG dx,
|
|
LONG dy)
|
|
{
|
|
IOleInPlaceObject *pipo;
|
|
RECT rcClip;
|
|
|
|
if(!_fInPlaceActive)
|
|
{
|
|
// If we're not inplace active, don't do anything
|
|
return;
|
|
}
|
|
|
|
_ped->_pdp->GetViewRect(rcClip);
|
|
_rcPos.left += dx;
|
|
_rcPos.right += dx;
|
|
_rcPos.top += dy;
|
|
_rcPos.bottom += dy;
|
|
|
|
if(_punkobj->QueryInterface(IID_IOleInPlaceObject, (void **)&pipo)
|
|
== NOERROR)
|
|
{
|
|
pipo->SetObjectRects(&_rcPos, &rcClip);
|
|
pipo->Release();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* COleObject::ResetPosRect(pAdjust)
|
|
*
|
|
* @mfunc Recompute the object's position rectangle from its cp.
|
|
*/
|
|
void COleObject::ResetPosRect(
|
|
LONG *pAdjust) //@parm output adjustment needed for positioning below baseline
|
|
{
|
|
CRchTxtPtr rtp(_ped, 0);
|
|
POINT pt, pt1;
|
|
LONG yHeight = _ped->_pdp->HimetricYtoDY(_sizel.cy);
|
|
|
|
rtp.SetCp(_cp);
|
|
if(_ped->_pdp->PointFromTp(rtp, NULL, FALSE, pt, NULL, TA_TOP) == -1)
|
|
return;
|
|
_rcPos.top = pt.y;
|
|
_ped->_pdp->PointFromTp(rtp, NULL, FALSE, pt1, NULL,
|
|
(_pi.dwFlags & REO_BELOWBASELINE) ? TA_BOTTOM : TA_BASELINE);
|
|
|
|
if(pAdjust)
|
|
*pAdjust = 0;
|
|
|
|
if(pt1.y - pt.y > yHeight)
|
|
{
|
|
// If line is bigger than object move object down.
|
|
_rcPos.top += pt1.y - pt.y - yHeight;
|
|
if(pAdjust)
|
|
*pAdjust = pt1.y - pt.y - yHeight;
|
|
}
|
|
|
|
_rcPos.bottom = _rcPos.top + yHeight;
|
|
_rcPos.left = pt.x;
|
|
_rcPos.right = _rcPos.left + _ped->_pdp->HimetricXtoDX(_sizel.cx);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void COleObject::DbgDump(DWORD id){
|
|
Tracef(TRCSEVNONE, "Object #%d %X: cp = %d , rect = (%d, %d, %d, %d)",id,this,_cp,_rcPos.top,_rcPos.left,_rcPos.bottom,_rcPos.right);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* COleObject:SetExtent(iActivating)
|
|
*
|
|
* @mfunc A wrapper around IOleObject::SetExtent which makes some additional
|
|
* checks if the first call to IOleObject::SetExtent fails.
|
|
*
|
|
* @rdesc HRESULT
|
|
*/
|
|
HRESULT COleObject::SetExtent(
|
|
int iActivating) //@parm indicates if object is currently being activated
|
|
{
|
|
LPOLEOBJECT poo;
|
|
|
|
// If we are connected to a link object, the native extent can't be change,
|
|
// so don't bother doing anything here.
|
|
if(_pi.dwFlags & REO_LINK)
|
|
{
|
|
// So we don't call GetExtents on remeasuring.
|
|
_fSetExtent = TRUE;
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT hr = _punkobj->QueryInterface(IID_IOleObject, (void **)&poo);
|
|
if(hr != NOERROR)
|
|
return hr;
|
|
|
|
// If we are about to activate the object, fall through and OleRun the
|
|
// object prior to attempting to SetExtent. Otherwise, attempt a SetExtent
|
|
// directly.
|
|
if(iActivating == SE_NOTACTIVATING)
|
|
{
|
|
// By default, we will call SetExtent when the object is next activated.
|
|
_fSetExtent = TRUE;
|
|
|
|
hr = poo->SetExtent(_pi.dvaspect, &_sizel);
|
|
|
|
DWORD dwStatus;
|
|
|
|
// If the server is not running we need to to some additional
|
|
// checking. If it was, we do not need to call SetExtent again.
|
|
|
|
// Find out if OLEMISC_RECOMPOSEONRESIZE is set. If it is, we should
|
|
// run the object and call setextent. If not, we defer calling set
|
|
// extent until we are ready to activate the object.
|
|
if(!(hr == OLE_E_NOTRUNNING &&
|
|
poo->GetMiscStatus(_pi.dvaspect, &dwStatus) == NOERROR &&
|
|
(dwStatus & OLEMISC_RECOMPOSEONRESIZE)))
|
|
{
|
|
goto DontRunAndSetAgain;
|
|
}
|
|
// Fall through and attempt the SetExtent again after running the object
|
|
}
|
|
|
|
{
|
|
SIZEL sizelsave = _sizel;
|
|
OleRun(_punkobj); // This call causes _sizel to be reset
|
|
// via OLE and FetchObjectExtents.
|
|
_sizel = sizelsave;
|
|
}
|
|
poo->SetExtent(_pi.dvaspect, &_sizel);
|
|
|
|
DontRunAndSetAgain:
|
|
if((hr == NOERROR) ||
|
|
(iActivating == SE_NOTACTIVATING && hr != OLE_E_NOTRUNNING))
|
|
{
|
|
_fSetExtent = FALSE;
|
|
}
|
|
// If the server is still not running, we try again at
|
|
// activation time. Otherwise the server has either
|
|
// done its thing or it doesn't do resize. Either way
|
|
// we don't bother trying again at activation time.
|
|
|
|
if(hr == NOERROR && _fIsPaintBrush)
|
|
{
|
|
SIZEL sizelChk;
|
|
|
|
poo->GetExtent(_pi.dvaspect, &sizelChk);
|
|
_fPBUseLocalSizel = !(sizelChk.cx == _sizel.cx &&
|
|
sizelChk.cy == _sizel.cy);
|
|
// HACK: Calls to SetExtent on PaintBrush objects may not
|
|
// actually change the object extents. In such cases,
|
|
// we will rely on local _sizel for PaintBrush object extents.
|
|
}
|
|
poo->Release();
|
|
return hr;
|
|
}
|