//
//
//**************************************************************************
//
// BOleService.cpp -- Implements the Bolero half of the application-level
//                         object. Provides some system services such as default
//                    dialogs.
//
// Copyright (c) 1993,94 by Borland International, Inc. All rights reserved
//
//**************************************************************************

#include "BOle.h"
#include "BOleSvc.h"
#include "BOleIPS.h"
#include "BOlePart.h"
#include "BOleDoc.h"
#include "BHatch.h"
#include "BOleCMan.h"
#include "BOleFact.h"
#include "BOleData.h"

struct BOleFactNode {
        IBClass *factory;
        BOleFactNode *pNext;
        BOleFactNode( IBClass *f ){ factory = f; f->AddRef(); pNext = NULL; }
        ~BOleFactNode(){ factory->Release(); }
};

HRESULT _IFUNC BOleService::QueryInterfaceMain(REFIID iid, LPVOID *p)
{
        HRESULT hr = ResultFromScode(E_NOINTERFACE);
        *p = NULL;

        // interfaces

        if (SUCCEEDED(hr = IOleInPlaceFrame_QueryInterface(this, iid, p))) {
        }
        else if SUCCEEDED(hr = IBService_QueryInterface(this, iid, p)) {
        }
        // base classes
        else if SUCCEEDED(hr = BOleComponent::  QueryInterfaceMain(iid, p)) {
        }

        // helpers

        return hr;
}

POINT BOleService::pixPerIn = {0,};

BOleService::BOleService (BOleClassManager *pF, IBUnknownMain *pO) : pApp(NULL),
        pActiveDoc(NULL), pFocusedSite(NULL), BOleComponent(pF, pO),
        helpMode (BOLE_HELP_GET), pClipboardData (NULL), pSubclassedWindow(NULL)
{
        clipList = NULL;
        clipCount = 0;
        clipOkToLink = FALSE;
        clipOkToEmbed = FALSE;

        dropList = NULL;
        dropCount = 0;
        dropOkToLink = FALSE;
        dropOkToEmbed = FALSE;

        fUseDropList = FALSE;
        pFirstFactNode = NULL;

}

BOleService::~BOleService ()
{
        FlushClipboardData();

        if (clipList) {
                delete [] clipList;
                clipList = NULL;
        }
        if (dropList) {
                delete [] dropList;
                dropList = NULL;
        }
}

//
//              Called in BOleSite::OnClose to check if the bolesite object
//              is on the clipboard (BOleSite doesn't do delayed rendering.)
//
BOOL BOleService::IsOnClipboard(IDataObject *pPat)
{
        BOOL ret = FALSE;
        if (pClipboardData) {
                if (pPat == pClipboardData) {
                        ret = TRUE;
                }
                else {
                        // if the shadow object is on the clipboard we need to check
                        // if the real data object pointer it holds is the same as
                        // the one passed
                        BOleShadowData * pShadow;
                        if (SUCCEEDED(pClipboardData->QueryInterface(
                                        IID_BOleShadowData, &(LPVOID)pShadow))) {
                                ret = (pShadow->GetRealData() == pPat);
                                pShadow->Release();
                        }
                }
        }
        return ret;
}

void BOleService::FlushClipboardData(BOOL bCheck)
{
  if (pClipboardData) { // one of ours may've been on the clipboard
    if (bCheck) {
      IDataObject *pD;
      if (S_OK == GetScode(OleGetClipboard(&pD))) {
        if (pD == pClipboardData)
          OleFlushClipboard();
        pD->Release();
      }
    }
    else
      OleFlushClipboard();

    pClipboardData = NULL;
  }
}

void BOleService::NotifyClipboardEmptied()
{
        pClipboardData = NULL;
}

HRESULT _IFUNC BOleService::Init( IBApplication *pA )
{

#ifdef _DEBUG
  //  to make sure client's QueryInterface is implemented correctly
  //
  IBApplication *dbgApp = NULL;
  LPOLEINPLACEFRAME dbgIPFrame = NULL;
  if ((!SUCCEEDED(pObjOuter->QueryInterfaceMain(IID_IBApplication, &(LPVOID)dbgApp)))
       || (!SUCCEEDED(pObjOuter->QueryInterfaceMain(IID_IOleInPlaceFrame,&(LPVOID)dbgIPFrame)))) {
#ifdef ANSI
    MessageBoxA(NULL,"QueryInterface Aggregation isn\'t working right",
                "BOleService::Init", MB_OK);
#else
    MessageBox (NULL, OLESTR("QueryInterface Aggregation isn\'t working right"),
                OLESTR("BOleService::Init"), MB_OK);
#endif
  }
  else {
    dbgApp->Release();
    dbgIPFrame->Release();
    dbgApp = NULL;
    dbgIPFrame = NULL;
  }
#endif

  if (!pA)
    return ResultFromScode(E_INVALIDARG);

  pApp = pA;
  BOleHatchWindow::Register (boleInst);

  // The screen is the same resolution for all windows and all processes
  // so we'll just squirrel this away so we don't have to retrieve it
  // every time we need to translate between himetric and pixels
  //
  HWND w = ::GetDesktopWindow ();
  HDC dc = ::GetDC (w);
  if (!w || !dc)
    return ResultFromScode (E_FAIL);
  pixPerIn.x = WIN::GetDeviceCaps(dc, LOGPIXELSX);
  pixPerIn.y = WIN::GetDeviceCaps(dc, LOGPIXELSY);
  ::ReleaseDC (w, dc);

  return NOERROR;
}

void _IFUNC BOleService::SetActiveDoc (BOleDocument FAR *pD)
{
        pActiveDoc = pD;
}

BOleDocument FAR *_IFUNC BOleService::GetActiveDoc ()
{
        return pActiveDoc;
}

// SetHelpMode is used by the other Bolero helper objects when
// one of them gets a broadcast help msg. It can come through
// any of them because help is in IOleWindow, from which they inherit.
//
void _IFUNC BOleService::SetHelpMode (BOOL fEnterMode)
{
        helpMode = fEnterMode ? BOLE_HELP_ENTER : BOLE_HELP_EXIT;
}

// Maybe useful for Activate As
//
void _IFUNC BOleService::UnloadObjects (REFCLSID c)
{
}

UINT _IFUNC BOleService::ExcludeOurselves (LPCLSID FAR *ppClsid)
{
        LPCLSID pClsid = *ppClsid;
        UINT rCnt = 128;
        UINT exclusionCount = 0;

        // Allocate an array of CLSIDs to pass back to the caller (who must delete it)
        //
        pClsid = (LPCLSID) new CLSID [rCnt];
        if (!pClsid) {
                *ppClsid = NULL;
                return 0;
        }

        for( BOleFactNode *pNode = pFirstFactNode; pNode; pNode = pNode->pNext ){

                // Ask the class factory if it's willing to embed in its own container
                // If it isn't willing to do this, we will exclude the CLSID for that
                // factory from the list.
                //
                if( !pNode->factory->AllowEmbedFromSelf() ){

                        pNode->factory->GetClassID(&pClsid[exclusionCount]);
                        exclusionCount++;
                }
        }

        *ppClsid = pClsid;
        return exclusionCount;
}

// ShowHelpButton -- BOleService helper function used by Bolero to find
//                   out if the container app wants us to show the help
//                   button for the dialog boxes. If the container is
//                   generated by Delphi, they might not.
//
BOOL _IFUNC BOleService::ShowHelpButton (BOleDialogHelp whichDialog)
{
        BOOL ret = TRUE;
        PIBOverrideHelpButton pOHB = NULL;

        if (SUCCEEDED(pApp->QueryInterface(IID_IBOverrideHelpButton,
                        &(LPVOID) pOHB))) {
                if (S_OK != GetScode (pOHB->ShowHelpButton (whichDialog)))
                        ret = FALSE;
                pOHB->Release();
        }

        return ret;
}

//*************************************************************************
//
// IService implementation
//
//*************************************************************************

// Convert is supported on the service because (1) the other OLE2 dialogs
// are supported on the IBService and (2) I didn't want to make another
// function on IBPart which doesn't match up on the real server object
//
HRESULT _IFUNC BOleService::ConvertUI (PIBPart pPart, BOOL b, BOleConvertInfo FAR *pInfo)
{
        BOlePart FAR *pPartImpl = NULL;
        pPart->QueryInterface (IID_BOlePart, &(LPVOID) pPartImpl);
        HRESULT hr = pPartImpl->ConvertUI (pApp, b, pInfo);
        pPartImpl->Release();
        return hr;
}

HRESULT _IFUNC BOleService::ConvertGuts (PIBPart pPart, BOOL b, BOleConvertInfo FAR *pInfo)
{
        BOlePart FAR *pPartImpl = NULL;
        pPart->QueryInterface (IID_BOlePart, &(LPVOID) pPartImpl);
        HRESULT hr = pPartImpl->ConvertGuts (b, pInfo);
        pPartImpl->Release();
        return hr;
}

// CreateStorageOnFile is a near-trivial helper function which creates
// an IStorage on a DOS file path. Bolero users can do this for themselves
// if they like, but this is a little protection from the OLE2 headers
//
LPSTORAGE _IFUNC BOleService::CreateStorageOnFile (LPCOLESTR pFileName, BOOL bCreate)
{
        IStorage * pStorage;
        if (bCreate) {
                StgCreateDocfile (
                        pFileName,
                        STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
                        0,
                        &pStorage
                        );
        }
        else {
                StgOpenStorage (
                        pFileName, NULL,
                        STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE,
                        NULL,
                        0,
                        &pStorage
                        );
        }
        return pStorage;
}

void _IFUNC BOleService::OnResize ()
{
        if (pActiveDoc && pActiveDoc->pActivePart) {

                // Ask the active IOleInPlaceActiveObject to recalculate the
                // dimensions of its border adornments in the window, based on
                // the rectangle available in our frame
                //
                RECT r;
                pApp->GetWindowRect (&r);
                pActiveDoc->pActivePart->ResizeBorder (&r, this, TRUE);
        }
}

void _IFUNC BOleService::OnActivate (BOOL fActivate)
{
        if (pActiveDoc && pActiveDoc->pActivePart)
                pActiveDoc->pActivePart->OnFrameWindowActivate (fActivate);
}

HRESULT _IFUNC BOleService::OnSetFocus(BOOL bSet)
{
        // Just send it on to the active doc
        //
        if (pActiveDoc)
                return pActiveDoc->OnSetFocus(bSet);

        // If there's no active doc, there can't be an active object, so
        // the Bolero container app gets to keep focus
        //
        return ResultFromScode (S_OK);
}

HRESULT _IFUNC BOleService::Browse(BOleInitInfo FAR * pInfo)
{
        HRESULT ret;

        // These guys are in member data so we don't have to create big objects
        // on the stack, but we can still have their lifetime extend from
        // IBService::Browse until IBPart::Init, like they couldn't if we
        // just used a stack variable for OLEUIINSERTOBJECT
        //
        dynamicScopePath[0] = 0;
        dynamicScopeClsid = CLSID_NULL;

        OLEUIINSERTOBJECT iod;
        _fmemset(&iod, 0, sizeof(iod));
        iod.cbStruct = sizeof(iod);
        iod.dwFlags  = IOF_SELECTCREATENEW;
        iod.hWndOwner= ::GetActiveWindow();
        iod.lpszFile = dynamicScopePath;
        iod.cchFile  = OLEUI_CCHPATHMAX;
        iod.dwIBApplication = (DWORD) pApp;
        pInfo->pStorage = NULLP;        // User must fill this in after...

        iod.cClsidExclude = ExcludeOurselves (&iod.lpClsidExclude);

        if (ShowHelpButton(BOLE_HELP_BROWSE))
                iod.dwFlags |= IOF_SHOWHELP;

        EnterBOleDialog (TRUE, iod.hHook, iod.hTask);
        UINT stat = OUI::OleUIInsertObject (&iod);
        EnterBOleDialog (FALSE, iod.hHook, iod.hTask);

        if (stat == OLEUI_OK) {
                DWORD dwFlags = iod.dwFlags;
                pInfo->hIcon = (dwFlags & IOF_CHECKDISPLAYASICON) ? (HICON) iod.hMetaPict : NULL;
                if (dwFlags & IOF_SELECTCREATENEW) {
                        pInfo->Where = BOLE_NEW;
                        pInfo->How = BOLE_EMBED;
                        dynamicScopeClsid = iod.clsid;
                        pInfo->whereNew.cid = (BCID) &dynamicScopeClsid;
//

                }
                else if (dwFlags & IOF_SELECTCREATEFROMFILE) {
                        pInfo->Where = BOLE_FILE;
                        pInfo->How = (dwFlags & IOF_CHECKLINK) ? BOLE_LINK : BOLE_EMBED;
                        pInfo->whereFile.pPath = new OLECHAR[strlen(iod.lpszFile) + 1];
                        lstrcpy ((LPOLESTR)pInfo->whereFile.pPath, iod.lpszFile);
                }
                ret = ResultFromScode (S_OK);
        }
        else
                ret = ResultFromScode (S_FALSE);

        if (iod.lpClsidExclude)
                delete [] iod.lpClsidExclude;

        if (iod.hMetaPict) {
;//             OleUIMetafilePictIconFree(iod.hMetaPict);    // clean up metafile
        }

        return ret;
}

// Dialog enabling strategy:
//
// BOleService::OnModalDialog is how Bolero containers and servers
// send dialog state to the outside world
//
// IBApplication::OnModalDialog is how Bolero containers and servers
// receive dialog state from the outside world
//
HRESULT _IFUNC BOleService::OnModalDialog (BOOL fEnable)
{
        HRESULT hr = NOERROR;

        // Propogate this state "down" to the active object
        //
        if (pActiveDoc)
                hr = pActiveDoc->OnModalDialog (fEnable);

        // Propogate this state "up" to the inplace frame
        //
        if (pFocusedSite) {
                PIBApplication tmpapp = NULL;
                if (SUCCEEDED(pFocusedSite->QueryInterface(
                        IID_IBApplication, &(LPVOID) tmpapp))) {
                        hr = tmpapp->OnModalDialog (fEnable);
                        tmpapp->Release();
                }
        }

        // Propogate this state "back" to the application. Since Bolero
        // calls OnModalDialog itself for the OLE2 dialog boxes, the app
        // wouldn't otherwise know we're in a modal dialog
        //
        hr = pApp->OnModalDialog (fEnable);

        return hr;
}

HRESULT _IFUNC BOleService::TranslateAccel (LPMSG pMsg)
{
        HRESULT hR = ResultFromScode( S_FALSE );
        if( pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST ){
                if( pFocusedSite )
                        hR = pFocusedSite->Accelerator( pMsg );
                else if( pActiveDoc )
                        hR = pActiveDoc->TranslateAccel( pMsg );
        }
        return hR;
}

BOleHelp _IFUNC BOleService::HelpMode (BOleHelp newMode)
{
        BOleHelp oldMode = helpMode;
        if (newMode != BOLE_HELP_GET)
                // broadcast to all documents
                if (pActiveDoc && pActiveDoc->pActivePart)
                        pActiveDoc->pActivePart->ContextSensitiveHelp (newMode == BOLE_HELP_ENTER);

        return oldMode;
}

HRESULT _IFUNC BOleService::CanClose()
{
        if (pFactory->ServerCount() == 0)
                return ResultFromScode (S_OK);
        else
                return ResultFromScode (S_FALSE);
}

HRESULT _IFUNC BOleService::BrowseClipboard (BOleInitInfo FAR * pInfo)
{
        if (TRUE == PasteHelper (pInfo, TRUE))
                return ResultFromScode (S_OK);
        else
                return ResultFromScode (S_FALSE);
}

HRESULT _IFUNC BOleService::Paste (BOleInitInfo *pInfo)
{
        if (TRUE == PasteHelper (pInfo, FALSE))
                return ResultFromScode (S_OK);
        else
                return ResultFromScode (S_FALSE);
}

// This function is called when a moniker becomes invalid for an object
// on the clipboard
//
void _IFUNC BOleService::RemoveLinkFromClipList()
{
        for (UINT i=0, from=0; i < clipCount; i++, from++) {
                if ((clipList[i].cfFormat == BOleDocument::oleLinkSrcDescFmt) ||
                        (clipList[i].cfFormat == BOleDocument::oleLinkSrcClipFmt)) {
                        // skip link format
                        //
                        ++from;
                }
                // and shift
                //
                if (i != from && from < clipCount)
                        clipList[i] = clipList[from];
        }
        clipCount -= (from - i);
        clipOkToLink = FALSE;
}

// This is a helper function for IService::Drag and IService::Clip. Its
// purpose is to make a format list for each operation. The lists are
// independent since drag/drop shouldn't disturb the clipboard. So the
// lists are parameterized in this function.
//
BOOL _IFUNC BOleService::FormatHelper (LPFORMATETC FAR* ppList, UINT *pCount,
        BOOL &fLink, BOOL &fEmbed, PIBDataProvider pProvider)
{
        UINT nOffered = 0;

        if (!pProvider) {
                *ppList = NULL;
                *pCount = 0;
                fLink =FALSE;
                fEmbed = FALSE;
                return FALSE;
        }

        nOffered = pProvider->CountFormats();

        if (nOffered == 0) {
                *ppList = NULL;
                *pCount = 0;
                fLink =FALSE;
                fEmbed = FALSE;
                return FALSE;
        }

        // This will disable linking when GetMoniker fails
        // eg. When the container has not been saved.
        if (fLink) {
                IOleObject *pOleObj;
                if (SUCCEEDED(pProvider->QueryInterface(IID_IOleObject,
                                                &(LPVOID) pOleObj))) {
                        IMoniker *pMon;
                        if (SUCCEEDED(pOleObj->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
                                              OLEWHICHMK_OBJFULL, &pMon))) {
                                pMon->Release();
                        }
                        else {
                                fLink = FALSE;
                        }
                        pOleObj->Release();
                }
                else
                        fLink = FALSE;    // forbid linking if IOleObject fails
        }

        if (*ppList)
                delete [] *ppList;

        // The part is responsible for offering all clipboard formats except
        // link source descriptor and object descriptor.
        //
        // Note that the part won't get called in GetFormatData for embeddings
        // (because getting an embedded object means calling IBDataProv::Save) or
        // for CF_METAFILEPICT (because we get that in IBDataProv::Draw)
        //
        *ppList = new FORMATETC [nOffered + 1 + (fLink ? 1 : 0)];
                                                // 1 for object descriptor
                                                // 1 for link source descriptor if linkable
        if (!*ppList)
                return FALSE;

        LPFORMATETC pNext = *ppList;


        // All objects get an object descriptor
        //
//

                pNext->cfFormat = BOleDocument::oleObjectDescFmt;
                pNext->ptd = NULL;
                pNext->dwAspect = DVASPECT_CONTENT;
                pNext->lindex = -1;
                pNext->tymed = TYMED_HGLOBAL;
                pNext++;
//


        if (fLink) {
                pNext->cfFormat = BOleDocument::oleLinkSrcDescFmt;
                pNext->ptd = NULL;
                pNext->dwAspect = DVASPECT_CONTENT;
                pNext->lindex = -1;
                pNext->tymed = TYMED_HGLOBAL;
                pNext++;
        }

        // Enumerate the server's formats for this object into our list
        //
        BOleFormat boleFmt;
        fEmbed = FALSE;
        for (UINT i = 0; i < nOffered; i++) {
                if (NOERROR == pProvider->GetFormat (i, &boleFmt)) {
                        if ((boleFmt.fmtId == BOleDocument::oleLinkSrcDescFmt) && !fLink) {
                                // This will disable linking when GetMoniker fails
                                // eg. When the container has not been saved.
                                continue;
                        }
                        if ((boleFmt.fmtId == BOleDocument::oleLinkSrcClipFmt) && !fLink) {
                                // This will disable linking when GetMoniker fails
                                // eg. When the container has not been saved.
                                continue;
                        }
                        if (boleFmt.fmtId == BOleDocument::oleEmbdObjClipFmt ||
                                boleFmt.fmtId == BOleDocument::oleEmbSrcClipFmt) {
                                // This will enable embedding if the source of the
                                // drag offers an embedding format
                                //
                                fEmbed = TRUE;
                        }
                        pNext->cfFormat = boleFmt.fmtId;
                        pNext->ptd = NULL;
                        pNext->dwAspect = DVASPECT_CONTENT;
                        pNext->lindex = -1;
                        pNext->tymed = boleFmt.fmtMedium & ~BOLE_MED_STATIC;
                        pNext++;
                }
        }
        *pCount = pNext - *ppList;
        return TRUE;
}

HRESULT _IFUNC BOleService::Clip (PIBDataProvider pClonePart, BOOL fLink,
        BOOL fEmbed, BOOL fUseDataCache)
{
        HRESULT hr;

        // If the clone object is null, we're invalidating everything on the
        // clipboard, for example if the app is shutting down or if the app
        // is doing pseudo-delayed-rendering, and the clipboard doesn't reflect
        // the data any more.
        //
        if (!pClonePart) {

                OleSetClipboard (NULL);                 //
                if (clipList) {
                        delete [] clipList;
                        clipList = NULL;
                }
                clipCount = 0;
                clipOkToLink = FALSE;
                clipOkToEmbed = FALSE;
                return ResultFromScode (S_OK);
        }

        // Get the IPart from the part object. Need this so we can enumerate
        // the formats which should be available to the data object.
        //
        PIBDataProvider pProvider = pClonePart;

        FlushClipboardData();

        // Here we have a choice of ways to get a data object to put on the
        // clipboard. The first is a "snapshot" object which can be used for
        // delayed rendering. The second is the original object which won't
        // be protected from delayed rendering.
        //

        if (fUseDataCache) {
                // Get the data object from the snapshot data cache implemented
                // in BOleData
                //
                BOlePart *pBOlePart = NULL;
                hr = pProvider->QueryInterface (IID_BOlePart, &(LPVOID) pBOlePart);
                if (!SUCCEEDED(hr))
                        return hr;

                hr = pBOlePart->CopyFromOriginal (NULL, &pClipboardData);
                pBOlePart->Release();
                if (!SUCCEEDED(hr))
                        return hr;
        }
        else {
                // Get the data object from the part. Because of aggregation this
                // is actually the IDataObject of the BOleSite which the part is
                // associated with.
                //
                IDataObject *pData;
                hr = pClonePart->QueryInterface(IID_IDataObject, &(LPVOID) pData);
                if (!SUCCEEDED(hr))
                        return hr;
                pClipboardData = new BOleShadowData(pFactory, pData);
                pData->Release();

                // Find out how many formats we need. This allows us to malloc a list
                // of FORMATETCs for matching later. The user doesn't have to know
                // about the OLE2 formats, so we need to add those to the count.
                //
                // This list is only used in BOleSite.
                //
                fUseDropList= FALSE;
                clipOkToLink = fLink; // will be cleared by FormatHelper if format not offered
                clipOkToEmbed = fEmbed;// will be cleared by FormatHelper if format not offered

                if (!FormatHelper (&clipList, &clipCount, clipOkToLink, clipOkToEmbed, pProvider))
                        // clipOkToLink is passed by reference
                        hr = ResultFromScode (DV_E_FORMATETC);
        }

        // Use the OLE2 API to put the data object on the clipboard.
        //
        if (SUCCEEDED(hr)) {

                hr = OleSetClipboard( pClipboardData);
                if (!SUCCEEDED(hr))
                        Clip(NULL, FALSE, FALSE, FALSE);


        }
        pClipboardData->Release();

        // pClipboardData is a non ref counted copy of the pointer
        // that is referenced by the clipboard.
        // We release our ref count now to allow BOleShadowData or BOleData
        // to go away as soon as some other app copies to the clipboard. We'll set
        // pClipboardData to NULL when BOleShadowData or BoleData goes away
        // to avoid leaving it dangling.

        return hr;
}

HRESULT _IFUNC BOleService::Drag (PIBDataProvider pPart, BOleDropAction inEffects, BOleDropAction *outEffect)
{
        HRESULT hr = ResultFromScode(S_FALSE);
        DWORD dwOutEffect;
        IDataObject *pDataObj = NULL;
        IDropSource *pDropSrc = NULL;
        if (outEffect)
                *outEffect = BOLE_DROP_NONE;
        BOOL fUnlockRunning = FALSE;

        // Get IDataObject of embedding or server being dragged

        BOlePart *pBOlePart = NULL;

        if (SUCCEEDED(pPart->QueryInterface(IID_IDataObject,&(LPVOID)pDataObj))) {

                // Cache the data formats associated with the server object so
                // when we get called in the IDataObject, we'll have something to
                // compare against in BOleSite
                //
                fUseDropList = TRUE;
                dropOkToLink = (inEffects & BOLE_DROP_LINK) != 0;
                FormatHelper (&dropList, &dropCount, dropOkToLink, dropOkToEmbed, pPart);
                if (!dropOkToLink)              // in case link not possible
                        (int)inEffects &= ~BOLE_DROP_LINK;
        }
        else {
                hr = pPart->QueryInterface (IID_BOlePart, &(LPVOID) pBOlePart);
                if (!SUCCEEDED(hr))
                        return hr;










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





//
                        // This can be a little slow, but I think it should be better
                        // than running the app
                        //
                        pBOlePart->CopyFromOriginal (NULL, &pDataObj);

        }

        if (!pDataObj)
                return hr;

        if (!pDropSrc)
                pPart->QueryInterface(IID_IDropSource,&(LPVOID)pDropSrc);

        if (!pDropSrc) {
                pDataObj->Release();
                return hr;
        }

        // Don't lock if the objects are in another process space
        //
        if (!pBOlePart) {
                CoLockObjectExternal (pDataObj, TRUE, FALSE);
                CoLockObjectExternal (pDropSrc, TRUE, FALSE);
        }

        hr = OLE::DoDragDrop (pDataObj, pDropSrc, (DWORD)inEffects, &dwOutEffect);

        // Fill in the out param if we got one
        //
        if (outEffect)
                *outEffect = (BOleDropAction)dwOutEffect;

        // Make up a return code. The Bolero convention here is NOERROR for
        // success or an error if not. Strangely, the container can fail from
        // IDropTarget::Drop and we still get DRAGDROP_S_DROP. Fortunately we
        // do get the DROPEFFECT_NONE if the container set it, so use it here
        // to produce an error.
        //
        if (GetScode(hr) == DRAGDROP_S_DROP)
                if (dwOutEffect == DROPEFFECT_NONE)
                        hr = ResultFromScode (E_FAIL);
                else
                        hr = ResultFromScode(S_OK);

        if (!pBOlePart) {
                // TRUE releases ref counts held by OLE2 if this is last unlock
                // this is important if the dragging object is a clone
                CoLockObjectExternal (pDataObj, FALSE, TRUE);
                CoLockObjectExternal (pDropSrc, FALSE, TRUE);
        }

        // When we're dragging an embedding, we need to keep the object running.
        // Unlock it after the drag/drop has completed.
        //
        if (pBOlePart) {
                if (fUnlockRunning)
                        OleLockRunning (pBOlePart->pOleObject, FALSE, FALSE);
                pBOlePart->Release();
        }

        pDropSrc->Release();
        pDataObj->Release(); // should delete BOleData if dragging from embedding

        return hr;
}

BOleMenuEnable _IFUNC BOleService::EnableEditMenu (BOleMenuEnable bmeIn,
                                                  PIBDataConsumer pConsumer)
{
        BOleMenuEnable bmeOut = BOleMenuEnable( 0 );
        HRESULT hr = NOERROR;
        LPDATAOBJECT data = NULL;

        if (bmeIn & BOLE_ENABLE_PASTELINK) {

                hr = OleGetClipboard (&data);
                if (S_OK == GetScode(hr)) {

                        hr = OleQueryLinkFromData (data);
                        if (data)
                                data->Release ();

                        // I didn't use SUCCEEDED here because someone returns 0x00030000
                        // which doesn't look like an scode at all. Go figure.
                        //
                        if (GetScode(hr) == S_OK)
                                ( int )bmeOut |= BOLE_ENABLE_PASTELINK;
                }
        }

        // We're borrowing the logic from the dialog which enumerates the links in
        // the document. In this case, we don't so much need to enumerate them;
        // it's enough to know that one exists.
        //
        if (bmeIn & BOLE_ENABLE_BROWSELINKS) {
                if (pActiveDoc && pActiveDoc->EnableBrowseLinks())
                        ( int )bmeOut |= BOLE_ENABLE_BROWSELINKS;
        }

        // The test for whether to enable these last two is really the same
        // criteria: a match between what the container can accept and what the
        // server offers.
        //
        if ((bmeIn & BOLE_ENABLE_PASTE) || (bmeIn & BOLE_ENABLE_BROWSECLIPBOARD)) {

                //
                //
                //
                //




                //
                LPDATAOBJECT pDataObj = NULL;
                OLEUIPASTEENTRY *pOle2UIEntries = NULL;
                BOleFormat *pBOleEntries = NULL;
                UINT nAcceptableFormats = 0;
                CLIPFORMAT i;

                hr = OLE::OleGetClipboard (&pDataObj);
                if (S_OK == GetScode(hr)) {
                        nAcceptableFormats = pConsumer->CountFormats ();
                        if (!nAcceptableFormats)
                                goto cleanup;
                        pOle2UIEntries = new OLEUIPASTEENTRY [nAcceptableFormats];
                        if (!pOle2UIEntries)
                                goto cleanup;
                        pBOleEntries = new BOleFormat[nAcceptableFormats];
                        if (!pBOleEntries)
                                goto cleanup;

                        for (i = 0; i < nAcceptableFormats; i++) {
                                //_fmemset (&pOle2UIEntries[i], 0, sizeof (OLEUIPASTEENTRY));
                                pConsumer->GetFormat (i, &pBOleEntries[i]);

                                pOle2UIEntries[i].fmtetc.cfFormat = pBOleEntries[i].fmtId;
                                pOle2UIEntries[i].fmtetc.ptd = NULL;
                                pOle2UIEntries[i].fmtetc.dwAspect = DVASPECT_CONTENT;
                                pOle2UIEntries[i].fmtetc.tymed = pBOleEntries[i].fmtMedium & ~BOLE_MED_STATIC;
                                pOle2UIEntries[i].fmtetc.lindex = -1;

                                pOle2UIEntries[i].lpstrFormatName =
                                        ( pBOleEntries[i].fmtName[0] ?
                                        pBOleEntries[i].fmtName : TEXT("%s") );
                                pOle2UIEntries[i].lpstrResultText =
                                        ( pBOleEntries[i].fmtResultName[0] ?
                                        pBOleEntries[i].fmtResultName : TEXT("%s") );

                                if (pBOleEntries[i].fmtId == BOleDocument::oleEmbdObjClipFmt ||
                                        pBOleEntries[i].fmtId == BOleDocument::oleLinkSrcClipFmt  ||
                                        pBOleEntries[i].fmtId == BOleDocument::oleEmbSrcClipFmt)

                                        //      PASTEONLY and ENABLEICON are mutually exclusive
                                        //
                                        pOle2UIEntries[i].dwFlags =
                                                OLEUIPASTE_PASTE | OLEUIPASTE_ENABLEICON;
                                else
                                        pOle2UIEntries[i].dwFlags = OLEUIPASTE_PASTEONLY;

                        }

                        i = MatchPriorityClipFormat (pDataObj, pOle2UIEntries, nAcceptableFormats);
                        if (i != 0xFFFF) {
                                ( int )bmeOut |= ( BOLE_ENABLE_PASTE|BOLE_ENABLE_BROWSECLIPBOARD );
                        }

cleanup:
                        // Clean up our various scratch buffers etc
                        //
                        if (pBOleEntries)
                                delete [] pBOleEntries;
                        if (pOle2UIEntries)
                                delete [] pOle2UIEntries;

                        pDataObj->Release();

                }


        }

        return bmeOut;

}

//*************************************************************************
//
// IOleWindow implementation -- base class of OLE2 windows
//
//*************************************************************************

HRESULT _IFUNC BOleService::GetWindow (HWND FAR *phwnd)
{
        *phwnd = pApp->GetWindow();
        return *phwnd != NULL ? NOERROR : ResultFromScode (E_FAIL);
}

HRESULT _IFUNC BOleService::ContextSensitiveHelp (BOOL fEnterMode)
{
        // The BOleService object is responsible for keeping track of Shift-F1
        //
        SetHelpMode (fEnterMode);
        return NOERROR;
}

//*************************************************************************
//
// IOleInPlaceUIWindow implementation
//
//*************************************************************************

HRESULT _IFUNC BOleService::GetBorder (LPRECT prectBorder)
{
        if (SUCCEEDED(pApp->GetWindowRect(prectBorder)))
                return NOERROR;

        return ResultFromScode (INPLACE_E_NOTOOLSPACE);
}

HRESULT _IFUNC BOleService::RequestBorderSpace (LPCRECT prectWidths)
{
        // Ask the application if it can accept the amount of space the
        // server wants on the left, top, right, and bottom of the window
        //
        if (SUCCEEDED(pApp->RequestBorderSpace (prectWidths)))
                return NOERROR;

        return ResultFromScode (INPLACE_E_NOTOOLSPACE);
}

HRESULT _IFUNC BOleService::SetBorderSpace (LPCBORDERWIDTHS prectWidths)
{
        // This seems like it does the same thing as RequestBorderSpace,
        // except that this time the server is really taking the space, so
        // we should get our stuff out of the way.
        //
        //

        if (SUCCEEDED(pApp->SetBorderSpace(prectWidths)))
                return NOERROR;

        return ResultFromScode (OLE_E_INVALIDRECT);
}

HRESULT _IFUNC BOleService::SetActiveObject (IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
{
        // Although it seems a little strange, we're supposed to put the
        // name of the object in our caption bar in the client.
        //
        IBWindow *pWnd;
        if (pActiveDoc)
                pWnd = getNegotiator(pApp, pActiveDoc->pContainer);
        else
                pWnd = pApp;            // just in case

        if (pszObjName)
           pWnd->AppendWindowTitle (pszObjName);
        else
                // Calling SetActiveObject with a null name means the object
                // is deactivating.
                //
                //


                //
                pWnd->RestoreUI();


        // Decrement the ref count of the pointer we're giving up, and
        // bump the ref count of the new pointer we're going to hold
        //
        if (pActiveDoc) {
                if (pActiveDoc->pActivePart)
                        pActiveDoc->pActivePart->Release();
                pActiveDoc->pActivePart = pActiveObject;
                if (pActiveDoc->pActivePart)
                        pActiveDoc->pActivePart->AddRef();
        }

        return NOERROR;
}

//*************************************************************************
//
// IOleInPlaceFrame implementation
//
//*************************************************************************

HRESULT _IFUNC BOleService::InsertMenus (HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
        if (SUCCEEDED(pApp->InsertContainerMenus (hmenuShared,
                (BOleMenuWidths*) lpMenuWidths)))
                return NOERROR;

        return ResultFromScode (E_FAIL);
}

HRESULT _IFUNC BOleService::SetMenu (HMENU hmenuShared, HOLEMENU holeMenu, HWND hwndActiveObject)
{
        HRESULT hr = NOERROR;

        // hmenuShared will be null when the call to BOleService::SetMenu is
        // initiated from BOlePart::OnUIDeactivate. hmenuShared will be valid
        // when the SetMenu call comes from the real server object
        //
        // Even when hmenuShared is NULL, we have to pass the call on to the
        // container because the container has to remove the menu from its
        // frame window.
        //
        hr = pApp->SetFrameMenu (hmenuShared);

        // holemenu will be null when the call to BOleService::SetMenu is
        // initiated from BOlePart::OnUIDeactivate. holeMenu will be valid
        // when the SetMenu call comes from the real server object
        //
        if (SUCCEEDED(hr))
                hr = OleSetMenuDescriptor (holeMenu, pApp->GetWindow(),
                        hwndActiveObject, NULL, NULL);

        return hr;
}

HRESULT _IFUNC BOleService::RemoveMenus (HMENU hmenuShared)
{
        //
        //
        //
        //




        BOOL fNoError = TRUE;

        // Remove container group menus
        //
        while (GetMenuItemCount(hmenuShared))
                fNoError &= RemoveMenu(hmenuShared, 0, MF_BYPOSITION);

        return fNoError ? NOERROR : ResultFromScode(E_FAIL);
}

HRESULT _IFUNC BOleService::SetStatusText (LPCOLESTR statusText)
{
        // Servers use this function to put text in the container's status bar.
        // The server is not supposed to negotiate tool space to put their own
        // status bar at the bottom of the frame window.
        //
        pApp->SetStatusText(statusText);
        return NOERROR;
}

HRESULT _IFUNC BOleService::EnableModeless (BOOL fEnable)
{
        return pApp->OnModalDialog (!fEnable);
}

HRESULT _IFUNC BOleService::TranslateAccelerator (MSG FAR* msg, WORD wID)
{
        HWND oldhwnd = msg->hwnd;
        msg->hwnd = pApp->GetWindow ();
        HRESULT hr = pApp->Accelerator(msg);
        msg->hwnd = oldhwnd;
        return hr;
}

/*
 *              Registration related.
 */

HRESULT _IFUNC BOleService::RegisterClass(LPCOLESTR szProgId, IBClassMgr * pCM, BCID rId,
        BOOL fInProcExe, BOOL fSingleUse)
{
        HRESULT hErr;
        LPUNKNOWN pObjF;
        IBClass *pClass;
        pFactory->ComponentCreate(&pObjF, NULL, cidBOleFactory);
        if (SUCCEEDED(hErr = pObjF->QueryInterface(IID_IBClass, &(LPVOID)pClass))) {
                if (SUCCEEDED(hErr = pClass->Init(fInProcExe, szProgId, pCM, rId))) {
                        if (SUCCEEDED(hErr = pClass->Register(fSingleUse))) {
                                AddClassFactory( pClass );
                        }
                }
                pClass->Release();
        }
        pObjF->Release();
        return hErr;
}

HRESULT _IFUNC BOleService::UnregisterClass(LPCOLESTR szProgId)
{
        return RemoveClassFactory(szProgId);
}


HRESULT BOleService::AddClassFactory(IBClass *pF)
{
        HRESULT hErr = NOERROR;
        BOleFactNode *pNewNode = new BOleFactNode( pF );
        if( !pFirstFactNode )
                pFirstFactNode = pNewNode;
        else {
                for( BOleFactNode *pNode = pFirstFactNode; pNode->pNext; pNode = pNode->pNext );
                pNode->pNext = pNewNode;
        }
        OLEHRES("Registered classid", hErr);
        return hErr;
}

HRESULT BOleService::RemoveClassFactory( LPCOLESTR szProgId)
{
        CLSID cid;
        if (SUCCEEDED( CLSIDFromProgID( szProgId, &cid ) ) ){
                return RemoveClassFactory( cid );
        }
        return ResultFromScode (E_FAIL);
}

HRESULT BOleService::RemoveClassFactory( REFCLSID cid )
{
        BOleFactNode *pNode = pFirstFactNode, *pPrevNode = NULL;
        while( pNode ){
                IBClass *pF = pNode->factory;
                if (pF->IsA(cid)) {
                        pF->Revoke();
                        // Unchain the node
                        if( pPrevNode )
                                pPrevNode->pNext = pNode->pNext;
                        else
                                pFirstFactNode = pNode->pNext;
                        BOleFactNode *pDel = pNode;
                        pNode = pNode->pNext;
                        delete pDel;                    // releases the factory
                } else {
                        pPrevNode = pNode;
                        pNode = pNode->pNext;
                }
        }
        return NOERROR;
}


HRESULT BOleService::FindClassFactory(LPCOLESTR szProgId, IBClass **pF)
{
        CLSID cid;
        if( SUCCEEDED( CLSIDFromProgID(szProgId, &cid ) ) ){
                return FindClassFactory( cid, pF );
        }
        return ResultFromScode (E_FAIL);
}

HRESULT BOleService::FindClassFactory(REFCLSID cid, IBClass **ppF)
{
        *ppF = NULL;
        for( BOleFactNode *pNode = pFirstFactNode; pNode; pNode = pNode->pNext ){
                if( pNode->factory->IsA(cid) ) {
                        *ppF = pNode->factory;
                        (*ppF)->AddRef();
                        break;
                }
        }
        return (*ppF) ? NOERROR : ResultFromScode(E_FAIL);
}





