/*
 *	@doc	INTERNAL
 *
 *	@module	COLEOBJ.CPP	OLE Object management class implemenation |
 *
 * 	Authors:
 *	alexgo  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.
 *
 *	KeithCu	Cleaned up (removed _rcPos, ResetPosRect(), ScrollObject,
			fixed undo/resizing issues)
 *			Added textflow support
 *
 *	Copyright (c) 1995-2000, 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 } };

//
//	Ink object
//
const GUID CLSID_Ink = 
	{ 0x13DE4A42, 0x8D21, 0x4C8E, {0xBF, 0x9C, 0x8F, 0x69, 0xCB, 0x06, 0x8F, 0xCA} };
const IID IID_ILineInfo = 
	{ 0x9C1C5AD5, 0xF22F, 0x4DE4, {0xB4, 0x53, 0xA2, 0xCC, 0x48, 0x2E, 0x7C, 0x33} };

#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)
	{
		RECTUV rc;
		GetRectuv(rc);

		// Invalidate rect that we're in.
		_ped->TxInvalidateRect(&rc);

		// 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();
	}
	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;

	CObjectMgr *pobjmgr = _ped->GetObjectMgr();
	if(!pobjmgr)
		return E_OUTOFMEMORY;
	
	// If the mode changes
	if(pobjmgr->GetHelpMode() != fEnterMode)
	{
		pobjmgr->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();
	if(!pobjmgr)
		return E_OUTOFMEMORY;

	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)
	{
		RECTUV rcuv;
		GetRectuv(rcuv);
		_ped->_pdp->RectFromRectuv(*prcPos, rcuv);

		// 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 are lame and draw outside the
	// areas they are supposed to.  So we need to 
	// just invalidate everything and redraw.

	_ped->TxInvalidate();
	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 _size
		SetExtent(SE_NOTACTIVATING);
	}

	// Some objects are lame and 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->TxInvalidate();
	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;

	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, NULL);

	_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 where ReplaceRange starts ("cpMin")
	LONG		cchDel,			//@parm Count of chars after cp that are deleted
	LONG		cchNew,			//@parm Count of chars inserted after cp
	LONG		cpFormatMin,	//@parm cpMin  for a formatting change
	LONG		cpFormatMax,	//@parm cpMost for a formatting change
	NOTIFY_DATA *pNotifyData)	//@parm special data to indicate 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 where ReplaceRange starts ("cpMin")
	LONG		cchDel,			//@parm Count of chars after cp that are deleted
	LONG		cchNew,			//@parm Count of chars inserted after cp
	LONG		cpFormatMin,	//@parm cpMin  for a formatting change
	LONG		cpFormatMax,	//@parm cpMost for a formatting change
	NOTIFY_DATA *pNotifyData)	//@parm special data to indicate changes
{
	// The only case we need to worry about is when changes
	// come before our object

	Assert(_fInUndo == FALSE);
#ifndef NOINKOBJECT
	LONG	cpOld = _cp;
#endif
	_fDraw = TRUE;
	if(cp <= _cp)
	{		
		if(cp + cchDel > _cp)
		{
			_fDraw = FALSE;
			return;
		}
		_cp += (cchNew - cchDel);
	}
#ifndef NOINKOBJECT
	if (_ped && _fIsInkObject && _pILineInfo)
	{
		if (cpFormatMin <= cpOld && cpOld < cpFormatMax)
		{
			// Inform Ink object for the formatting changes
			UINT	iInkWidth = 0;

			_ped->SetInkProps(_cp, _pILineInfo, &iInkWidth);

		}
	}
#endif
}
		
/*
 *	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;
	memcpy(&preobj->sizel, &_size, sizeof(SIZE));

   	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;
	
	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;

	preobj->sizel.cx = max(preobj->sizel.cx, 0);
	preobj->sizel.cy = max(preobj->sizel.cy, 0);

	memcpy(&_size, &preobj->sizel, sizeof(SIZE));	// 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, _fPBUseLocalSize, which
		// indicates that for this PB object, SetExtent calls are not 
		// acknowledged by the object, and we are to use our local value
		// of _size as the object extents.
		FetchObjectExtents();
		SetExtent(SE_NOTACTIVATING);
	}
#ifndef NOINKOBJECT
	else if(IsEqualCLSID(preobj->clsid, CLSID_Ink))
	{
		ILineInfo	*pILineInfo;

		_fIsInkObject = TRUE;

		_pi.dwFlags &= ~REO_RESIZABLE;		// No resizing for ink objects
		_pi.dwFlags |= REO_BELOWBASELINE | REO_INVERTEDSELECT;

		if(_punkobj->QueryInterface(IID_ILineInfo, (void **)&pILineInfo) == NOERROR)
		{
			UINT	iInkWidth = 0;

			_pILineInfo = pILineInfo;
			
			// Inform Ink object for the formatting changes
			_ped->SetInkProps(_cp, _pILineInfo, &iInkWidth);
		}
	}
#endif

	hr = ConnectObject();

    if(IsZombie())
        return CO_E_RELEASED;
        
	if(preobj->sizel.cx || preobj->sizel.cy)
		memcpy(&_size, &preobj->sizel, sizeof(SIZE));
	else
		FetchObjectExtents();

    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, dup, dvpAscent, dvpDescent, dvpDescentFont, tflow)
 *
 * 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	dvpInch,		//@parm	resolution of device
	long	dupInch,
	LONG &	dup,			//@parm Object width
	LONG &	dvpAscent,		//@parm Object ascent
	LONG &  dvpDescent,		//@parm Object descent
	SHORT	dvpDescentFont,	//@parm object's font descent
	TFLOW	tflow)			//@parm textflow
{
	LONG dvp;

	//If we are rotated and the OLE object doesn't understand rotation, then
	//we will measure and draw the object unrotated
	if (IsUVerticalTflow(tflow) && !(_pi.dwFlags & REO_CANROTATE))
	{
		dup = W32->HimetricToDevice(_size.dv, dupInch);
		dvp = W32->HimetricToDevice(_size.du, dvpInch);
	}
	else
	{
		dup = W32->HimetricToDevice(_size.du, dupInch);
		dvp = W32->HimetricToDevice(_size.dv, dvpInch);
	}

	if (_pi.dwFlags & REO_BELOWBASELINE)
	{
		dvpDescent = dvpDescentFont;
		dvpAscent = max(0, dvp - dvpDescent);
	}
	else //The normal case
	{
		dvpAscent = dvp;
		dvpDescent = 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		up,		//@parm Upper left corner x coordinate of handle box
	int		vp,		//@parm Upper left corner y coordinate of handle box
	const POINTUV &pt)//@parm Point to check
{
    RECTUV    rc;
    
    rc.left = up;
    rc.top = vp;

	// Add one to bottom right because PtInRect does not consider
	// points on bottom or right to be in rect.
    rc.right = rc.left + dxyHandle + 1;
    rc.bottom = rc.top + 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 POINTUV &pt,	//@parm POINT containing client coord. of the cursor.
	BOOL fLogical)		//@parm TRUE if you want the logical as opposed to visual cursor
{
	// If object is not resizeable, no chance of hitting a resize handle!
	if(!(_pi.dwFlags & REO_RESIZABLE) || FWrapTextAround())
		return NULL;

	RECTUV rc;
	GetRectuv(rc);

	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;

	WCHAR *idcur = 0;

	// 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))
	{
		idcur = IDC_SIZENWSE;
	}
	else 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))
	{
		idcur = IDC_SIZEWE;
	}
	else if(InHandle(rc.left, rc.bottom-dxyHandle, pt) ||
	   InHandle(rc.right-dxyHandle, rc.top, pt))
	{
		idcur = IDC_SIZENESW;
	}
	else 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))
	{
		idcur = IDC_SIZENS;
	}

	//Convert logical cursor to visual cursor
	if (!fLogical && IsUVerticalTflow(_ped->_pdp->GetTflow()))
	{
		if (idcur == IDC_SIZENS)
			idcur = IDC_SIZEWE;
		else if (idcur == IDC_SIZEWE)
			idcur = IDC_SIZENS;
		else if (idcur == IDC_SIZENESW)
			idcur = IDC_SIZENWSE;
		else if (idcur == IDC_SIZENWSE)
			idcur = IDC_SIZENESW;
	}

	return idcur;
}

/* 
 * 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, &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
		int ropOld = SetROP2(hdc, R2_NOT);
		HBRUSH hbrOld = (HBRUSH) SelectObject(hdc, GetStockObject(NULL_BRUSH));
		Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
		SelectObject(hdc, hbrOld);
		SetROP2(hdc, ropOld);
	}

	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 UNDER_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);
	if (!hdcMem)
		return;

	LPBITMAPINFO	pbmi = (LPBITMAPINFO) GlobalLock(_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.
 *	REVIEW (keithcu) How expensive is SaveDC/RestoreDC? We could
 *  have a flag when we know that the object isn't going to totally screw
 *	around with our DC and then not go through that expense.
 *
 */
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)
	const RECTUV *prcClip,	//@parm Clipping rectangle
	BOOL		fMetafile,	//@parm Whether the HDC is a metafile
	POINTUV	   *ppt,		//@parm Top left corner of where to draw
	LONG		dvpBaselineLine,
	LONG		dvpDescentMaxCur,
	TFLOW		tflow)
{
	BOOL			 fMultipleSelect = FALSE;
	CObjectMgr *	 pobjmgr = _ped->GetObjectMgr();
	IViewObject *	 pvo;
	CDisplayPrinter *pdpPrint;
	RECT			 rc, rcClip;
	RECTUV			 rcuv;
	LONG			 cpMin, cpMost;
	_ped->GetSelRangeForRender(&cpMin, &cpMost);
	BOOL			 fSelected = _cp >= cpMin && _cp < cpMost && !FWrapTextAround();

	SaveDC(hdc);	 //Anything in this function below could change things.

	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;
	}

	// Draw object where we are asked within rendering rectangle
	rcuv.left = ppt->u;
	rcuv.bottom = ppt->v + dvpBaselineLine;

	if (IsUVerticalTflow(tflow) && !(_pi.dwFlags & REO_CANROTATE))
	{
		rcuv.right = rcuv.left + W32->HimetricToDevice(_size.dv, dypInch);
		rcuv.top = rcuv.bottom - W32->HimetricToDevice(_size.du, dxpInch);
	}
	else
	{
		rcuv.right = rcuv.left + W32->HimetricToDevice(_size.du, dxpInch);
		rcuv.top = rcuv.bottom - W32->HimetricToDevice(_size.dv, dypInch);
	}

	if (_pi.dwFlags & REO_BELOWBASELINE)
		OffsetRect((RECT*)&rcuv, 0, dvpDescentMaxCur);

	pdp->RectFromRectuv(rc, rcuv);
	pdp->RectFromRectuv(rcClip, *prcClip);

	if (fSelected)
		SetBkMode(hdc, OPAQUE);

	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, rcClip.left, rcClip.top, rcClip.right, rcClip.bottom);

	if(_fIsEBookImage)
	{
		POINT pt = {rc.left, rc.top};
		WinLPtoDP(hdc, &pt, 1);
		if(_ped->fInHost2())
    		(_ped->GetHost())->TxEBookImageDraw(_EBookImageID, hdc, &pt, NULL/*&rc*/,
                                        (pobjmgr->GetSingleSelect()==this));
	}
	else 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;

			//REVIEW (keithcu) Has this ever been tested? GetPrintPage
			//is in twips while IViewObject::Draw is in metafile units!
			RECT 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() && !FWrapTextAround())
	{
		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;

	//REVIEW (keithcu) Too heavy handed?
 //	if (FWrapTextAround())
//		_ped->_pdp->InvalidateRecalc();

	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();
	}
}

				
//
//	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 fails, 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.  _size is updated
 */
void COleObject::FetchObjectExtents()
{
	HRESULT hr = NOERROR;
	IOleObject *poo;
	IViewObject2 *pvo;

    if(IsZombie())
        return;
        
	// We _don't_ want to make calls to GetExtent if:
	// (1) We have outstanding updates to _size 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(_fIsEBookImage)
	{
		_size.du = _ped->_pdp->DUtoHimetricU(_sizeEBookImage.cx);
		_size.dv = _ped->_pdp->DVtoHimetricV(_sizeEBookImage.cy);
	}
	else
	if(_fAspectChanged || !(_fSetExtent || (_fIsPaintBrush && _fPBUseLocalSize)))
	{	
		// 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, (SIZE*)&_size);
				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, (SIZE*)&_size);
				pvo->Release();
			}
		}

	    if(IsZombie())
	        return;
        
		if(hr != NOERROR || _size.du == 0 || _size.dv == 0)
			_size.du = _size.dv = 2000;
	}
}

/*
 *	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);		
		if(!pobjmgr)
		{
			poo->Release();
			return E_OUTOFMEMORY;
		}

		// 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();
	}

#ifndef NOINKOBJECT
	SafeReleaseAndNULL((IUnknown**)&_pILineInfo);
#endif

	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");
	
	CDocInfo *pDocInfo = NULL;
	if(_ped)
	{
		pDocInfo = _ped->GetDocInfoNC();
		if(!_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;
	}

	if(!pDocInfo || _hdata != pDocInfo->_hdata)
		GlobalFree(_hdata);	// Don't free _hdata if background is using it

	_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;

	// 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;
		RECTUV rc;
		RECT rcPos;

		GetRectuv(rc);
		_ped->_pdp->RectFromRectuv(rcPos, rc);
		hr = poo->DoVerb(OLEIVERB_PRIMARY, &msg, (LPOLECLIENTSITE)this, 0, hwnd, &rcPos);

		if(FAILED(hr))
		{
			ENOLEOPFAILED	enoleopfailed;

			enoleopfailed.iob = _ped->_pobjmgr->FindIndexForCp(GetCp());
			enoleopfailed.lOper = OLEOP_DOVERB;
			enoleopfailed.hr = hr;
	        _ped->TxNotify(EN_OLEOPFAILED, &enoleopfailed);
		}
	    poo->Release();
		if(_fInPlaceActive && !(_pi.dwFlags & REO_INPLACEACTIVE))
		{
			CObjectMgr *pobjmgr = _ped->GetObjectMgr();
			if(!pobjmgr)
			{
				_fActivateCalled = FALSE;
				return FALSE;
			}

			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;

	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);

			RECT	rcPos;
			RECTUV rcuv;
			GetRectuv(rcuv);
			_ped->_pdp->RectFromRectuv(rcPos, rcuv);

			// 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, CodePage)
 *
 *	@mfunc
 *		Used for textize support,  Tries to determine the text representation
 *		for an object and then writes that info	to the given stream. If
 *		CodePage = 1200 (little-endian Unicode), the object is queried for
 *		Unicode text; else it's queried for Ansi text.
 *
 *	@rdesc
 *		LONG	count of chars written
 */
LONG COleObject::WriteTextInfoToEditStream(
	EDITSTREAM *pes,		//@parm Edit stream to write to
	UINT		CodePage)	//@parm CodePage: 1200 uses Unicode; else Ansi
{
	LONG		 cbWritten = 0;
	BOOL		 fUnicode = CodePage == 1200;
	STGMEDIUM	 med;
	IDataObject *pdataobj = NULL;
	IOleObject * poo;

	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[fUnicode ? iUnicodeFETC : iAnsiFETC], &med);
	if(FAILED(hr))
		pes->dwError = (DWORD)hr;
	else
	{
		HANDLE	hGlobal = med.hGlobal;
		LONG	cb = 0;
		BYTE *	pb = (BYTE *)GlobalLock(hGlobal);
		if(pb)
		{
			if(fUnicode)
				for(; (WCHAR)pb[cb]; cb += 2);
			else
				for(; pb[cb]; cb++);

			pes->dwError = pes->pfnCallback(pes->dwCookie, pb, cb, &cbWritten);
			GlobalUnlock(hGlobal);
		}
		ReleaseStgMedium(&med);
	}

Default:
	if(cbWritten <= 0)
	{
		BYTE rgb[] = {' ', 0};

		pes->pfnCallback(pes->dwCookie, rgb, 1 + fUnicode, &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 POINTUV  & pt,
	const RECTUV   & rc,
	      LPTSTR   lphand,
	      BOOL   & fTop,
	      BOOL   & fBottom,
	      BOOL   & fLeft,
	      BOOL   & fRight)
{
   	fTop = abs(pt.v - rc.top) < abs(pt.v - rc.bottom);
	fBottom = !fTop;
	fLeft = abs(pt.u - rc.left) < abs(pt.u - rc.right);
	fRight = !fLeft;

	if(lphand == IDC_SIZENS)
		fLeft = fRight = FALSE; 
	else if(lphand == IDC_SIZEWE)
		fTop = fBottom = FALSE;
}

/*
 *	COleObject::HandleResize (&ptxy)
 *	
 *	@mfunc Deal with object resizing.
 *
 *	@rdesc	BOOL
 */
BOOL COleObject::HandleResize(
	const POINT &ptxy)
{
	LPTSTR lphand;
	DWORD  dwFlags = _pi.dwFlags;
 	HWND   hwnd;
	RECT   rcxyPos;
	RECTUV rcPos;
	BOOL   fTop, fBottom, fLeft, fRight, fEscape;
	POINTUV	pt;
	CDisplay *pdp = _ped->_pdp;
	int dupMin = pdp->GetDupSystemFont();
	int dvpMin = pdp->GetDvpSystemFont();

	pdp->PointuvFromPoint(pt, ptxy);

	if(!(dwFlags & REO_SELECTED) || !(dwFlags & REO_RESIZABLE) ||
		(lphand = CheckForHandleHit(pt, TRUE)) == NULL)
		return FALSE;
 	
	GetRectuv(rcPos);
	pdp->RectFromRectuv(rcxyPos, rcPos);

	HDC hdc = pdp->GetDC();
	_ped->TxGetWindow(&hwnd);
	SetCapture(hwnd);
	
	SetAllowedResizeDirections(pt, rcPos, lphand, fTop, fBottom, fLeft, fRight);
	
	// Erase and redraw frame without handles.
	DrawFrame(pdp, hdc, &rcxyPos);
	_pi.dwFlags = REO_NULL;
	DrawFrame(pdp, hdc, &rcxyPos);

	fEscape = FALSE;
	const INT vkey = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
	while (GetAsyncKeyState(vkey) & 0x8000)
	{		
		POINT ptLast = ptxy;
		POINT ptxyCur;
		POINTUV ptCur;
		MSG msg;

		// Stop if the ESC key has been pressed
		if(GetAsyncKeyState(VK_ESCAPE) & 0x0001)
		{
			fEscape = TRUE;
			break;
		}
		
		GetCursorPos(&ptxyCur);
		ScreenToClient(hwnd, &ptxyCur);
		pdp->PointuvFromPoint(ptCur, ptxyCur);

#ifndef UNDER_CE
// 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. 
		// If mouse hasn't moved, try again
		if((ptxyCur.x == ptLast.x) && (ptxyCur.y == ptLast.y))
			continue;
#endif

		ptLast = ptxyCur;

		// Erase old rectangle, update rectangle, and redraw
		DrawFrame(pdp, hdc, &rcxyPos);	

		if(fLeft)   rcPos.left   = min(ptCur.u, rcPos.right - dupMin);
		if(fRight)  rcPos.right  = max(ptCur.u, rcPos.left + dupMin);
		if(fTop)    rcPos.top    = min(ptCur.v, rcPos.bottom - dvpMin);
		if(fBottom) rcPos.bottom = max(ptCur.v, rcPos.top + dvpMin);

		pdp->RectFromRectuv(rcxyPos, rcPos);
		DrawFrame(pdp, hdc, &rcxyPos);

		// FUTURE: (joseogl): It would be cool if we could do something
		// better 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_NCMOUSEMOVE, WM_NCMBUTTONDBLCLK, 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, &rcxyPos);
  	ReleaseCapture();
 	_pi.dwFlags = dwFlags;

	// Resize unless the user aborted
	if(!fEscape)
	{
		SIZEUV size;
		if (IsUVerticalTflow(pdp->GetTflow()) && !(_pi.dwFlags & REO_CANROTATE))
		{
			size.du = rcPos.bottom - rcPos.top;
			size.dv = rcPos.right - rcPos.left;
		}
		else
		{
			size.du = rcPos.right - rcPos.left;
			size.dv = rcPos.bottom - rcPos.top;
		}

		size.du = pdp->DUtoHimetricU(size.du);
		size.dv = pdp->DVtoHimetricV(size.dv);
		Resize(size, TRUE);
	}

	pdp->ReleaseDC(hdc);
	return TRUE;
}

/*
 *	COleObject::Resize(size)
 *	
 *	@mfunc Set new object size.  Handle undo details.
 */
void COleObject::Resize(
	const SIZEUV &size, 
	BOOL  fCreateAntiEvent)
{
	CDisplay *	pdp = _ped->_pdp;
	SIZEUV		sizeold = _size;

	// Change the size of our internal representation.
	_size = size;

	//If size didn't really change, don't do anything else.
	if(size.du != sizeold.du || size.dv != sizeold.dv)
	{
		if(_ped->_fUseUndo && fCreateAntiEvent)
		{
			CGenUndoBuilder undobldr(_ped, UB_AUTOCOMMIT);
			IAntiEvent *pae;

			pae = gAEDispenser.CreateResizeObjectAE(_ped, this, sizeold);
			if(pae)
				undobldr.AddAntiEvent(pae);
		}

		SetExtent(SE_NOTACTIVATING);

		// Force a redraw that will stretch the object.
		pdp->OnPostReplaceRange(CP_INFINITE, 0, 0, _cp, _cp + 1, NULL);

		_ped->GetCallMgr()->SetChangeEvent(CN_GENERIC);
	}
}

/*
 *	COleObject::OnReposition ()
 *	
 *	@mfunc Set object's new position.  May have changed as a result of scrolling.
 */
void COleObject::OnReposition()
{
	IOleInPlaceObject *pipo;

	if(!_fInPlaceActive)
	{
		// If we're not inplace active, don't do anything
		return;
	}

	if(_punkobj->QueryInterface(IID_IOleInPlaceObject, (void **)&pipo) == NOERROR)
	{
		RECTUV rcuvClip, rcuv;
		RECT rcClip, rc;

		_ped->_pdp->GetViewRect(rcuvClip);
		_ped->_pdp->RectFromRectuv(rcClip, rcuvClip);

		GetRectuv(rcuv);
		_ped->_pdp->RectFromRectuv(rc, rcuv);

		pipo->SetObjectRects(&rc, &rcClip);
        pipo->Release();
	}
}

 /*
 *	COleObject::GetRectuv(rc)
 *	
 *	@mfunc Compute the object's position rectangle from its cp.
 */
void COleObject::GetRectuv(
	 RECTUV &rc)
{
	CRchTxtPtr rtp(_ped, _cp);
	POINTUV pt;
	CDispDim dispdim;

	DWORD grf = TA_BASELINE;
	if (_pi.dwFlags & REO_BELOWBASELINE)
		grf = TA_BOTTOM;
	
	if(_ped->_pdp->PointFromTp(rtp, NULL, FALSE, pt, NULL, grf, &dispdim) == -1)
		return;

	rc.left = pt.u;
	rc.right = rc.left + dispdim.dup;
	rc.bottom = pt.v;

	LONG dv = _size.dv;
	if (IsUVerticalTflow(_ped->_pdp->GetTflow()) && !(_pi.dwFlags & REO_CANROTATE))
		dv = _size.du;

	rc.top = rc.bottom - _ped->_pdp->HimetricVtoDV(dv);
}

#ifdef DEBUG
void COleObject::DbgDump(DWORD id)
{
	Tracef(TRCSEVNONE, "Object #%d %X: cp = %d , ",id,this,_cp);
}
#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, (SIZE*)&_size);

		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
	}

	SIZEUV sizesave;
	sizesave = _size;
	OleRun(_punkobj);		// This call causes _size to be reset 
							// via OLE and FetchObjectExtents.
	_size = sizesave;
	poo->SetExtent(_pi.dvaspect, (SIZE*)&_size);

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)
	{
		SIZEUV sizeChk;

		poo->GetExtent(_pi.dvaspect, (SIZE*)&sizeChk);
		_fPBUseLocalSize = !(sizeChk.du == _size.du && sizeChk.dv == _size.dv);
		// HACK:  Calls to SetExtent on PaintBrush objects may not
		// 	actually change the object extents.  In such cases, 
		//	we will rely on local _size for PaintBrush object extents.
	}
	poo->Release();
	return hr;
}
