//
//
//**************************************************************************
//
// BOlePart.cpp -- Implements the Bolero half of the OLE2 client site.
//                 BOlePart objects impersonate the server object (IPart)
//                 from the point of view of the Bolero customer who's
//                 writing a client site (ISite)
//
// Copyright (c) 1993,94 by Borland International, Inc. All rights reserved
//
//**************************************************************************

#include "BOleSvc.h"
#include "BOleDoc.h"
#include "BOlePart.h"
#include "BOleData.h"
#include "BOleCMan.h"


//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//

// MulDiv uses int; won't work with HIMETRIC values
//
inline int Scale(double src, long mult, long div)
{
        src *= mult;
        src += (div/2); //
        src /= div;
        return (int) src;
}

const OLECHAR szcBOlePart[] = TEXT("\3BOlePart");

// Since we don't know why Excel returns RPC_E_CALL_REJECTED the
// first N times and then finally works, we have this kludge.
//
HRESULT ForceOleRun(IOleObject *pOO)
{
        int timeout = 500;
        while (timeout-- > 0) {
                if (ResultFromScode(RPC_E_CALL_REJECTED) != OleRun(pOO))
                        break;
        }
        return (timeout == 0) ? ResultFromScode(RPC_E_CALL_REJECTED) : NOERROR;
}


HRESULT _IFUNC BOlePart::QueryInterfaceMain(REFIID iid, LPVOID FAR *ppv)
{
        HRESULT hr = ResultFromScode(E_NOINTERFACE);
        *ppv = NULL;

        // Self
        //
        if (iid == IID_BOlePart) {
                (BOlePart*) *ppv = this;
                AddRef ();
                return NOERROR;
        }

        // interfaces

           SUCCEEDED(hr = IOleInPlaceSite_QueryInterface(this, iid, ppv))
        || SUCCEEDED(hr = IOleClientSite_QueryInterface(this, iid, ppv))
        || SUCCEEDED(hr = IAdviseSink_QueryInterface(this, iid, ppv))
 //       || SUCCEEDED(hr = IBPart_QueryInterface(this, iid, ppv))
        || SUCCEEDED(hr = IBPart2_QueryInterface(this, iid, ppv))
        || SUCCEEDED(hr = IBLinkable_QueryInterface(this, iid, ppv))
#ifdef PART_DATA
        || SUCCEEDED(hr = IPersist_QueryInterface(this, iid, ppv))
        || SUCCEEDED(hr = IDataObject_QueryInterface(this, iid, ppv))
        || SUCCEEDED(hr = IEnumFORMATETC_QueryInterface(this, iid, ppv))
#endif
        || SUCCEEDED(hr = IDropSource_QueryInterface(this, iid, ppv))
        || (IsLink && SUCCEEDED(hr = IBLinkInfo_QueryInterface(this, iid, ppv)))

        // base classes

        || SUCCEEDED(hr = BOleComponent::QueryInterfaceMain(iid, ppv))

        ;

        return hr;
};

//**************************************************************************
//
// BOlePart implementation -- functions which aren't part of any inherited
//                            interface, but are just part of the private
//                            implementation
//
//**************************************************************************

BOlePart::BOlePart (BOleClassManager *pF, IBUnknownMain * pO, BOleDocument * pOD) :
        BOleComponent(pF, pO),
        pDoc (pOD),             pOleObject(NULLP),   pViewObject(NULLP),
        pInPlaceObject(NULLP),  pSite (NULLP),       pVerbEnumerator(NULLP),
        verbIndex(0),           dwDrawAspect (0),    IsOpen(FALSE),
        HasMoniker(FALSE),      IsOpenInPlace(FALSE),IsOpenInsideOut(FALSE),
        IsLink (FALSE),         pNextPart(NULLP),    pPrevPart (NULLP),
        pLinkObject (NULLP),    dwExtAspect(0),      pStgFromInit(NULLP),
        pDataObject (NULLP),    pEnumFE (NULLP),     MonikerAssigned(FALSE),
        pILinkCont (0),         pszInstName (NULLP), pShortName (NULL),
        pLongName (NULL),       pAppName (NULL),     fLinkSrcAvail(TRUE),
        pDocument (NULL)
{
        if (pDoc)
                pDoc->AddRefMain();

        oleVerb.lpszVerbName = NULLP;
        mfpIcon = NULL;
        CachedExtent.cx = CachedExtent.cy = 0;        //
        scaleSite.xN = 1L;
        scaleSite.xD = 1L;
        scaleSite.yN = 1L;
        scaleSite.yD = 1L;
        OLEPRINTF5("Initializing scaleSite:\t {%ld, %ld, %ld, %ld}",
                scaleSite.xN, scaleSite.xD, scaleSite.yN, scaleSite.yD);
}


BOlePart::~BOlePart()
{
        Close();  // just to be sure

        if (pszInstName) {
                delete [] pszInstName;
                pszInstName = NULLP;
        }

        if (pDocument) {        // set in BOleContainer::GetObject
                CoLockObjectExternal(pDocument, FALSE, FALSE);
                pDocument = NULL;
        }
        DeleteTypeNames();
}

void BOlePart::DeleteTypeNames ()
{
    // Delete any type names which were allocated in IOleObject::GetUserType
        //
        if (pLongName || pShortName || pAppName) {
                LPMALLOC pMalloc = NULL;
                HRESULT hr = ::CoGetMalloc (MEMCTX_TASK, &pMalloc);
                if (SUCCEEDED(hr)) {
                        if (pShortName) {
                                if (pMalloc->DidAlloc(pShortName))
                                pMalloc->Free (pShortName);
                                pShortName = NULLP;
                        }
                        if (pAppName) {
                                if (pMalloc->DidAlloc(pAppName))
                                pMalloc->Free (pAppName);
                                pAppName = NULLP;
                        }
                        if (pLongName) {
                                if (pMalloc->DidAlloc(pLongName))
                                pMalloc->Free (pLongName);
                                pLongName = NULLP;
                        }
                        pMalloc->Release ();
                }
        }
}

// Close -- Let go of all our connections to the server object
//
HRESULT _IFUNC BOlePart::Close()
{
        // We should let go of all server resources to allow the server to quit.
        //
        HRESULT hRes = NOERROR;

        RemoveFromList ();

        // Ask the server to close its document and exit its program
        if (pOleObject) {
                hRes = pOleObject->Close(OLECLOSE_SAVEIFDIRTY);
                IsOpen = FALSE;
        }

        if (pOleObject) {
                pOleObject->Release();
                pOleObject = NULL;
        }
        if (pViewObject) {
                pViewObject->Release();
                pViewObject = NULL;
        }
        if (pDataObject) {
                pDataObject->Release();
                pDataObject = NULL;
        }
        if (pEnumFE) {
                //
                pEnumFE = NULL;
        }
        if (pDoc) {
                pDoc->ReleaseMain();        // dec count on helper
                pDoc = NULL;
        }
        if (pLinkObject) {
                pLinkObject->Release();
                pLinkObject = NULL;
        }
        if (pStgFromInit) {
                pStgFromInit->Release ();
                pStgFromInit = NULL;
        }

        // Required for AVI media stuff to release properly.
        // And DLL servers
        //
        CoFreeUnusedLibraries();

        return hRes;
}

BOOL _IFUNC BOlePart::InitAdvises(LPOLEOBJECT lpOleObject,
                                  DWORD dwDrawAspect,
                                  LPOLESTR lpszContainerApp,
                                  LPOLESTR lpszContainerObj,
                                  LPADVISESINK lpAdviseSink,
                                  BOOL fCreate)
{
        LPVIEWOBJECT lpViewObject;
        HRESULT hrErr;
        BOOL fStatus = TRUE;
#if defined( SPECIAL_CONTAINER )
        DWORD dwTemp;
#endif

        hrErr = lpOleObject->QueryInterface (IID_IViewObject,
                (LPVOID FAR*)&lpViewObject);

        // Setup View advise
        //
        if (hrErr == NOERROR) {

                lpViewObject->SetAdvise(dwDrawAspect, (fCreate ? ADVF_PRIMEFIRST : 0),
                        lpAdviseSink);

                lpViewObject->Release();
        } else
                fStatus = FALSE;

#if defined( SPECIAL_CONTAINER )
        /* Setup OLE advise.
        **    OLE2NOTE: normally containers do NOT need to setup an OLE
        **    advise. this advise connection is only useful for the OLE's
        **    DefHandler and the OleLink object implementation. some
        **    special container's might need to setup this advise for
        **    programatic reasons.
        **
        **    NOTE: this advise will be torn down automatically by the
        **    server when we release the object, therefore we do not need
        **    to store the connection id.
        */
        hrErr = lpOleObject->Advise(lpAdviseSink, (DWORD FAR*)&dwTemp);
        if (hrErr != NOERROR)
                fStatus = FALSE;
#endif

        // Setup the host names for the OLE object.
        //
        hrErr = lpOleObject->SetHostNames(lpszContainerApp,lpszContainerObj);
        if (hrErr != NOERROR)
                fStatus = FALSE;

        // Inform the loadded object's handler/inproc-server that it is in
        //    its embedding container's process.
        //
        OleSetContainedObject(lpOleObject, TRUE);

        return fStatus;
}

#ifdef __BORLANDC__ //PHP OpenStream block is optimized incorrectly
#pragma option -Od
#endif

HRESULT BOlePart::CreateFromBOleInitInfo(BOleInitInfo *pBI, BOOL &fShowAfterCreate)
{
        HRESULT hRes = ResultFromScode(E_INVALIDARG);
        // another result code which can fail without causing Init to fail
        HRESULT hrOkToFail;

        BOOL fIcon = pBI->hIcon != NULL;
        DWORD dwRenderOpt = fIcon ? OLERENDER_NONE : OLERENDER_DRAW;

        dwDrawAspect = fIcon ? DVASPECT_ICON : DVASPECT_CONTENT;

        switch (pBI->Where) {
                case BOLE_STORAGE: {

                        // Load the OLE object's display aspect out of the stream we
                        // dumped it in. This is all we need to do in order to make
                        // drawing aspects persistent
                        //
                        LPSTREAM pStream;
                        if (SUCCEEDED (pStgFromInit->OpenStream (szcBOlePart, NULL,
                                STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream)))  {
                                ULONG cb;
                                pStream->Read (&dwDrawAspect, sizeof(dwDrawAspect), &cb);
                                if (dwDrawAspect == DVASPECT_ICON) {
                                        fIcon = TRUE;
                                        dwRenderOpt = OLERENDER_NONE;
                                }
                                if (!fIcon) {
                                        pStream->Read (&scaleSite, sizeof(scaleSite), &cb);
                                        OLEPRINTF5("Reading scaleSite:\t {%ld, %ld, %ld, %ld}",
                                                scaleSite.xN, scaleSite.xD, scaleSite.yN, scaleSite.yD);
                                        SIZE size;
                                        size.cx = scaleSite.xD;     // server's extent's in Denom
                                        size.cy = scaleSite.yD;

                                        if (!(size.cx == 1 && size.cy == 1))
                                                // if scale factor exists,
                                                // force the server to restore the extent from last time.

                                                // because Word doesn't put height back to what
                                                // it was before. (they only load the width??)
                                                SetPartSize(&size);
                                }
                                pStream->Release ();
                        }
                        // Load OLE object from container's file
                        //
                        hRes = OLE::OleLoad (pStgFromInit, IID_IOleObject, this, &(LPVOID)pOleObject);

                        // Don't activate OLE objects when loading container's file. If there
                        // are a bunch of objects, or if the server is a big app, performance
                        // would be severely degraded.
                        //
                        fShowAfterCreate = FALSE;
                        break;
                }

                case BOLE_NEW:

                        hRes = OLE::OleCreate (*(LPIID)pBI->whereNew.cid, IID_IOleObject, dwRenderOpt,
                                NULL, this, pStgFromInit, &(LPVOID) pOleObject);

                        // Find out whether the object is inside out.
                        //
                        if (hRes == NOERROR) {
                                if (dwDrawAspect == DVASPECT_CONTENT) {
                                        DWORD status = 0;

                                        // Cl2test is passing 0 and ignoring the
                                        // error from Shapes. If I use the real aspect, outline returns
                                        // that it's an inside-out object. Crazy.
                                        //
                                        hrOkToFail = pOleObject->GetMiscStatus (dwDrawAspect, &status);
                                        if (hrOkToFail != NOERROR)
                                                IsOpenInsideOut = FALSE;
                                        else
                                                IsOpenInsideOut = (status & (OLEMISC_INSIDEOUT|OLEMISC_ACTIVATEWHENVISIBLE)) ?
                                                        TRUE : FALSE;
                                }
                        }
                        break;

                case BOLE_FILE:

                        if (pBI->How == BOLE_EMBED) {
                                hRes = OLE::OleCreateFromFile(CLSID_NULL, pBI->whereFile.pPath, IID_IOleObject,
                                dwRenderOpt, NULL, this, pStgFromInit, &(VOID FAR*)pOleObject);

                                fShowAfterCreate = FALSE;
                        }
                        else {
                                hRes = OLE::OleCreateLinkToFile (pBI->whereFile.pPath, IID_IOleObject,
                                        dwRenderOpt, NULL, this, pStgFromInit, &(VOID FAR*)pOleObject);

                                // User should double-click to open link source. Showing the
                                // object here would bring up the server out-of-place, which
                                // would obscure the link we just created.
                                //
                                fShowAfterCreate = FALSE;
                        }
                        break;

                case BOLE_DATAOBJECT:

                        if (pBI->How == BOLE_EMBED) {
                                hRes = OLE::OleCreateFromData (pBI->whereData.pData, IID_IOleObject,
                                        OLERENDER_DRAW, NULL, this, pStgFromInit, &(VOID FAR*)pOleObject);

                                // This is the result of a drop or a paste special. The UI
                                // convention is not to inplace activate the server
                                //
                                fShowAfterCreate = FALSE;
                        }
                        else if (pBI->How == BOLE_LINK) {

                                hRes = OLE::OleCreateLinkFromData (pBI->whereData.pData, IID_IOleObject,
                                        OLERENDER_DRAW, NULL, this, pStgFromInit, &(VOID FAR*)pOleObject);

                                // User should double-click to open link source. Showing the
                                // object here would bring up the server out-of-place, which
                                // would obscure the link we just created.
                                //
                                fShowAfterCreate = FALSE;

                        }
                        else {
                                hRes = OleCreateStaticFromData (pBI->whereData.pData, IID_IOleObject,
                                        OLERENDER_DRAW, NULL, this, pStgFromInit, &(LPVOID) pOleObject);
                                fShowAfterCreate = FALSE;
                        }

//
//
//

                        // Get the drawing aspect for the object.
                        //
                        STGMEDIUM stgmed;
                        dwDrawAspect = fIcon ? DVASPECT_ICON : DVASPECT_CONTENT;
                        HGLOBAL memhandle = pDoc->pService->GetDataFromDataObject (
                                pBI->whereData.pData, BOleDocument::oleObjectDescFmt, NULL,
                                DVASPECT_CONTENT, &stgmed);
                        if (memhandle) {
                                LPOBJECTDESCRIPTOR objDesc = (LPOBJECTDESCRIPTOR) WIN::GlobalLock (memhandle);
                                if (objDesc->dwDrawAspect == DVASPECT_ICON)
                                        dwDrawAspect = DVASPECT_ICON;
                                WIN::GlobalUnlock (memhandle);
                                OLE::ReleaseStgMedium (&stgmed);
                        }

                        // Find out whether the object is inside out.
                        //
                        if (hRes == NOERROR)
                                if (dwDrawAspect == DVASPECT_CONTENT) {
                                        DWORD status;
                                        pOleObject->GetMiscStatus (dwDrawAspect, &status);
                                        IsOpenInsideOut = (status & (OLEMISC_INSIDEOUT|OLEMISC_ACTIVATEWHENVISIBLE)) ?
                                                TRUE : FALSE;
                                }
                        break;
        }
        if (SUCCEEDED(hRes) && !pOleObject)
                hRes = ResultFromScode(E_NOINTERFACE);

        return hRes;
}

#ifdef __BORLANDC__
#pragma option -O.
#endif

// Init -- Create a new OLE2 server object. Probably the BOlePart helper
//         has just been created, and it's time to initiate OLE2 transactions
//
HRESULT _IFUNC BOlePart::Init(IBSite * pIS,BOleInitInfo *pBI)
{

#ifdef _DEBUG

        // Test to make sure client's QueryInterface is implemented correctly
        //
        IBSite *dbgSite = NULL;
        LPOLECLIENTSITE dbgCSite = NULL;
        if (  (!SUCCEEDED(pObjOuter->QueryInterfaceMain(IID_IBSite, &(LPVOID)dbgSite)))
                || (!SUCCEEDED(pObjOuter->QueryInterfaceMain(IID_IOleClientSite,&(LPVOID)dbgCSite)))) {
                MessageBox (NULL,
                        OLESTR("QueryInterface Aggregation isn\'t working right"),
                        OLESTR("BOlePart::Init"), MB_OK);
        }
        else {
                dbgSite->Release();
                dbgCSite->Release();
                dbgSite = NULL;
                dbgCSite = NULL;
        }

#endif


        pSite = pIS;

        if (pBI->pContainer) {
                BOleDocument *oldDoc = pDoc;
                if (!SUCCEEDED(pBI->pContainer->QueryInterface(IID_BOleDocument, &(LPVOID) pDoc)))
                        return ResultFromScode (E_NOINTERFACE);
                pDoc->Release();        // PREVENT CYCLE

                // Since passing the container in the init info was added later, this
                // makes the reference counting semantic on our BOleDocument pointer
                // the same as is done in BOlePart::BOlePart and BOlePart::Close
                //
                pDoc->AddRefMain();             // AddRef the new doc from the init info
                if (oldDoc)
                        oldDoc->ReleaseMain();  // Release the doc from the constructor

                pContainer = pBI->pContainer;
        }
        else
                //  Only work if the caller is not a container/server
                pContainer = pDoc->GetContainer();

        AddToList ();

        pStgFromInit = pBI->pStorage;
        pStgFromInit->AddRef();

        HRESULT hRes = ResultFromScode (E_INVALIDARG);

        BOOL fShowAfterCreate = TRUE;

        for (int idx=0; idx < 42; idx++) {    // knock 42 times if you want Excel
                if (ResultFromScode(RPC_E_CALL_REJECTED) !=
                        (hRes = CreateFromBOleInitInfo(pBI, fShowAfterCreate)))
                        break;
        }

        if (!SUCCEEDED(hRes))
                return hRes;

        // Do standard initialization on the freshly created object
        // where pOleObject is valid


        // If we have to draw as an icon, make sure the IOleCache in
        // handler for this OLE object has an iconic representation
        //
        // If we're loading from storage, just run the usual advises
        //
        if (dwDrawAspect == DVASPECT_ICON && pBI->Where != BOLE_STORAGE) {
#if 0
                if (pBI->Where == BOLE_FILE)
                        mfpIcon = OleGetIconOfFile ((LPSTR)(pBI->whereFile.pPath), TRUE);
                else {
                        OLECHAR buf[128] = {0,};
                        mfpIcon = OleGetIconOfClass (*(LPIID)pBI->whereNew.cid, buf, TRUE);
                }
#endif
                mfpIcon = pBI->hIcon;
                CacheIconicAspect (mfpIcon);
                OnViewChange(dwDrawAspect, -1);
        }
        else {
                IBApplication * pApplication = pDoc->pApplication;
                InitAdvises (pOleObject, dwDrawAspect,
                        (LPOLESTR)pApplication->GetAppName(),
                        (LPOLESTR)pContainer->GetWindowTitle(), this, (pBI->Where != BOLE_STORAGE));
                //
                //
        }

        if (fShowAfterCreate && hRes == NOERROR)
                hRes = DoVerb(OLEIVERB_SHOW);

        // Cache the view object interface for drawing later on
        //
        pOleObject->QueryInterface (IID_IViewObject, &(VOID FAR*)pViewObject);

        // Cache the data object interface for "embed from embedding"
        //
        pOleObject->QueryInterface (IID_IDataObject, &(VOID FAR*)pDataObject);

        // Cache the "user type name" for enumerating verbs later on
        //
        pOleObject->GetUserType (USERCLASSTYPE_SHORT, &pShortName);

        //      Ask the OLE object if it's a link, and cache the pointer if so
        //
        IsLink = SUCCEEDED(pOleObject->QueryInterface (IID_IOleLink, &(VOID FAR*) pLinkObject));

        return hRes;
}

// Copies CF_EMBEDDEDOBJECT into a BOleData cache so we can do embed from
// embedding with a snapshot of the embedded object. This allows the user to
// change the original embedding and still paste the snapshot later.
//
HRESULT _IFUNC BOlePart::CopyFromOriginal (LPFORMATETC pFE, LPDATAOBJECT FAR*ppCopy)
{
//
//

        *ppCopy = NULL;

        // Get the original object's IDataObject and IPersistStorage interfaces
        // so we can write it into our cache. The server object must support
        // these interfaces.
        //
#if 1
        // We have to make sure that we DO need to know that this guy implements
        // IDataObject, otherwise, just use the other branch
        LPDATAOBJECT pOrig;
        HRESULT hr = pOleObject->QueryInterface (IID_IDataObject, &(LPVOID) pOrig);
        if (!SUCCEEDED(hr))
                return hr;
        LPPERSISTSTORAGE pPersistStg = NULL;
        hr = pOrig->QueryInterface (IID_IPersistStorage, &(LPVOID) pPersistStg);
        // JP: Now no longer use pOrig. Lets release it
        pOrig->Release();
        if (!SUCCEEDED(hr))
                return hr;
#else
        HRESULT hr = pOleObject->QueryInterface( IID_IPersistStorage, &(LPVOID) pPersistStg);
        if (!SUCCEEDED(hr))
                return hr;
#endif
        // Create the data cache object which will hold the snapshots in as many
        // formats as we need
        //
        LPUNKNOWN pObjDataCache = NULL;
        hr = pFactory->ComponentCreate ((LPUNKNOWN FAR*)&pObjDataCache, NULL, cidBOleData);
        if (!SUCCEEDED(hr))
                return hr;
        LPDATAOBJECT pIDataCache = NULL;
        hr = pObjDataCache->QueryInterface (IID_IDataObject, &(LPVOID)pIDataCache);
        pObjDataCache->Release(); //JP
        pObjDataCache = NULL;

        if (!SUCCEEDED(hr))
                return hr;

        // Create a temporary docfile which will hold the data
        //
        STGMEDIUM stm;
        _fmemset (&stm, 0, sizeof(STGMEDIUM));
        stm.tymed = TYMED_ISTORAGE;
        hr = ::StgCreateDocfile (NULL, STGM_TRANSACTED | STGM_READWRITE |
                STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &stm.pstg);
        if (!SUCCEEDED(hr))
                return hr;

        // Save the original OLE server object into our temporary docfile
        //
        if (pPersistStg->IsDirty () == NOERROR) {
                ::OleSave (pPersistStg, stm.pstg, FALSE);
                pPersistStg->SaveCompleted (NULL);
        }
        else
                pStgFromInit->CopyTo (0, NULL, NULL, stm.pstg);
        pPersistStg->Release ();

        FORMATETC fe;
        _fmemset (&fe, 0, sizeof(FORMATETC));
        fe.cfFormat = BOleDocument::oleEmbdObjClipFmt;
        fe.ptd = NULL;
        fe.dwAspect = dwDrawAspect;
        fe.lindex = -1;
        fe.tymed = TYMED_ISTORAGE;

        hr = pIDataCache->SetData (&fe, &stm, TRUE);
        if (!SUCCEEDED(hr))
                return hr; //

        stm.tymed = TYMED_HGLOBAL;
        fe.cfFormat = BOleDocument::oleObjectDescFmt;
        fe.tymed = TYMED_HGLOBAL;
        POINTL unused = {0,0};
        SIZEL unused2 = {0,0};
        stm.hGlobal = OleStdGetObjectDescriptorDataFromOleObject (pOleObject,
                NULL, dwDrawAspect, unused, &unused2);
        hr = pIDataCache->SetData (&fe, &stm, TRUE);
        if (!SUCCEEDED(hr))
                return hr; //

        AddSiteData (pIDataCache);
        AddCachedData( pIDataCache );

        // Setup linking formats for linking to embedding (if possible)
        //
        DWORD dwStat=0L;
        pOleObject->GetMiscStatus (dwDrawAspect, &dwStat);
        if (!(OLEMISC_CANTLINKINSIDE & dwStat)) // (says can't do in registry)
                AddLinkSourceData (pIDataCache);

        *ppCopy = pIDataCache;
        return NOERROR;

}

HRESULT _IFUNC BOlePart::SwitchDisplayAspect(
                LPOLEOBJECT             lpOleObj,
                LPDWORD                 lpdwCurAspect,
                DWORD                   dwNewAspect,
                HGLOBAL                 hMetaPict,
                BOOL                    fDeleteOldAspect,
                BOOL                    fSetupViewAdvise,
                LPADVISESINK            lpAdviseSink,
                BOOL FAR*               lpfMustUpdate,
                BOOL                    fRunIfNecessary
)
{
        LPOLECACHE      lpOleCache = NULL;
        LPVIEWOBJECT    lpViewObj = NULL;
        LPENUMSTATDATA  lpEnumStatData = NULL;
        STATDATA        StatData;
        FORMATETC       FmtEtc;
        STGMEDIUM       Medium;
        DWORD           dwAdvf;
        DWORD           dwNewConnection;
        DWORD           dwOldAspect = *lpdwCurAspect;
        HRESULT         hrErr;

        // This fixes a bug in the sample code: insert a shape as an icon,
        // close sr2test, then convert the icon to content. They fail because
        // the object isn't running. We run it if we need to. The fRunIfNecessary
        // flag may be false if we're in SwitchDisplayAspect from Insert Object.
        // Obviously, then the object doesn't need to be run.
        //
        BOOL fMustClose = FALSE;
        if (fRunIfNecessary && !OLE::OleIsRunning(pOleObject)) {
                OleRun (pOleObject);
                fMustClose = TRUE;
        }

        if (lpfMustUpdate)
                *lpfMustUpdate = FALSE;

        lpOleObj->QueryInterface (IID_IOleCache, &(VOID FAR*) lpOleCache);

        // if IOleCache* is NOT available, do nothing
        //
        if (!lpOleCache)
                return ResultFromScode(E_INVALIDARG);

        // Setup new cache with the new aspect
        FmtEtc.cfFormat = NULL;     // whatever is needed to draw
        FmtEtc.ptd      = NULL;
        FmtEtc.dwAspect = dwNewAspect;
        FmtEtc.lindex   = -1;
        FmtEtc.tymed    = TYMED_NULL;

        // Icons shouldn't receive data advise connections. If they did, the
        // metafile representation of the icon in the OLE cache would be lost.
        //
        if (dwNewAspect == DVASPECT_ICON && hMetaPict)
                dwAdvf = ADVF_NODATA;
        else
                dwAdvf = ADVF_PRIMEFIRST;

        hrErr = lpOleCache->Cache(&FmtEtc, dwAdvf, &dwNewConnection);

        if (!SUCCEEDED(hrErr)) {
                lpOleCache->Release();
                return hrErr;
        }

        *lpdwCurAspect = dwNewAspect;

        // Stuff the icon into the cache
        //
        if (dwNewAspect == DVASPECT_ICON && hMetaPict) {

                FmtEtc.cfFormat = CF_METAFILEPICT;
                FmtEtc.ptd      = NULL;
                FmtEtc.dwAspect = DVASPECT_ICON;
                FmtEtc.lindex   = -1;
                FmtEtc.tymed    = TYMED_MFPICT;

                Medium.tymed            = TYMED_MFPICT;
                Medium.hGlobal          = hMetaPict;
                Medium.pUnkForRelease   = NULL;

                hrErr = lpOleCache->SetData (&FmtEtc, &Medium, FALSE /* fRelease */);
        } else {
                if (lpfMustUpdate)
                        *lpfMustUpdate = TRUE;
        }

        if (fSetupViewAdvise && lpAdviseSink) {
                // re-establish the ViewAdvise connection
                //
                lpOleObj->QueryInterface (IID_IViewObject, &(VOID FAR*) lpViewObj);

                if (lpViewObj) {
                        lpViewObj->SetAdvise(dwNewAspect, 0, lpAdviseSink);
                        lpViewObj->Release();
                }
        }

        // Remove cached presentations for the ole display aspect
        //
        if (fDeleteOldAspect) {
                hrErr = lpOleCache->EnumCache(&lpEnumStatData);

                while(hrErr == NOERROR) {
                        hrErr = lpEnumStatData->Next(1, &StatData, NULL);
                        if (hrErr != NOERROR)
                                break;              // DONE! no more caches.

                        if (StatData.formatetc.dwAspect == dwOldAspect) {

                                // Remove previous cache with old aspect
                                //
                                lpOleCache->Uncache(StatData.dwConnection);
                        }
                }

                if (lpEnumStatData)
                        lpEnumStatData->Release();
        }

        if (lpOleCache)
                lpOleCache->Release();

        if (fMustClose)
                pOleObject->Close (OLECLOSE_SAVEIFDIRTY);

        return NOERROR;
}

BOOL _IFUNC BOlePart::CacheIconicAspect (HGLOBAL mfpIcon)
{
        BOOL fMustUpdate;
        HRESULT hrErr = SwitchDisplayAspect (pOleObject, &dwDrawAspect,
                DVASPECT_ICON, mfpIcon, FALSE, TRUE, this, &fMustUpdate, FALSE);

        //if (hrErr == NOERROR)
                //hrErr = pOleObject->Update ();

        OleUIMetafilePictIconFree(mfpIcon);    // clean up metafile

        return (hrErr == NOERROR);
}

void BOlePart::AddToList ()
{
        // Insert "this" onto the list of part instances in this document.
        //
        // Avoid overhead by inserting the new part at the front of the list
        // rather than searching for a null pNextPart. So the list is in
        // first-in-last-out order
        //
        BOlePart *pOldFirstPart = pDoc->GetFirstPart ();
        pDoc->SetFirstPart (this);
        pNextPart = pOldFirstPart;
        if (pOldFirstPart)
                pOldFirstPart->pPrevPart = this;
}

void BOlePart::RemoveFromList ()
{
        // Remove "this" from the list of part instances in this document.
        //
        // Standard list removal by joining the previous part with the next one
        //
        if (pPrevPart)
                pPrevPart->pNextPart = pNextPart;
        if (pNextPart)
                pNextPart->pPrevPart = pPrevPart;

        if (pDoc)
                pDoc->OnRemovePart (this);

        pNextPart = pPrevPart = NULLP;
}

// ConvertHelper -- Helper function which is used in ConvertUI and
//                  ConvertGuts. The reason to have this function is
//                  because the user type and clsid must be known in
//                  both UI and guts, but it wouldn't be nice to put
//                  them in the BOleConvertInfo struct, which would
//                  otherwise be the best way to get dynamic scope.
//
HRESULT _IFUNC BOlePart::ConvertHelper (LPCLSID pCid,
                                        LPOLESTR FAR* ppType,
                                        WORD FAR* pwFormat)
{
        BOOL fHaveClsid = FALSE;
        BOOL fHaveUserType = FALSE;
        HRESULT hr = NOERROR;

        // (PHP) This isn't really nice, but if I don't save the object,
        // ReadFmtUserTypeStg fails. Maybe it's not so bad since we're
        // going to save it anyway to convert it.
        //
        LPPERSISTSTORAGE pps = NULL;
        pOleObject->QueryInterface (IID_IPersistStorage, &(LPVOID) pps);
        pps->Save (pStgFromInit, TRUE);
        pps->Release();

        hr = ReadClassStg (pStgFromInit, pCid);
        if (SUCCEEDED(hr))
                fHaveClsid = TRUE;

        hr = ReadFmtUserTypeStg (pStgFromInit, pwFormat, ppType);
        if (SUCCEEDED(hr))
                fHaveUserType = TRUE;



        if (!fHaveClsid) {
                hr = pOleObject->GetUserClassID (pCid);
                if (!SUCCEEDED(hr))
                        *pCid = CLSID_NULL;
        }

        if (!fHaveUserType) {
                OLECHAR szUserType[128];
                *pwFormat = 0;
                if (OleStdGetUserTypeOfClass(
                        *pCid, szUserType, sizeof(szUserType), NULL)) {
                        *ppType = OleStdCopyString(szUserType, NULL);
                } else {
                        *ppType = NULL;
                }
        }

        return hr;
}


// Bring up the Convert dialog box to give the user a chance to convert
// or emulate the OLE object associated with this part.
//
// Convert is actually implemented here because most of the data needed
// to do the conversion lives in the BOlePart not the BOleService
//
//
//
HRESULT _IFUNC BOlePart::ConvertUI (PIBApplication pHelpApp, BOOL f, BOleConvertInfo FAR *pInfo)
{
        HRESULT hr = NOERROR;

        _fmemset (pInfo, 0, sizeof(BOleConvertInfo));

        OLEUICONVERT ouc;
        _fmemset (&ouc, 0, sizeof (ouc));

        ouc.cbStruct = sizeof (ouc);
        if (pDoc->pService->ShowHelpButton (BOLE_HELP_CONVERT))
                ouc.dwFlags = CF_SHOWHELPBUTTON;
        ouc.hWndOwner = ::GetActiveWindow();    // pHelpApp->GetWindow();
        ouc.fIsLinkedObject = IsLink;
        ouc.dvAspect = dwDrawAspect;
        ouc.dwIBApplication = (DWORD) pHelpApp;

        if (IsLink) {
                hr = pLinkObject->GetSourceDisplayName (&ouc.lpszDefLabel);
                hr = pOleObject->GetUserClassID (&ouc.clsid);
        }
        else
                ConvertHelper (&ouc.clsid, &ouc.lpszUserType, &ouc.wFormat);


        STGMEDIUM medium;
        if (dwDrawAspect == DVASPECT_ICON)
                ouc.hMetaPict = OleStdGetData (pDataObject, CF_METAFILEPICT, NULL,
                        DVASPECT_ICON, &medium);
        else
                ouc.hMetaPict = NULL;

        pDoc->pService->EnterBOleDialog (TRUE, ouc.hHook, ouc.hTask);
        UINT uRet = OleUIConvert(&ouc);
        pDoc->pService->EnterBOleDialog (FALSE, ouc.hHook, ouc.hTask);

        // If the user clicked OK, look at the dialog settings to see what
        // we should set in the output BOleConvertInfo.
        //
        if (uRet == OLEUI_OK) {

                if (ouc.dwFlags & CF_SELECTCONVERTTO && !IsEqualCLSID (ouc.clsid, ouc.clsidNew)) {
                        (int) pInfo->action |= BOLE_CONVERT_TO_CLSID;
                        pInfo->clsidNew = ouc.clsidNew;
                }

                if (ouc.dwFlags & CF_SELECTACTIVATEAS) {
                        (int) pInfo->action |= BOLE_ACTIVATE_AS_CLSID;
                        pInfo->clsidNew = ouc.clsidNew;
                }

                if (ouc.dvAspect != dwDrawAspect || ouc.fObjectsIconChanged) {
                        if (ouc.dvAspect == DVASPECT_ICON) {
                                (int) pInfo->action |= BOLE_ASPECT_TO_ICON;
                                pInfo->hIcon = ouc.hMetaPict;
                        }
                        else
                                (int) pInfo->action |= BOLE_ASPECT_TO_CONTENT;
                }
        }

        // If the user clicked Cancel or clicked OK, but didn't actually do
        // anything, we would get here with the action bitfield cleared.
        //
        // Return S_FALSE to indicate that the BOleConvertInfo is not valid,
        // and we shouldn't call ConvertGuts
        //
        if (pInfo->action == (BOleConvertAction) 0)
                hr = ResultFromScode (S_FALSE);
        else
                hr = ResultFromScode (S_OK);

        return hr;

}

HRESULT _IFUNC BOlePart::ConvertGuts (BOOL b, BOleConvertInfo FAR *pInfo)
{
        BOOL fMustActivate = FALSE;
        BOOL fMustRun = FALSE;
        BOOL fObjConverted = TRUE;

        HRESULT hr = NOERROR;

        // This could be a while
        //
        HCURSOR hPrevCursor = ::SetCursor (::LoadCursor (NULL, IDC_WAIT));

        CLSID cid = CLSID_NULL;
        LPOLESTR pType = NULL;
        WORD wFormat = 0;
        ConvertHelper (&cid, &pType, &wFormat);

        // User selected Convert To
        //
        if (pInfo->action & BOLE_CONVERT_TO_CLSID) {
                LPSTORAGE pTmpStg = pStgFromInit; pTmpStg->AddRef();// will be released in Close
                BOleDocument *pTmpDoc = pDoc; pTmpDoc->AddRefMain();
                Close ();
                pDoc = pTmpDoc;
                pStgFromInit = pTmpStg;
                DeleteTypeNames();
                hr = OleStdDoConvert (pStgFromInit, pInfo->clsidNew);
                if (!SUCCEEDED (hr))
                        goto error;
                hr = OLE::OleLoad (pStgFromInit, IID_IOleObject, this, &(LPVOID)pOleObject);
                fMustRun = TRUE;
                fObjConverted = TRUE;

                InitAdvises (pOleObject, dwDrawAspect,
                        (LPOLESTR)pDoc->pApplication->GetAppName(),
                        (LPOLESTR)pContainer->GetWindowTitle(), this, FALSE);

        }

        // User selected Activate As
        //
        if (pInfo->action & BOLE_ACTIVATE_AS_CLSID) {
                LPSTORAGE pTmpStg = pStgFromInit; pTmpStg->AddRef();// will be released in Close
                BOleDocument *pTmpDoc = pDoc; pTmpDoc->AddRefMain();
                Close ();
                pDoc = pTmpDoc;
                pStgFromInit = pTmpStg;
                DeleteTypeNames();

                pDoc->pService->UnloadObjects (cid);
                hr = OleStdDoTreatAsClass (pType, cid, pInfo->clsidNew);
                hr = OLE::OleLoad (pStgFromInit, IID_IOleObject, this, &(LPVOID)pOleObject);
                fMustActivate = TRUE;
        }

        // We'll want to draw this object later on
        //
        if (pOleObject) {
                if (pViewObject)
                        pViewObject->Release();
                pOleObject->QueryInterface (IID_IViewObject, &(LPVOID) pViewObject);
                if (pDataObject)
                        pDataObject->Release();
                pOleObject->QueryInterface (IID_IDataObject, &(VOID FAR*)pDataObject);
        }

        // If we're switching to icon, don't check the curr aspect because
        // we could be switching between icons.
        //
        if (pInfo->action & BOLE_ASPECT_TO_ICON) {

                // Special thing for scripting: if you ask for an object to be converted
                // to an icon from script/macro, you probably don't have an HMETAPICT
                // for the icon, so we let them pass 0 as a sentinel, and we use a
                // default icon for the CLSID
                //
                if (pInfo->hIcon == 0) {
                        pInfo->hIcon = OleGetIconOfClass (cid, NULL, TRUE);
                }

                // Don't delete the old aspect if we're switching from one icon
                // to another. Otherwise, deleting the old aspect depends on whether
                // we're activating the object
                //
                BOOL fDeleteOld;
                if (dwDrawAspect == DVASPECT_ICON)
                        fDeleteOld = FALSE;
                else
                        fDeleteOld = !b;

                hr = SwitchDisplayAspect (pOleObject, &dwDrawAspect, DVASPECT_ICON,
                        pInfo->hIcon, fDeleteOld, TRUE, this, &fMustRun, TRUE);
                if (SUCCEEDED(hr)) {
                        OnViewChange(DVASPECT_ICON, -1);
                }
        }

        // Switching to content aspect
        //
        if (dwDrawAspect == DVASPECT_ICON && pInfo->action & BOLE_ASPECT_TO_CONTENT) {
                hr = SwitchDisplayAspect (pOleObject, &dwDrawAspect, DVASPECT_CONTENT,
                        NULL, !b, TRUE, this, &fMustRun, TRUE);
                if (SUCCEEDED(hr)) {
                        OnViewChange(DVASPECT_CONTENT, -1);
                }
        }

        if (fMustRun) {
                if (!fMustActivate && !OLE::OleIsRunning (pOleObject)) {
                        hr = OleRun (pOleObject);
                        if (!SUCCEEDED(hr))
                                return hr;

                        if (fObjConverted) {

                                // Of course, static objects can't be run. This isn't an
                                // error to be worried about
                                //
                                if (hr == ResultFromScode (OLE_E_STATIC)) {
                                        Close();
                                        pStgFromInit->Revert ();
                                        return hr; //
                                }
                                else {
                                        // The object probably already has an OLERENDER_DRAW cache,
                                        // but this is just to make sure.
                                        //
                                        LPOLECACHE pCache = NULL;
                                        hr = pOleObject->QueryInterface (IID_IOleCache, &(LPVOID) pCache);
                                        if (SUCCEEDED(hr)) {
                                                FORMATETC fe;
                                                DWORD newCnxn;
                                                fe.cfFormat = NULL;
                                                fe.ptd = NULL;
                                                fe.dwAspect = DVASPECT_CONTENT;
                                                fe.lindex = -1;
                                                fe.tymed = TYMED_NULL;
                                                pCache->Cache (&fe, ADVF_PRIMEFIRST, &newCnxn);
                                                pCache->Release();
                                        }
                                }
                                // Force the storage to be committed
                                //
                                pOleObject->Close (OLECLOSE_SAVEIFDIRTY);

                        }
                }
        }

        pSite->Invalidate(BOLE_INVAL_VIEW);
        if (fMustActivate)
                DoVerb (OLEIVERB_PRIMARY);
error:

                //

        ::SetCursor (hPrevCursor);
        return hr;
}

void _IFUNC BOlePart::ResetObjectRects ()
{
        RECT pos, clip;
        OLEPRINTF1("Entering ResetObjectRects");
        if (NOERROR == pSite->GetSiteRect (&pos, &clip)) {
                OLEPRINTF3("GettingSiteRect:\t {%d, %d}",
                                   pos.right - pos.left, pos.bottom - pos.top);
                pInPlaceObject->SetObjectRects (&pos, &clip);
        }
}

//**************************************************************************
//
// IPart implementation
//
//**************************************************************************

HRESULT _IFUNC BOlePart::Save (IStorage * pStg, BOOL fSameAsLoad, BOOL fRemember)
{
        HRESULT ret = NOERROR;
        // Get a pointer to the Ole object's IPersistStorage
        if (pOleObject) {
                IPersistStorage* pPersistStg;
                HRESULT hr = pOleObject->QueryInterface(IID_IPersistStorage,
                        &(VOID FAR *)pPersistStg);
                if (SUCCEEDED(hr) && !pPersistStg)
                        ret = ResultFromScode (E_FAIL);
                else {

                        // Write the object out to storage.
                        //
                        ret = OLE::OleSave(pPersistStg, pStg, fSameAsLoad);

                        // Write the object's drawing aspect out to storage so we know
                        // which presentation data to use when we reload it. OleSave
                        // already knows how to save the iconic presentation data to storage
                        //
                        // Also write out the additional scale factor for the size
                        // imposed when the user resized the size.
                        //

                        LPSTREAM pStream = NULLP;

                        if (SUCCEEDED(pStg->CreateStream(szcBOlePart,
                                STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
                                NULL,NULL,&pStream)))  {
                                ULONG cb;
                                pStream->Write(&dwDrawAspect, sizeof(dwDrawAspect), &cb);
                                if (!(dwDrawAspect == DVASPECT_ICON)) {
                                        OLEPRINTF5("Saving scaleSite:\t {%ld, %ld, %ld, %ld}",
                                                           scaleSite.xN, scaleSite.xD, scaleSite.yN, scaleSite.yD);
                                        pStream->Write(&scaleSite, sizeof(scaleSite), &cb);
                                }
                                pStream->Release();
                        }

                        if (fRemember) {
#if 0           // not used in outline example or mfc source
                                // for SaveAs case release initial storage and remember new one
                                pPersistStg->HandsOffStorage();  // server releases initial storage
#endif
                                pPersistStg->SaveCompleted ( pStg );
                                if (pStgFromInit)
                                        pStgFromInit->Release();
                                pStgFromInit = pStg;
                                pStgFromInit->AddRef();
                        }
                        else {
                                pPersistStg->SaveCompleted ( NULL );
                        }
                        pPersistStg->Release();
                }

        }
        return ret;
}

HRESULT _IFUNC BOlePart::CanOpenInPlace ()
{
        // this is only used in our server code
        return ResultFromScode(E_FAIL);
}

HRESULT _IFUNC BOlePart::GetPalette (LPLOGPALETTE FAR *ppPal)
{
        if (pViewObject) {
                HRESULT hrErr = pViewObject->GetColorSet (
                        dwDrawAspect, -1, NULL, NULL, NULL, ppPal);
                if (hrErr == NOERROR)
                        return hrErr;
        }

        return ResultFromScode (E_FAIL);

}

HRESULT _IFUNC BOlePart::SetHost (IBContainer FAR *pNewContainer)
{
        if (!pNewContainer)
                return ResultFromScode (E_FAIL);

        BOleDocument *pNewDoc = NULL;
        if (!SUCCEEDED(pNewContainer->QueryInterface(IID_BOleDocument, &(LPVOID) pNewDoc)))
                return ResultFromScode (E_NOINTERFACE);
        pNewDoc->Release();    // PREVENT CYCLE

        if (IsOpenInPlace)
                //
                //
                return ResultFromScode (E_FAIL);

        if (pNewDoc->pActivePart)
                // Need to change pActivePart to a BOlePart* to
                // we can call Activate (FALSE) here.
                //
                return ResultFromScode (E_FAIL);


        RemoveFromList ();
        if (pDoc) {
                pDoc->ReleaseMain();
                pDoc = NULLP;
        }
        if (pNewDoc) {
                pDoc = pNewDoc;
                pDoc->AddRefMain();     // directly on helper
                pContainer = pNewContainer;
        }
        AddToList ();
        return NOERROR;
}

HRESULT _IFUNC BOlePart::Open (BOOL bOpen)
{
        if (bOpen) {
                HRESULT hRes = DoVerb(OLEIVERB_OPEN);
                pSite->Invalidate(BOLE_INVAL_VIEW); // ? do we need this (ICS)
                return hRes;
        }
        else {
                return pOleObject->DoVerb(OLEIVERB_HIDE, NULL, this, 0,
                        pContainer->GetWindow(), (LPRECT) 0);
        }
}

HRESULT _IFUNC BOlePart::Show (BOOL bShow)
{
        if (bShow) {
                HRESULT hRes = DoVerb(OLEIVERB_SHOW);
                pSite->Invalidate(BOLE_INVAL_VIEW); // ? do we need this (ICS)
                return hRes;
        }
        else {
                return pOleObject->DoVerb(OLEIVERB_HIDE, NULL, this, 0,
                        pContainer->GetWindow(), (LPRECT) 0);
        }

}


HRESULT _IFUNC BOlePart::Activate (BOOL fActivate)
{
        HRESULT hr = NOERROR;

        if (fActivate) {
                hr = DoVerb (OLEIVERB_PRIMARY);
        }
        else
                if (IsOpenInPlace) {
                        hr = pInPlaceObject->UIDeactivate ();

#if 1    //
                        if (hr == NOERROR)
                                if (IsOpenInsideOut)
                                        hr = pOleObject->DoVerb (OLEIVERB_DISCARDUNDOSTATE, NULL,
                                                this, 0, pContainer->GetWindow(), (LPRECT) 0);
                                else {
                                        if (pInPlaceObject)
                                                hr = pInPlaceObject->InPlaceDeactivate ();
                                }
#endif
                }

        return hr;
}

HWND _IFUNC BOlePart::OpenInPlace (HWND hWndParent)
{
        // this is meaningful only in our server code
        return 0;
}

HRESULT _IFUNC BOlePart::SetPartSize (LPSIZE pSize)
{
        SIZEL sl;
        sl.cx = MAP_PIX_TO_LOGHIM (pSize->cx, BOleService::pixPerIn.x);
        sl.cy = MAP_PIX_TO_LOGHIM (pSize->cy, BOleService::pixPerIn.y);

        // Size passed is in scaled units but we need to tell the server
        // its "actual size"
        //
        BOleScaleFactor scale;
        pSite->GetZoom(&scale);
        sl.cx = Scale(sl.cx, scale.xD, scale.xN);    // unscale
        sl.cy = Scale(sl.cy, scale.yD, scale.yN);

        // IOleObject::SetExtent only works if the object is running.
        // If not, we need to cache the extent locally and pass it on to the
        // object when it opens.
        //
        if (!pOleObject || !OLE::OleIsRunning(pOleObject)) {

                // If the object's MISCSTATUS specifies that it wants to be run
                // whenever we resize it, do so here
                //
                DWORD miscStatus = 0;
                if (pOleObject &&
                        (NOERROR == pOleObject->GetMiscStatus(dwDrawAspect, &miscStatus)))
                        if (miscStatus & OLEMISC_RECOMPOSEONRESIZE)
                                if (NOERROR == ForceOleRun (pOleObject))
                                        return pOleObject->SetExtent (dwDrawAspect, &sl);

                // Cache the size of the nonrunning object
                //
                CachedExtent = sl;
                return NOERROR;

        }

        // Set the size of the running object
        //
        HRESULT hr = pOleObject->SetExtent (dwDrawAspect, &sl);
        return hr;
}

HRESULT _IFUNC BOlePart::GetPartSize (LPSIZE pSize)
{
        if (!pSize)
                return ResultFromScode (E_INVALIDARG);

        SIZEL sl;

        // get himetric size and convert it to pixels
        // also adjust for scaling
        // we can give it to SetPartPos and Extent
        //
        HRESULT hr = pOleObject->GetExtent(dwDrawAspect, &sl);
        if (SUCCEEDED(hr)) {

                // If we've cached a modified extent for the object, inform the object
                // now that it's running.
                //
                if (CachedExtent.cx) {
                        if ((sl.cx == CachedExtent.cx) && (sl.cy == CachedExtent.cy)) {
                                CachedExtent.cx = 0L;
                        }
                        else {
                                if (SUCCEEDED(pOleObject->SetExtent (dwDrawAspect, &CachedExtent))) {
                                        hr = pOleObject->GetExtent(dwDrawAspect, &sl);
                                        CachedExtent.cx = 0L;
                                }
                        }
                }


                pSize->cx = MAP_LOGHIM_TO_PIX (sl.cx, BOleService::pixPerIn.x);
                pSize->cy = MAP_LOGHIM_TO_PIX (sl.cy, BOleService::pixPerIn.y);

                BOleScaleFactor scale;
                pSite->GetZoom(&scale);
                pSize->cx = Scale(pSize->cx, scale.xN, scale.xD);
                pSize->cy = Scale(pSize->cy, scale.yN, scale.yD);
        }
        else {
                OLEPRINTF1("Entering GetPartSize failure case");
                // if the server isn't ready to return the size we should use the sites
                RECT rPos, rClip;
                pSite->GetSiteRect(&rPos, &rClip);
                OLEPRINTF3("GettingSiteRect:\t {%d, %d}",
                                   rPos.right - rPos.left, rPos.bottom - rPos.top);
                pSize->cx = rPos.right - rPos.left;
                pSize->cy = rPos.bottom - rPos.top;
        }
        return hr;
}

HRESULT _IFUNC BOlePart::SetPartPos (LPRECT pNewSize)
{
        HRESULT hr = ResultFromScode (E_FAIL);

        OLEPRINTF1("Entering SetPartPos");
        // If we're open in place, we can actually change the location of the
        // object's in-place window. Used for scrolling
        //
        if (IsOpenInPlace) {
                RECT pos, clip;
                if (NOERROR == pSite->GetSiteRect (&pos, &clip))  {
                hr = pInPlaceObject->SetObjectRects ((LPCRECT)pNewSize, &clip);
                OLEPRINTF3("SettingObjectRects:\t {%d, %d}",
                   pNewSize->right - pNewSize->left,
                   pNewSize->bottom - pNewSize->top);
                }
        }

        // If we're not open in place, we can only change the extent of the
        // object, and the rectangles should be right in the next IPart::Draw
        //
        else {
                SIZE s;
                s.cx = pNewSize->right - pNewSize->left;
                s.cy = pNewSize->bottom - pNewSize->top;
                SetPartSize(&s);
        }

        return hr;
}

inline BOOL IsMetafile(HDC hdc)
{
        return (DT_METAFILE == GetDeviceCaps(hdc,TECHNOLOGY));
}

HRESULT _IFUNC BOlePart::Draw (HDC hdc, LPCRECTL pPosRect, LPCRECTL pClipRect,
                               BOleAspect reqAspect, BOleDraw bdFlags)
{
        int oldDCInfo = WIN::SaveDC (hdc);

        //
        //
        //
//
//
//
//

        HRESULT hRes = ResultFromScode (E_FAIL);

        // If you're drawing objects, but not using the metafile to get it,
        // you'd get the high-res graphics data yourself, but you still need to
        // draw the open-editing shading. Hence this flag.
        //
        if (bdFlags & BOLE_DRAW_SHADINGONLY) {
                hRes = NOERROR;
                goto shadingOnly;
        }

        // If this object is a link, make sure we're bound to it just in case
        // someone else has opened the link source and makes changes to it. This
        // will force those changes to be propogated to us
        //
        if (IsLink) {
                DWORD linkopts = 0;
                if (SUCCEEDED(pLinkObject->GetUpdateOptions (&linkopts)))
                        if (linkopts & OLEUPDATE_ALWAYS)
                                pLinkObject->BindIfRunning();
        }

        // Remove the special Bolero aspect values from the bitfield
        // so those values don't show through when we send the aspect
        // to OLE2. OLE2 will return OLE_E_BLANK for non-standard values
        //
        dwExtAspect = 0; // Clear this out so caller can use different settings each time through
        if (reqAspect & BOLE_DEFAULT)
                dwExtAspect = dwExtAspect | BOLE_DEFAULT;
        else
                dwDrawAspect = reqAspect;
        if (reqAspect & BOLE_ASPECT_MAINTAIN)
                dwExtAspect = dwExtAspect | BOLE_ASPECT_MAINTAIN;
        if (reqAspect & BOLE_CLIP)
                dwExtAspect = dwExtAspect | BOLE_CLIP;

        reqAspect = (reqAspect & ~(BOLE_ASPECT_MAINTAIN | BOLE_DEFAULT | BOLE_CLIP));

        if (pViewObject) {
                RECTL hmRect = *pPosRect;

                // Get the part's extents in himetric
                SIZEL srvExt;
                HRESULT hr = pOleObject->GetExtent(dwDrawAspect, &srvExt);
                if (SUCCEEDED(hr)) {
                        // scale part's extents by client's scale factor
                        BOleScaleFactor scale;
                        pSite->GetZoom(&scale);
                        srvExt.cx = Scale(srvExt.cx, scale.xN, scale.xD);
                        srvExt.cy = Scale(srvExt.cy, scale.yN, scale.yD);
                }
                else {
                        OLEPRINTF1("Couldn't GetExtent during draw");
                        // if server's extent isn't available, use what's passed in
                        srvExt.cx = hmRect.right - hmRect.left;
                        srvExt.cy = hmRect.bottom - hmRect.top;
                }

#if 1
                if (dwExtAspect & BOLE_ASPECT_MAINTAIN) {
                        // Scale the rect we draw into if either the x extent or the y extent
                        // overflows. The resulting rect is in the upper left corner of the client.
                        SIZEL cliExt;
                        cliExt.cx = hmRect.right - hmRect.left;
                        cliExt.cy = hmRect.bottom - hmRect.top;

                        //
                        long dx, dy;
                        dx = cliExt.cx - srvExt.cx;
                        dy = cliExt.cy - srvExt.cy;
                        if (dx >= 0 && dy >= 0) {
                                // We have enough room, both x and y, to display the object
                                hmRect.right = hmRect.left + srvExt.cx;
                                hmRect.bottom = hmRect.top + srvExt.cy;
                        }
                        else {
                                // Either the x or y extent is too large. Decide which axis overflows
                                // more, and leave it the same size as the client, which is its
                                // maximum value. Then scale the axis which overflows less (maybe not
                                // at all) to maintain the server's aspect ratio
                                //
                                if (dx < dy)
                                        hmRect.bottom = hmRect.top + Scale(srvExt.cy, cliExt.cx, srvExt.cx);
                                else
                                        hmRect.right = hmRect.left + Scale(srvExt.cx, cliExt.cy, srvExt.cy);
                        }
                }
#endif

                if (dwExtAspect & BOLE_CLIP) {
                        hmRect.right = hmRect.left + srvExt.cx;
                        hmRect.bottom = hmRect.top + srvExt.cy;
                }
                // Ask the server object to draw itself.
                //
                hRes = pViewObject->Draw(dwDrawAspect, -1, NULL,  NULL,
                        NULL, hdc, &hmRect, IsMetafile(hdc) ? pClipRect : NULL,
                        NULL, 0);
        }
        else {
                WIN::RestoreDC (hdc, oldDCInfo);
                return ResultFromScode (E_FAIL);
        }

shadingOnly:

        RECT r;
        r.top = pPosRect->top;
        r.left = pPosRect->left;
        r.bottom = pPosRect->bottom;
        r.right = pPosRect->right;

        // If the server app has the embedded object open in its own window,
        // as opposed to inplace in our window, draw special highlighting
        //
        if (IsOpen) {
                OUI::OleUIDrawShading(&r, hdc, OLEUI_SHADE_FULLRECT, 0);
        }

        // If the app wants us to draw the borders around OLE2 objects as
        // described in the OLE2 UI guidelines, do it here.
        //
        if (bdFlags & BOLE_DRAW_SHOWOBJECTS) {
                int penStyle = IsLink ? PS_DASH : PS_SOLID;
                HPEN p = CreatePen (penStyle, 1, RGB(0,0,0));
                HBRUSH b = (HBRUSH) GetStockObject (HOLLOW_BRUSH);
                HPEN oldP = (HPEN) SelectObject (hdc,p);
                HBRUSH oldB = (HBRUSH) SelectObject (hdc,b);
                Rectangle (hdc, ( int )r.left, ( int )r.top,
                        ( int )( r.right - r.left ),
                        ( int )( r.bottom - r.top ) );
                SelectObject (hdc, oldP);
                SelectObject (hdc, oldB);
                DeleteObject (p);
                DeleteObject (b);
        }

        WIN::RestoreDC (hdc, oldDCInfo);
        return hRes;
}

HRESULT _IFUNC BOlePart::InsertMenus (HMENU, BOleMenuWidths*)
{
        // this is meaningful only in our server code
        return ResultFromScode (E_NOTIMPL);
}

void _IFUNC BOlePart::FrameResized (const RECT *, BOOL)
{
        // this is meaningful only in our server code
}

HRESULT _IFUNC BOlePart::ShowTools (BOOL)
{
        // this is meaningful only in our server code
         return ResultFromScode (E_NOTIMPL);
}

HRESULT _IFUNC BOlePart::EnumVerbs (BOleVerb *pBV)
{
        // The purpose of verbIndex is to keep track of the enumerator in
        // here rather than forcing the Bolero user to do it. It is assumed
        // that the Bolero user will call EnumVerbs until it fails, otherwise
        // the enumerator will be left in an incoherent state

        // The strategy for pVerbEnumerator is different than other interfaces.
        // Usually, we just get a pointer to it during IPart::Init and hang on
        // to the pointer until the Part is destroyed. In this case, the verb
        // enumerator could be made invalid by closing the server app. Then
        // the Next function returns RPC_E_SERVERDIED. It seems that OLE2, probably
        // in the default handler, provides another enumerator which can get
        // the verbs, probably from the registration database, when the
        // server isn't running. So we'll obtain and release the pointer
        // dynamically.

        // The lpszVerbName field in the OLEVERB is dynamically allocated during
        // IEnumVerb::Next. In order to prevent a memory leak, deallocate it here.
        // The DidAlloc is probably unnecessary, but it's a little protection
        // against the user passing in a pBV->verbName which is just a random
        // value off the stack.
        //
        if (pBV->verbName) {
                LPMALLOC pMalloc = NULL;
                HRESULT hr = ::CoGetMalloc (MEMCTX_TASK, &pMalloc);
                if (!SUCCEEDED(hr))
                        return hr;
                if (!IsBadReadPtr ((LPVOID) pBV->verbName, 4) &&
                        pMalloc->DidAlloc ((LPVOID) pBV->verbName)) {
                        pMalloc->Free ((LPVOID) pBV->verbName);
                        pBV->verbName = NULL;
                }
                pMalloc->Release ();
        }

        if (verbIndex == 0) {
                HRESULT hr = pOleObject->EnumVerbs(&pVerbEnumerator);

                if (hr != NOERROR)
                        return hr;

                pVerbEnumerator->Reset ();
        }

        // Loop over verbs looking for non-negative verb numbers. The negative
        // ones are "aliases" for other verbs, and shouldn't be displayed
        //
        // The loop stops when Next returns an error, but loops over alias
        // verbs. EnumVerbs should be called in a loop by the user
        //
        while (TRUE) {
                SCODE sc = GetScode(pVerbEnumerator->Next (1, &oleVerb, NULL));
                if (sc == S_OK) {
                        if (oleVerb.lVerb > -1) {
                                pBV->verbName = oleVerb.lpszVerbName;
                                pBV->typeName = GetName (BOLE_NAME_SHORT);
                                pBV->verbIndex = oleVerb.lVerb;
                                pBV->fCanDirty = !(oleVerb.grfAttribs & OLEVERBATTRIB_NEVERDIRTIES);
                                verbIndex++;
                                return NOERROR;
                        }
                }
                else {
                        verbIndex = 0;
                        pVerbEnumerator->Release ();
                        return ResultFromScode (sc);
                }
        }
}

HRESULT _IFUNC BOlePart::DoVerb (UINT whichVerb)
{
        ForceOleRun(pOleObject);    // this line only added for Excel problems

        // Ask the container/app again for their names, in case something
        // is different from the last time we were open editing.
        //
        pOleObject->SetHostNames (pDoc->pApplication->GetAppName(),
                pDoc->pContainer->GetWindowTitle());

        RECT rect;
        pSite->GetSiteRect (&rect, 0);
        HRESULT hRes = pOleObject->DoVerb((LONG) (int)whichVerb, NULL, this, 0,
                pContainer->GetWindow(), &rect);

        if( !SUCCEEDED( hRes ) ){
                // This following (potentially big) block will deserve a helper function.
                // So far, only ONE error is implemented. Need to be completed.
                // Reference: outline/cntrline.c: ContainerLine_ProcessOleRunError()
                //
                SCODE error = GetScode( hRes );
                if ((error >= MK_E_FIRST && error <= MK_E_LAST) ||
                         (error == OLE_E_CANT_BINDTOSOURCE)) {
                        HandleLinkError();
                        fLinkSrcAvail = FALSE;
                }
                if( error == REGDB_E_CLASSNOTREG ){
                        // Server not registered (unexpected here?)
                }
                if( error == OLE_E_STATIC ){
                        // A static object with no server registered
                }
                if( error == OLE_E_CLASSDIFF ){
                        // Type of link has changed
                }
                if( ( error == CO_E_APPDIDNTREG )||( error == CO_E_APPNOTFOUND ) ){
                        // Server not found
                }
                if( error == E_OUTOFMEMORY ){
                        // Out of memory
                }
                return hRes;
        }

        HWND hWnd, hWndPrevParented;
        if (IsOpenInPlace && pInPlaceObject &&
                SUCCEEDED(pInPlaceObject->GetWindow(&hWnd)) &&
                SUCCEEDED(GetWindow(&hWndPrevParented))) {
                while (hWnd && GetParent(hWnd) != hWndPrevParented) {
                        hWnd = GetParent(hWnd);  // make sure its the window they parented
                }
                if (hWnd && hWnd != GetTopWindow(GetParent(hWnd))) {
                        SetWindowPos(hWnd, HWND_TOP, 0,0,0,0,   // MSWORD z-order workaround
                                SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
#ifdef ANSI
                  LONG l = GetWindowLongA(hWnd, GWL_STYLE);  // visio paint problems
                  SetWindowLongA(hWnd, GWL_STYLE ,l | WS_CLIPSIBLINGS);  // visio paint problems
#else
                  LONG l = GetWindowLong(hWnd, GWL_STYLE);  // visio paint problems
                  SetWindowLong(hWnd, GWL_STYLE ,l | WS_CLIPSIBLINGS);  // visio paint problems
#endif
                }
        }

        return hRes;
}

HRESULT _IFUNC BOlePart::DragFeedback(POINT*,BOOL)
{
        return ResultFromScode (E_NOTIMPL);
}

UINT _IFUNC BOlePart::CountFormats ()
{
#if 0
        LPENUMFORMATETC enumFormatEtc;
        UINT n = 0;

        if( pDataObject && SUCCEEDED( pDataObject->EnumFormatEtc( DATADIR_GET, &enumFormatEtc ) ) ){
                FORMATETC fmt;
                while( enumFormatEtc->Next( 1, &fmt, 0 ) == NOERROR )
                        n++;
                enumFormatEtc->Release();
        }
        return n;
#else
        return 0;
#endif
}

HRESULT _IFUNC BOlePart::GetFormat ( UINT i, BOleFormat *BOleFmt )
{
#if 0
        LPENUMFORMATETC enumFormatEtc;

        if( pDataObject && SUCCEEDED( pDataObject->EnumFormatEtc( DATADIR_GET, &enumFormatEtc ) ) ){
                FORMATETC fmt;
                while( enumFormatEtc->Next( 1, &fmt, 0 ) != NOERROR && i-- );
                _fmemset( BOleFmt, 0, sizeof( *BOleFmt ) );
                BOleFmt->fmtId = fmt.cfFormat;
                BOleFmt->fmtMedium = ( BOleMedium )fmt.tymed;
                enumFormatEtc->Release();
        }
        return NOERROR;
#else
        return ResultFromScode (E_NOTIMPL);
#endif
}

HANDLE  _IFUNC BOlePart::GetFormatData (BOleFormat*pF)
{
        LPDATAOBJECT pDataObj = NULL;
        if (!pOleObject)
                assert (FALSE);

        HRESULT hr = pOleObject->QueryInterface (IID_IDataObject, &(void FAR*) pDataObj);

        if (!pDataObj || !SUCCEEDED(hr))
                return NULL;

        STGMEDIUM medium;

        // Just change it to WORD in boledefs.h please
        HANDLE h = pDoc->pService->GetDataFromDataObject (pDataObj,
                (CLIPFORMAT)pF->fmtId, NULL, dwDrawAspect, &medium);

        pDataObj->Release ();
        //

        return h;
}

// Set data into the server
//
HRESULT  _IFUNC BOlePart::SetFormatData(BOleFormat* fmt, HANDLE hData, BOOL release)
{
  if (!fmt || !hData)
    return ResultFromScode(DATA_E_FORMATETC);

  LPDATAOBJECT pIData;
  HRESULT hr = pOleObject->QueryInterface (IID_IDataObject, &(LPVOID) pIData);
  if (!SUCCEEDED(hr))
    return hr;

  STGMEDIUM stm;
  _fmemset (&stm, 0, sizeof(STGMEDIUM));
  stm.tymed = TYMED_ISTORAGE;

  FORMATETC fe;
  _fmemset (&fe, 0, sizeof(FORMATETC));
  fe.cfFormat = fmt->fmtId;
  fe.ptd = NULL;
  fe.dwAspect = DVASPECT_CONTENT;
  fe.lindex = -1;
  fe.tymed = TYMED_HGLOBAL;

  return pIData->SetData (&fe, &stm, release);
}


LPOLESTR _IFUNC BOlePart::GetName (BOlePartName nameCode)
{
        // First see if we've already retrieved this name, and return
        // it without asking again
        //
        LPOLESTR FAR *ppName = NULL;
        switch (nameCode) {
                case BOLE_NAME_LONG  : ppName = &pLongName; break;
                case BOLE_NAME_SHORT : ppName = &pShortName; break;
                case BOLE_NAME_APP   : ppName = &pAppName; break;
                default              : return NULL;
        }

        if (*ppName)
                return *ppName;

        if (!pOleObject)
                return NULL;

        // Ask the OLE object for the name. GetUserType will allocate space
        // in our IMalloc, so we don't have to allocate it again. Of course,
        // we will have to free it later.
        //
        HRESULT hr = pOleObject->GetUserType (nameCode, ppName);

        if (!SUCCEEDED(hr) || !(*ppName))
                return NULL;

        return *ppName;
}

HRESULT _IFUNC BOlePart::DoQueryInterface(REFIID iid, void FAR* FAR* pif)
{       return (pOleObject) ?
                pOleObject->QueryInterface(iid, pif)
                : ResultFromScode(E_NOINTERFACE);
}

#undef  OLE_INTERFACE
/**************************************************************************/
#define OLE_INTERFACE "IOleClientSite"

HRESULT _IFUNC BOlePart::SaveObject ()
{
        if (!pStgFromInit)
                return ResultFromScode(E_NOTIMPL);

        return Save(pStgFromInit, TRUE, FALSE);
}

IMoniker * _IFUNC BOlePart::GetObjectMoniker(DWORD dwAssign)
{
        IMoniker* pMoniker = NULL;
        if (pszInstName) {

                switch (dwAssign) {
                        case OLEGETMONIKER_FORCEASSIGN:
                                OLE::CreateItemMoniker(TEXT("!"), pszInstName, &pMoniker);
                                if (pOleObject && !MonikerAssigned) {
                                        pOleObject->SetMoniker(OLEWHICHMK_OBJREL, pMoniker);
                                        MonikerAssigned = TRUE;
                                }
                                break;
                        case OLEGETMONIKER_TEMPFORUSER:
                                OLE::CreateItemMoniker(TEXT("!"), pszInstName, &pMoniker);
                                break;
                        case OLEGETMONIKER_ONLYIFTHERE:
                                if (MonikerAssigned) {
                                        OLE::CreateItemMoniker(TEXT("!"), pszInstName, &pMoniker);
                                }
                                break;
                        case OLEGETMONIKER_UNASSIGN:
                                MonikerAssigned = FALSE;
                                break;
                }
        }
        return pMoniker;
}

HRESULT _IFUNC BOlePart::GetMoniker (DWORD dwAssign, DWORD dwWhichMoniker, IMoniker* FAR* ppmk)
{
        *ppmk = NULL;
        if (pILinkCont) {
                switch (dwWhichMoniker) {

                        // Identifies the document within the filesystem
                        case OLEWHICHMK_CONTAINER:
                                pILinkCont->GetMoniker(ppmk);
                                break;

                        // Identifies the object relative to the document
                        case OLEWHICHMK_OBJREL:
                                *ppmk = GetObjectMoniker(dwAssign);
                                break;

                        // Identifies the doc and object absolutely
                        case OLEWHICHMK_OBJFULL:
                                LPMONIKER pmkDoc;
                                LPMONIKER pmkObj;
                                if (SUCCEEDED(pILinkCont->GetMoniker(&pmkDoc))) {
                                        pmkObj = GetObjectMoniker(dwAssign);
                                        if (pmkObj) {
                                                CreateGenericComposite(pmkDoc, pmkObj, ppmk);
                                                pmkDoc->Release();
                                                pmkObj->Release();
                                        }
                                }
                                break;
                }
        }
        return ResultFromScode((*ppmk) ? S_OK : E_FAIL);
}

HRESULT _IFUNC BOlePart::GetContainer (IOleContainer* FAR* ppContainer)
{
        return pDoc->QueryInterface(IID_IOleContainer, (LPVOID*)ppContainer);
}

HRESULT _IFUNC BOlePart::ShowObject ()
{
        if (!IsOpenInPlace)
                pSite->SiteShow (TRUE);
        return NOERROR;
}

HRESULT _IFUNC BOlePart::OnShowWindow (BOOL fShow)
{
        // The server app calls OnShowWindow when it believes we should draw
        // or redraw the open-editing shading over the object. To cut down on
        // the noise, only Invalidate if the new state is different.
        //
        if (fShow != IsOpen) {
                IsOpen = fShow;
                pSite->Invalidate(BOLE_INVAL_VIEW);
        }
        return NOERROR;
}

HRESULT _IFUNC BOlePart::RequestNewObjectLayout ()
{
        // The OUTLINE test app says OLE doesn't use this function yet.
        // The OLE docs say it can be called if the server needs more or less
        //   room in the client, but that the client can ignore the request if
        //       the client wants to determine the amount of available real estate
        //
        return ResultFromScode (E_NOTIMPL);
}

//*************************************************************************
//
// ILinkInfo implementation
//
//*************************************************************************

HRESULT _IFUNC BOlePart::SourceBreak()
{
        if (!pLinkObject)
                return ResultFromScode (E_FAIL);

        // Break the link by setting its moniker to NULL
        //
        HRESULT hrErr = pLinkObject->SetSourceMoniker ((LPMONIKER)NULL, CLSID_NULL);
        if (SUCCEEDED(hrErr)) {
                IsLink = FALSE;
                pLinkObject->Release();
                pLinkObject = NULL;
        }

        // Release all the pointers we have on this OLE object in preparation
        // for creating a new object. We might also delete the part, but it seems
        // rude to do that while the user still has a refcount on us.
        //
        //Close ();

        // Create a static object in place of the current link object
        //
        IDataObject *dataObj = NULL;
        hrErr = pOleObject->QueryInterface (IID_IDataObject, &(VOID FAR*)dataObj);
        if (hrErr != NOERROR)
                return hrErr;

        FORMATETC fe;
        LPFORMATETC pFE;
        DWORD render;
        if (dwDrawAspect == DVASPECT_ICON) {
                // If the link we're breaking is displayed as an icon, we'll have to
                // display the static picture as content
                //
                fe.tymed = TYMED_NULL;
                fe.ptd = NULL;
                fe.lindex = -1;
                fe.dwAspect = DVASPECT_ICON;
                fe.cfFormat = 0;
                pFE = &fe;
//              dwDrawAspect = DVASPECT_CONTENT;
        }
        else {
                pFE = NULL;
        }

        render = OLERENDER_DRAW;

        pOleObject->Release(); //

        hrErr = OLE::OleCreateStaticFromData (dataObj, IID_IOleObject, render,
                pFE, this, pStgFromInit, &(LPVOID)pOleObject);

        if (dataObj) {
                dataObj->Release ();
                dataObj = NULL;
        }

        return NOERROR;
}

HRESULT _IFUNC BOlePart::SourceGet(LPOLESTR FAR szOut, UINT count)
{
        if (!pOleObject || !pLinkObject)
                return ResultFromScode (E_FAIL);

        LPOLESTR FAR *pShortType = NULL;
        LPOLESTR FAR *pFullType = NULL;
        LPOLESTR pDisplayName;
        DWORD len;


        BOOL b = SourceGet (&pDisplayName, pFullType, pShortType, &len);

        if (b) {
                lstrcpyn( szOut, pDisplayName, count );
                IMalloc *pMalloc;
                if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pMalloc))) {
                        pMalloc->Free(pDisplayName);
                        pMalloc->Release();
                }
        }
        return(b ? NOERROR : ResultFromScode (E_FAIL));
}

HRESULT _IFUNC BOlePart::SourceSet (LPOLESTR pNewSrc)
{
        if (!pLinkObject)
                return ResultFromScode (E_FAIL);

        HRESULT hrErr = NOERROR;
        LPMONIKER pMk = NULLP;
        CLSID clsid   = CLSID_NULL;
        LPBC bindCtx  = NULLP;

//

        // Create a new bind context to use for these monikers
        //
        hrErr = OLE::CreateBindCtx (0 /*unused*/, &bindCtx);
        if (hrErr != NOERROR)
                return hrErr;

        // Recursively resolve this textual representation into a binary moniker
        //
        DWORD chEaten = 0;
        hrErr = OLE::MkParseDisplayName (bindCtx, pNewSrc, &chEaten, &pMk);
        if (hrErr != NOERROR) {
                bindCtx->Release ();
                return hrErr;
        }

        // Obtain the CLSID of the object the moniker points to by using
        // a pointer to the IOleObject as a middleman
        //
        LPOLEOBJECT pObj = NULLP;
        hrErr = pMk->BindToObject (bindCtx, NULL, IID_IOleObject, &(VOID FAR*)pObj);
        if (hrErr == NOERROR) {
                hrErr = pObj->GetUserClassID (&clsid);
                pObj->Release();

                OnViewChange (dwDrawAspect, -1); // get new size if any
                fLinkSrcAvail = TRUE;
        }
        else {
                HandleLinkError();
                fLinkSrcAvail = FALSE;
        }
//
//
//
//
//
//
//
//
//
//
//
//
//

        // Make changes to the link object: if we were successful in getting a
        // moniker, use that, but fall back to the textual name if no moniker
        // was found.
        //
        if (pMk)
                hrErr = pLinkObject->SetSourceMoniker (pMk, clsid);
        else
                hrErr = pLinkObject->SetSourceDisplayName (pNewSrc);

        // Release scratch pointers (in reverse order)
        //
        if (pMk)
                pMk->Release ();
        if (bindCtx)
                bindCtx->Release ();

        // Now that the site is relinked to a new link source, we don't know
        // anything about the type of the new source. So delete the type
        // information we keep around. It will get allocated next time someone
        // asks for it.
        //
        DeleteTypeNames();

        if (hrErr == NOERROR)
                UpdateNow();

        if (dwDrawAspect == DVASPECT_ICON) {
                CacheIconicAspect(OleGetIconOfFile (pNewSrc, TRUE));
        }

        return hrErr;
}

HRESULT _IFUNC BOlePart::SourceOpen()
{
        if (!pOleObject)
                return ResultFromScode (E_FAIL);

        return DoVerb (OLEIVERB_SHOW);
}

HRESULT _IFUNC BOlePart::UpdateGet(BOleLinkUpdate *pOptions)
{
        if (!pLinkObject)
                return ResultFromScode (E_FAIL);

        // Use a temporary to avoid problems since BOleLinkUpdate is an enum
        // of some size (we don't want to rely on) and the update options
        // are a long
        //
        DWORD o = 0;
        HRESULT hrErr = pLinkObject->GetUpdateOptions (&o);
        *pOptions = (BOleLinkUpdate)o;

        return hrErr;
}

HRESULT _IFUNC BOlePart::UpdateSet(BOleLinkUpdate options)
{
        if (!pLinkObject)
                return ResultFromScode (E_FAIL);

        HRESULT hrErr = pLinkObject->SetUpdateOptions (options);
        return hrErr;
}

// Since we don't know why Excel returns RPC_E_CALL_REJECTED the
// first N times and then finally works, we have this kludge.
//
HRESULT ForceOleUpdate(IOleObject *pOO)
{
        int timeout = 500;
        while (timeout-- > 0) {
                if (ResultFromScode(RPC_E_CALL_REJECTED) != pOO->Update())
                        break;
        }
        return (timeout == 0) ? ResultFromScode(RPC_E_CALL_REJECTED) : NOERROR;
}

HRESULT _IFUNC BOlePart::UpdateNow()
{
        if (!pOleObject)
                return ResultFromScode (E_FAIL);

        HRESULT hrErr = pOleObject->IsUpToDate ();
        if (hrErr != NOERROR)
                hrErr = ForceOleUpdate(pOleObject);

        return hrErr;
}

// This version of SourceGet is not part of the public bolero
// interface for now. It only exists to support IOleUILinkContainer::GetLinkSource
// Maybe we'll end up putting more stuff in the Bolero version ???
//
BOOL _IFUNC BOlePart::SourceGet
(
        LPOLESTR FAR *lplpszDisplayName,
        LPOLESTR FAR *lplpszFullLinkType,
        LPOLESTR FAR *lplpszShortLinkType,
        DWORD FAR *lplenFileName
)
{
        if (!pOleObject || !pLinkObject)
                return FALSE;

        LPMONIKER pmk;
        HRESULT hrErr = pLinkObject->GetSourceMoniker (&pmk);
        if (hrErr == NOERROR) {
                pOleObject->GetUserType (USERCLASSTYPE_FULL, lplpszFullLinkType);
                pOleObject->GetUserType (USERCLASSTYPE_SHORT, lplpszShortLinkType);
                *lplenFileName = OleStdGetLenFilePrefixOfMoniker (pmk);
                pmk->Release ();
        }

        hrErr = pLinkObject->GetSourceDisplayName (lplpszDisplayName);
        return (hrErr == NOERROR);
}

BOlePart * _IFUNC BOlePart::GetNextLink (BOOL fAllowThis)
{
        BOlePart *ret = fAllowThis ? this : this->pNextPart;
        while (ret) {
                if (ret->IsLink)
                        return ret;
                ret = ret->pNextPart;
        }
        return NULLP;
}

//*************************************************************************
//
// IAdviseSink implementation
//
//*************************************************************************

void _IFUNC BOlePart::OnDataChange (FORMATETC FAR* pFormatetc,STGMEDIUM FAR* pStgmed)
{
        // I'm not sure how to respond to this, vs how to respond to OnViewChange.
        //  It seems like if our visible view onto the data hasn't changed, then
        //  we're not interested in OnDataChange. Maybe there's some significance
        //  for non-visible server objects...
        //
#ifdef _DEBUG
        MessageBox (NULL, OLESTR("BOLE.DLL"), OLESTR("Unexpected call to OnDataChange"), MB_OK);
#endif
        return;
}

void _IFUNC BOlePart::OnViewChange (DWORD changedAspect, LONG lindex)
{
        // The server may generate an OnViewChange when its extent changes,
        // so try to make the site the same size.
        //
        // This is really important for Paintbrush (all OLE1 servers?) whose
        // initial size is zero, and don't send OnPosRectChanged (because
        // they're never inplace). They only send OnViewChange, and expect
        // us to get the new extent.
        //
        // The changedAspect parameter tells us which aspect's view has
        // changed. It seems like we only care if the aspect we're currently
        // viewing is the same as the one which has changed.
        //

        if (dwDrawAspect == changedAspect) {

                // this can be called when we've resized the site, but not set
                // the part extent. In such a case the part extent is not equal to
                // the site extent.  The part is scaling by the ratio, scaleSite (set in
                // OnUIActivate).  So, we need to scale the part extent
                // and set our site size to that. If we were to just use the part's
                // extent, the symptom would be that upon deactivation the site
                // would return to the size it had before the (non-active) site
                // was resized, (or worse if an in-place active resize happened too).

                SIZE shortSize;

                GetPartSize(&shortSize);

                // adjust by additional scaling caused when resizing site while
                // not in place active (and not setting server extent).

                OLEPRINTF5("Adjusting by scaleSite:\t {%ld, %ld, %ld, %ld}",
                        scaleSite.xN, scaleSite.xD, scaleSite.yN, scaleSite.yD);

                shortSize.cx = Scale(shortSize.cx, scaleSite.xN, scaleSite.xD);
                shortSize.cy = Scale(shortSize.cy, scaleSite.yN, scaleSite.yD);

                if (!SUCCEEDED(pSite->SetSiteExtent(&shortSize))) {

                        // if client doesn't accept server's extent maybe the
                        // server will accept the client's...
                        //
                        RECT rSite, rClip;
                        HRESULT hr = pSite->GetSiteRect(&rSite, &rClip);
                        if (SUCCEEDED(hr)) {
                                SIZE sizeSite = {rSite.right - rSite.left, rSite.bottom - rSite.top};
                                SetPartSize(&sizeSite);
                        }
                }

                // Generate a paint message for the client app's window, which will
                // eventually call back to our pViewObject->Draw
                //
                pSite->Invalidate(BOLE_INVAL_VIEW | BOLE_INVAL_DATA);
                UpdateSiteScaleFactor();
        }

        return;
}

void _IFUNC BOlePart::OnRename (IMoniker* pmk)
{
        // Significant for IOleLink objects which must track the link source
        //
        //
        return;
}

void _IFUNC BOlePart::OnSave ()
{
        // Significant if the client implements IOleCache with ADVF_ONSAVE
        //
        return;
}

void _IFUNC BOlePart::OnClose ()
{
        // Significant for IOleLink objects when server is shutting down
        //
        return;
}


//**************************************************************************
//
// IOleWindow implementation
//
//**************************************************************************

HRESULT _IFUNC BOlePart::GetWindow (HWND FAR *phwnd)
{
        HRESULT hr = NOERROR;
        PIBSite2 pSite2 = NULL;
        *phwnd = NULL;

        hr = pObjOuter->QueryInterfaceMain (IID_IBSite2, &(LPVOID)pSite2);
        if (SUCCEEDED (hr)) {
                hr = pSite2->GetParentWindow (phwnd);
                pSite2->Release();
        }
        else {
                *phwnd = pContainer->GetWindow();
                hr = NOERROR;
        }

        return hr;
}

HRESULT _IFUNC BOlePart::ContextSensitiveHelp (BOOL fEnterMode)
{
        // The BOleService object is responsible for keeping track of Shift-F1
        //
        pDoc->pService->SetHelpMode (fEnterMode);

        return NOERROR;
}

//**************************************************************************
//
// IOleInPlaceSite Implementation
//
//**************************************************************************

HRESULT _IFUNC BOlePart::CanInPlaceActivate (void)
{
        HRESULT hr;
        // Objects rendered as an icon can only be edited "out of place"
        //
        if (dwDrawAspect == DVASPECT_ICON)
                OLERET(S_FALSE);

        return pContainer->AllowInPlace();
}

HRESULT _IFUNC BOlePart::OnInPlaceActivate (void)
{
        // In case we don't have balanced calls...
        if (pInPlaceObject) {
                pInPlaceObject->Release();
                pInPlaceObject = NULL;
        }
        // This is our opportunity to get a pointer to the object's
        // IOleInPlaceObject interface
        //
        HRESULT hr = pOleObject->QueryInterface(IID_IOleInPlaceObject, &(VOID FAR*)pInPlaceObject);

        if (hr == NOERROR) {
                IsOpenInPlace = TRUE;
                // If the object has opened in place site should hide themselves to
                // avoid strange drawing behavior
                //
                pSite->SiteShow (FALSE);
        }
        else {
                IsOpenInPlace = FALSE;
                pInPlaceObject = NULL;
        }

        return hr;
}

HRESULT _IFUNC BOlePart::OnInPlaceDeactivate (void)
{
        if (pInPlaceObject) {
                pInPlaceObject->Release();
                pInPlaceObject = NULL;
        }
        if (IsOpenInPlace) {
                IsOpenInPlace = FALSE;
                pSite->SiteShow (TRUE);    // put the site window back again
        }
        return NOERROR;
}

// Remember the additional scale factor for this site.
// If we've resized the site the scale factor is different than normal.
// We need to know it in OnViewChange, in case the server is resized
// when in-place active.
//
void BOlePart::UpdateSiteScaleFactor()
{
        RECT rPos, rClip;
        if (SUCCEEDED(pSite->GetSiteRect (&rPos, &rClip))) {

                SIZE sizePart;
                GetPartSize(&sizePart);

                if ((sizePart.cx == 0) || (sizePart.cy == 0)) {
                        scaleSite.xN = scaleSite.xD = scaleSite.yN = scaleSite.yD = 1L;
                }
                else {
                        scaleSite.xN = rPos.right - rPos.left;
                        scaleSite.xD = sizePart.cx;
                        scaleSite.yN = rPos.bottom - rPos.top;
                        scaleSite.yD = sizePart.cy;
                }
                OLEPRINTF5("Updating scaleSite:\t {%ld, %ld, %ld, %ld}",
                        scaleSite.xN, scaleSite.xD, scaleSite.yN, scaleSite.yD);
        }
}

HRESULT _IFUNC BOlePart::OnUIActivate (void)
{
        OLEPRINTF1("Entering OnUIActivate");

        // (Used to UpdateSiteScaleFactor in GetWindowContext.
        // But, MS Excel Chart doesn't give accurate extents at that point
        // In OnInPlaceActivate Excel Chart gives inaccurate extents.
        // In OnUIActivate Excel Sheet gives inaccurate extents.
        //
        UpdateSiteScaleFactor();

        // The inplace server is about to put up its own tool/status
        // bars, so ask the client app to remove its adorners
        //
        // Although it isn't in the official docs, the Outline example
        // (in frametls.c) uses the convention where SetBorderSpace({0,})
        // means hide adornments

        // hide their adornments
        //
        HRESULT hr = NOERROR;

        LPOLEINPLACESITE pIPS;
        if (SUCCEEDED(GetParentIPS(&pIPS))) {
                IBPart *pServerSide;
                if (SUCCEEDED(pDoc->QueryInterface(IID_IBPart, &(LPVOID)pServerSide))) {
                        pServerSide->ShowTools(FALSE);
                        pServerSide->Release();
                }
                pIPS->Release();
        }
        else {
                RECT r = {0,0,0,0};

                pContainer->BringToFront();

                IBWindow *pWnd = getNegotiator(pDoc->pApplication, pContainer);

                hr = pWnd->SetBorderSpace (&r);
        }

        pSite->OnSetFocus(TRUE);

        // Hitting ESC from some servers only does a UIDeactivate so
        // next time you activate it, it only calls OnUIActivate rather
        // than OnInPlaceActivate. So be sure to keep the flags coherent with
        // the real state.
        //
        IsOpenInPlace = TRUE;
        pSite->SiteShow (FALSE); // assumes hiding an already hidden site is a nop

        return hr;
}

HRESULT _IFUNC BOlePart::OnUIDeactivate (BOOL fUndoable)
{
        // The inplace server is about to remove its own tool/status
        // bars, so ask the client app to restore its adorners
        //
        // Although it isn't in the official docs, the Outline example
        // (in frametls.c) uses the convention where SetBorderSpace(NULL)
        // means show adornments
        //

        HRESULT hr = NOERROR;

        if (IsOpenInPlace && !IsOpenInsideOut) {
                // when ESC key is used to deactivate excel and word
                // OnInPlaceDeactivate is not called.
                pSite->SiteShow (TRUE);    // put the site window back again
                IsOpenInPlace = FALSE;
        }

        // This SetFocus call is to take the focus back from the OLE object's
        // inplace window, since that window has gone away
        //
        pSite->OnSetFocus(TRUE);

        LPOLEINPLACESITE pIPS;
        if (SUCCEEDED(GetParentIPS(&pIPS))) {
                IBPart *pServerSide;
                if (SUCCEEDED(pDoc->QueryInterface(IID_IBPart, &(LPVOID)pServerSide))) {
                        pServerSide->ShowTools(TRUE);
                        pServerSide->Release();
                }
                pIPS->Release();
        }
        else {
                // show their adornments
                //
                IBWindow *pWnd = getNegotiator(pDoc->pApplication, pContainer);

                hr = pWnd->SetBorderSpace (NULL);

                // We're using this call to our own IOleInPlaceFrame implementation
                // to get to OleSetMenuDescriptor(NULL), which is supposed to unhook
                // OLE's message dispatching stuff. We could just call
                // OleSetMenuDescriptor from here, but it's not clear if the server
                // app might call SetMenu with NULL for the same purpose?
                //
                // The pointer-juggling here is intended to make sure we disconnect
                // the OLE message filter from the right window (frame vs. container).
                //
                IOleInPlaceFrame FAR *ipframe = NULL;
                if (S_OK == (GetScode(pDoc->pApplication->IsMDI())))
                        ipframe = pDoc->pService;
                else
                        ipframe = pDoc;
                PIBWindow ipNegWnd = getNegotiator (pDoc->pApplication, pDoc->pContainer);
                hr = ipframe->SetMenu (NULL, NULL, ipNegWnd->GetWindow());
        }

        return hr;
}

HRESULT _IFUNC BOlePart::GetParentIPS(LPOLEINPLACESITE *ppIPS)
{
        HRESULT hr = ResultFromScode(E_NOINTERFACE);
        LPOLEOBJECT pSelfAsServer;
        if (SUCCEEDED(pDoc->QueryInterface(IID_IOleObject, &(LPVOID)pSelfAsServer))) {
                LPOLECLIENTSITE pCS;
                if (SUCCEEDED(pSelfAsServer->GetClientSite(&pCS)) && pCS) {
                        hr = pCS->QueryInterface(IID_IOleInPlaceSite, (LPVOID*)ppIPS);
                        pCS->Release();
                }
                pSelfAsServer->Release();
        }
        return hr;
}

HRESULT _IFUNC BOlePart::GetWindowContext (IOleInPlaceFrame* FAR* ppFrame, IOleInPlaceUIWindow* FAR* ppDoc, LPRECT prcPosRect, LPRECT prcVisRect, LPOLEINPLACEFRAMEINFO pFrameInfo)
{
        LPOLEINPLACESITE pIPSite;
        if (SUCCEEDED(GetParentIPS(&pIPSite)) && (S_OK == pIPSite->CanInPlaceActivate ()))
        {
                // if nested in place active,
                // get parent's window interfaces, but our site size
                //
                HRESULT hr = pIPSite->GetWindowContext(ppFrame, ppDoc, prcPosRect,
                                               prcVisRect, pFrameInfo);
                pIPSite->Release();
                pSite->GetSiteRect (prcPosRect, prcVisRect);
                return hr;
        }

        // The isMdi flag tells the server whether it should negotiate for
        // space on the "frame" window or the "document" window
        //
        IBApplication * pApp = pDoc->pApplication;
        pFrameInfo->fMDIApp = (GetScode(pApp->IsMDI()) == S_OK) ? TRUE : FALSE;

        // Decide which window should do inplace negotiation
        //
        PIBWindow ipNegWnd = NULL;
        if (pFrameInfo->fMDIApp)
                ipNegWnd = pDoc->pApplication;
        else
                ipNegWnd = pContainer;

        // Decide which HWND to give out so toolbar parenting etc is correct
        //
        pFrameInfo->hwndFrame = ipNegWnd->GetWindow();

        // Allow the server to call back for menu negotiation at the document level
        //
        ipNegWnd->QueryInterface (IID_IOleInPlaceFrame, (void**) ppFrame);

        // Allow the server to call back for toolbar negotiation at the document level
        //
        if (pFrameInfo->fMDIApp)
                pDoc->QueryInterface (IID_IOleInPlaceUIWindow, (void**)ppDoc);
        else
                *ppDoc = NULL;

        // Get the accelerator table from the window
        //
        if (!SUCCEEDED(ipNegWnd->GetAccelerators (&pFrameInfo->haccel,
                (int FAR *) (&pFrameInfo->cAccelEntries)))) {
                pFrameInfo->haccel = NULL;
                pFrameInfo->cAccelEntries = 0;
        }

        pSite->GetSiteRect (prcPosRect, prcVisRect);

        return NOERROR;
}

HRESULT _IFUNC BOlePart::DiscardUndoState ()
{
        //
        return ResultFromScode (E_NOTIMPL);
}

HRESULT _IFUNC BOlePart::DeactivateAndUndo ()
{
        // This is called by the server when the user picks Undo immediately
        // after activating the server inplace. We should remove the server's
        // adornments, set focus to ourselves, and pop the undo stack
        //
        HRESULT hr = pInPlaceObject->UIDeactivate();
        if (hr != NOERROR)
                return hr;

        pSite->OnSetFocus(TRUE);
        //
        return NOERROR;
}

HRESULT _IFUNC BOlePart::Scroll (SIZE scrollExtent)
{
        //
        return ResultFromScode (E_NOTIMPL);
}

HRESULT _IFUNC BOlePart::OnPosRectChange (LPCRECT lprcPosRect)
{
        HRESULT hr;
        // The server size is changing, let's try to set it in the container..

        OLEPRINTF1("Entering OnPosRectChange");
        if (SUCCEEDED(pSite->SetSiteRect (lprcPosRect))) {
                RECT posR, clipR;
                pSite->GetSiteRect (&posR, &clipR);
                if (pInPlaceObject) {
                        // use lprcPosRect because the site may not want to really
                        // change the extent.  They could succeed to allow
                        // the in-place window to be free, but when the user
                        // deactivates go back to the same old site.
                        hr = pInPlaceObject->SetObjectRects (lprcPosRect, &clipR);
                }
        }
        else {
                // try to back out of hatch window resize with the fewest possible
                // side effects.  This is best all around solution.  Although visio
                // scales inappropriately, one can live with it.

                RECT rSite, rClip;
                pSite->GetSiteRect(&rSite, &rClip);
                SIZE sizeSite = {rSite.right - rSite.left, rSite.bottom - rSite.top};

                SetPartSize(&sizeSite);
                pInPlaceObject->SetObjectRects (&rSite, &rClip);
                hr = ResultFromScode(E_FAIL);

#if 0
                // Here are the other trials:

                // this approach causes visio to scale inappropriately
                // and excel's extents to be wrong
                SetPartSize(&sizeSite);
                hr = pInPlaceObject->SetObjectRects (&rSite, &rClip);

                // w/o SetPartSize also excel sheet and chart scale inappropriately
                hr = pInPlaceObject->SetObjectRects (&rSite, &rClip);

                // same but chart in MS excel is a bit better
                pInPlaceObject->SetObjectRects (&rSite, &rClip);
                hr = ResultFromScode(E_FAIL);

                // this approach causes servers (chart, visio) to leave hatch window
                SetPartSize(&sizeSite);
                hr = NOERROR;

                // same but chart doesn't leave window, only visio
                SetPartSize(&sizeSite);
                hr = ResultFromScode(E_FAIL);

                // just failing causes both scale factor and window problems
                hr = ResultFromScode(E_FAIL);

                // doing the set part size after plays havoc with scaling
                pInPlaceObject->SetObjectRects (&rSite, &rClip);
                SetPartSize(&sizeSite);
                hr = ResultFromScode(E_FAIL);

#endif
        }
        return hr;
}

HRESULT _IFUNC BOlePart::GetMoniker(IMoniker FAR* FAR* ppMon)
{
        HRESULT hrErr = GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
                OLEWHICHMK_OBJFULL, ppMon);
        return hrErr;
}

HRESULT _IFUNC BOlePart::OnRename(PIBLinkable pCont, LPCOLESTR szName)
{
        HRESULT hrErr = NOERROR;

        pILinkCont = pCont;
        if (pszInstName) {
                delete pszInstName;
                pszInstName = NULL;
        }
        if (szName) {
                pszInstName = new OLECHAR[strlen (szName) +1];
                lstrcpy(pszInstName, szName);
        }

        if (pOleObject) {
                LPMONIKER pMon;
                // Server registers as GetObjectMoniker calls IOleObject::SetMoniker
                GetObjectMoniker(OLEGETMONIKER_UNASSIGN);    // really force!
                pMon = GetObjectMoniker(OLEGETMONIKER_FORCEASSIGN);
                if (pMon)
                        pMon->Release();

                // update window title
                if (pszInstName) {
                        pOleObject->SetHostNames (pDoc->pApplication->GetAppName(),
                        pDoc->pContainer->GetWindowTitle());
                }
        }
        return hrErr;
}

//**************************************************************************
//
// IDropSource Implementation -- Although this is normally implemented by
//                               server objects, we need it on our client
//                               side helper to do "embed from embedding"
//                               which is where you drag/drop or copy/paste
//                               from an existing embedded object.
//
//**************************************************************************

HRESULT _IFUNC BOlePart::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
        SCODE sc;

        if (fEscapePressed)
                sc = DRAGDROP_S_CANCEL;
        else
                if (!(grfKeyState & MK_LBUTTON))
                        sc = DRAGDROP_S_DROP;
                else
                        sc = S_OK;

        return ResultFromScode (sc);

}

HRESULT _IFUNC BOlePart::GiveFeedback(DWORD dwEffect)
{
        //
        //
        return ResultFromScode (DRAGDROP_S_USEDEFAULTCURSORS);
}

//**************************************************************************
//
// CopyFromOriginal() helpers
//
//**************************************************************************

HRESULT _IFUNC BOlePart::AddCachedData (IDataObject *pTarget)
{
        BOOL fAddedMetafile = FALSE;
        LPOLECACHE pOleCache;

        if (SUCCEEDED (pOleObject && pDataObject &&
                pOleObject->QueryInterface (IID_IOleCache, &(LPVOID) pOleCache))) {

                // Get IEnumSTATDATA interface for IOleCache
                //
                LPENUMSTATDATA lpEnumSTATDATA;
                if (pOleCache->EnumCache (&lpEnumSTATDATA) == NOERROR && lpEnumSTATDATA) {

                        // Enumerate all of the cached formats
                        //
                        STATDATA statData;
                        while (lpEnumSTATDATA->Next (1, &statData, NULL) == NOERROR) {
                                OLECHAR name[32];
                                GetClipboardFormatName( statData.formatetc.cfFormat, name, sizeof(name));

                                // For each "cachable" format, try to get a copy of the data
                                //
                                STGMEDIUM stgMedium;
                                if (pDataObject->GetData (&statData.formatetc, &stgMedium) != NOERROR) {
                                        // Data is not available
                                }
                                else
                                        if (stgMedium.pUnkForRelease != NULL) {

                                                // Don't cache data with pUnkForRelease != NULL
                                                //
                                                ::ReleaseStgMedium (&stgMedium);
                                                ::OleStdDeleteTargetDevice (statData.formatetc.ptd);
                                        }
                                        else {

                                                // Transfer to the target
                                                //
                                                pTarget->SetData (&statData.formatetc, &stgMedium, TRUE /*give ownership*/);
                                                if (statData.formatetc.cfFormat == CF_METAFILEPICT)
                                                        fAddedMetafile = TRUE;
                                        }
                        }
                        lpEnumSTATDATA->Release();
                }
                pOleCache->Release();
        }

        // Just about everyone has a metafile in the cache. Paintbrush does not,
        // so we'll add it forcibly here.
        //
        if (!fAddedMetafile) {
                FORMATETC fe;
                fe.cfFormat = CF_METAFILEPICT;
                fe.tymed = TYMED_MFPICT;
                fe.lindex = -1;
                fe.ptd = NULL;

                SIZEL sizel;
                if (NOERROR != pOleObject->GetExtent (dwDrawAspect, &sizel)) {
                        RECT rcpos, rcclip;
                        pSite->GetSiteRect (&rcpos, &rcclip);
                        sizel.cx = MAP_PIX_TO_LOGHIM (rcpos.right - rcpos.left, BOleService::pixPerIn.x);
                        sizel.cy = MAP_PIX_TO_LOGHIM (rcpos.bottom - rcpos.top, BOleService::pixPerIn.y);
                }

                STGMEDIUM sm;
                sm.tymed = TYMED_MFPICT;
                sm.pUnkForRelease = NULL;
                sm.hGlobal = OleStdGetMetafilePictFromOleObject (pOleObject,
                        dwDrawAspect, &sizel, NULL);
                if (sm.hGlobal)
                        pTarget->SetData (&fe, &sm, TRUE);

        }

        return NOERROR;
}

HRESULT _IFUNC BOlePart::AddLinkSourceData (IDataObject *pTarget)
{
        FORMATETC fe;
        STGMEDIUM stgMedium;
        LPMONIKER lpMoniker;

        if (!pLinkObject) {

                // Get moniker from client site
                //
                LPOLECLIENTSITE cSite;
                if (SUCCEEDED (pObjOuter->QueryInterfaceMain (IID_IOleClientSite, &(LPVOID)cSite))) {
                        cSite->GetMoniker (OLEGETMONIKER_TEMPFORUSER, OLEWHICHMK_OBJFULL, &lpMoniker);
                        cSite->Release();
                        if (!lpMoniker)
                                return ResultFromScode (E_FAIL);
                } else
                        return ResultFromScode (E_FAIL);
        }
        else {
                // Get moniker from the link object itself
                //
                if (SUCCEEDED (pOleObject->GetMoniker(OLEGETMONIKER_TEMPFORUSER,
                        OLEWHICHMK_OBJFULL, &lpMoniker))) {
                }
                else
                        return ResultFromScode (E_FAIL);
        }

        // Create a memory based stream to write the moniker to
        //
        LPSTREAM lpStream;
        if (!SUCCEEDED (::CreateStreamOnHGlobal (NULL, TRUE, &lpStream))) {
                lpMoniker->Release();
                return ResultFromScode (E_FAIL);
        }

        // Write the moniker to the stream
        //
        if (!SUCCEEDED (OleSaveToStream (lpMoniker, lpStream))) {
                lpStream->Release();
                return ResultFromScode (E_FAIL);
        }

        // Write the class ID of the document to the stream as well
        //
        CLSID clsid;
        pOleObject->GetUserClassID (&clsid);
        WriteClassStm (lpStream, clsid);

        lpMoniker->Release();

        // Give this stream to the data source (which then owns it)
        //
        _fmemset (&fe, 0, sizeof (fe));
        fe.cfFormat = BOleDocument::oleLinkSrcClipFmt;
        fe.dwAspect = dwDrawAspect;
        fe.lindex = -1;
        fe.tymed = TYMED_ISTREAM;
        stgMedium.tymed = TYMED_ISTREAM;
        stgMedium.pstm = lpStream;
        stgMedium.pUnkForRelease = NULL;
        pTarget->SetData (&fe, &stgMedium, TRUE);

        // Set the object descriptor
        //
        stgMedium.tymed = TYMED_HGLOBAL;
        fe.cfFormat = BOleDocument::oleLinkSrcDescFmt;
        fe.tymed = TYMED_HGLOBAL;
        POINTL unused = {0,0};
        SIZEL unused2 = {0,0};
        stgMedium.hGlobal = OleStdGetObjectDescriptorDataFromOleObject(pOleObject,
                NULL, dwDrawAspect, unused, &unused2);
        stgMedium.pUnkForRelease = NULL;
        pTarget->SetData (&fe, &stgMedium, TRUE);

        return NOERROR;
}

HRESULT _IFUNC BOlePart::AddSiteData (LPDATAOBJECT pTarget)
{
        // Ask the site if they have any data to add.
        //
        // Implementation note: this is a slightly quirky way to do this, but
        // we're not set up to change it at this point. Other ways it could work:
        //
        // 1. Use aggregation so the user calls IBService::Clip with the data
        //    provider supported on their site object. Then they would get to
        //    provide their formats first, and then call back to the data provier
        //    on BOlePart to add the cached formats. Although this seems OLE-like,
        //    abandoned this idea because the aggregation is tricky and our
        //    data format negotiation stuff is different enough from OLE2's to
        //    make it difficult to bottleneck the OLE2 cache.
        //
        // 2. Put a flag in BOlePart which would be set when we're in a CopyFrom-
        //    Original. Then our BOlePart's data provider implementation would
        //    know when to get the data of the real OLE object, and when to get
        //    it from the cache. Abandoned this idea because the idea of more
        //    weird state information isn't attractive.
        //
        // 3. Add a SetFormatData to IBPart or IBDataProvider which could do an
        //    IBDataObject::SetData on the cache directly. Abandoned this idea
        //    because it would be another stub method on the server side.
        //

        HRESULT hr = NOERROR;
        PIBDataProvider pProvider = NULL;
        hr = pSite->QueryInterface (IID_IBDataProvider, &(LPVOID) pProvider);

        // Should never happen because aggregation will always return the
        // data provider on us if the site doesn't support it.
        //
        if (hr != NOERROR)
                return hr;

        if (pProvider != (PIBDataProvider)(IBPart*)this) {
                UINT count = pProvider->CountFormats ();

                HANDLE hData;
                BOleFormat bf;
                FORMATETC fe;
                STGMEDIUM stm;

                for (UINT i = 0; i < count; i++) {

                        // Get the formats the site wants to add
                        //
                        hr = pProvider->GetFormat (i, &bf);
                        if (hr == NOERROR) {
                                hData = pProvider->GetFormatData (&bf);

                                // Convert from BOleFormat to FORMATETC
                                //
                                if (hData) {
                                        fe.cfFormat = bf.fmtId;
                                        fe.ptd = NULL;
                                        fe.dwAspect = dwDrawAspect;
                                        fe.lindex = -1;
                                        switch (bf.fmtId) {
                                                case CF_METAFILEPICT:
                                                        fe.tymed = TYMED_MFPICT;
                                                        break;
                                                case CF_BITMAP:
                                                        fe.tymed = TYMED_GDI;
                                                        break;
                                                default:
                                                        fe.tymed = TYMED_HGLOBAL;
                                        }

                                        // Only formats described by HGLOBALs are allowed here.
                                        // If we had this to do again, we'd just use FORMATETC
                                        //
                                        stm.tymed = fe.tymed;
                                        stm.hGlobal = hData;
                                        stm.pUnkForRelease = NULL;
                                }

                                // Set the FORMATETC and STGMEDIUM into the data cache
                                //
                                hr = pTarget->SetData (&fe, &stm, TRUE);
                        }
                }
        }

        pProvider->Release();
        return hr;
}

// Link source not available
//
void _IFUNC BOlePart::HandleLinkError()
{
        HWND hWnd = pContainer->GetWindow();
        if (hWnd) {

                // Need to retrieve the descriptive name for the container to make the caption
                //
                LPOLESTR pCaption = TEXT( "" );
                HWND hTopWnd = GetTopWindow( hWnd );
                if (OleUIPromptUser (IDD_LINKSOURCEUNAVAILABLE, hTopWnd, pCaption) == ID_PU_LINKS ){

                        // Since a link is unavailable, we ask the user whether they
                        // want to go to the links dialog box. If they do, we invoke it
                        // through the IBOverrideBrowseLinks interface. The reason we
                        // don't call it directly on the BOleDocument is so that a
                        // container app (WPWin) can use their own dialog in this case.
                        //
                        // In general, it isn't nice for Bolero to call our own dialogs
                        // because users are supposed to be able to use their own.
                        //
                        PIBOverrideBrowseLinks pOBL = NULL;
                        HRESULT qiHr = pDoc->QueryInterface (IID_IBOverrideBrowseLinks, &(LPVOID)pOBL);
                        if (SUCCEEDED(qiHr)) { // should never fail since OBL is supported on the doc
                                pOBL->BrowseLinks();
                                pOBL->Release();
                        }
                }
        }
}



