File:  [WindowsNT SDKs] / mstools / ole20 / samples / outline / clipbrd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:24:38 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntsdk-jul-1993, HEAD
Microsoft Windows NT Build 511 (SDK Final Release) 07-24-1993

/*************************************************************************
** 
**    OLE 2 Sample Code
**    
**    clipbrd.c
**    
**    This file contains the major interfaces, methods and related support
**    functions for implementing clipboard data transfer. The code
**    contained in this file is used by BOTH the Container and Server
**    (Object) versions of the Outline sample code.
**    (see file dragdrop.c for Drag/Drop support implementation)
**    
**    OleDoc Object
**      exposed interfaces:
**          IDataObject
**    
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/

#include "outline.h"
#include <enumfetc.h>
#include <geticon.h>

OLEDBGDATA

extern LPOUTLINEAPP             g_lpApp;

// REVIEW: should use string resource for messages
char ErrMsgPasting[] = "Could not paste data from clipboard!";
char ErrMsgBadFmt[] = "Invalid format selected!";
char ErrMsgPasteFailed[] = "Could not paste data from clipboard!";



/*************************************************************************
** OleDoc::IDataObject interface implementation
*************************************************************************/

STDMETHODIMP OleDoc_DataObj_QueryInterface (
        LPDATAOBJECT        lpThis,
        REFIID              riid,
        LPVOID FAR*         lplpvObj
)
{
    LPOLEDOC lpOleDoc = ((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;

    return OleDoc_QueryInterface((LPOLEDOC)lpOleDoc, riid, lplpvObj);
}


STDMETHODIMP_(ULONG) OleDoc_DataObj_AddRef(LPDATAOBJECT lpThis)
{
    LPOLEDOC lpOleDoc = ((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;

    OleDbgAddRefMethod(lpThis, "IDataObject");

    return OleDoc_AddRef((LPOLEDOC)lpOleDoc);
}


STDMETHODIMP_(ULONG) OleDoc_DataObj_Release (LPDATAOBJECT lpThis)
{
    LPOLEDOC lpOleDoc = ((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;

    OleDbgReleaseMethod(lpThis, "IDataObject");

    return OleDoc_Release((LPOLEDOC)lpOleDoc);
}


STDMETHODIMP OleDoc_DataObj_GetData (
        LPDATAOBJECT        lpThis,
        LPFORMATETC         lpFormatetc,
        LPSTGMEDIUM         lpMedium
)
{
    LPOLEDOC lpOleDoc = ((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    HRESULT hrErr;

    OLEDBG_BEGIN2("OleDoc_DataObj_GetData\r\n")

#if defined( OLE_SERVER )
    // Call OLE Server specific version of this function
    hrErr = ServerDoc_GetData((LPSERVERDOC)lpOleDoc, lpFormatetc, lpMedium);

#elif defined( OLE_CNTR )
    // Call OLE Container specific version of this function
    hrErr = ContainerDoc_GetData(
            (LPCONTAINERDOC)lpOleDoc,
            lpFormatetc,
            lpMedium
    );
#endif

    OLEDBG_END2
    return hrErr;
}


STDMETHODIMP OleDoc_DataObj_GetDataHere (
        LPDATAOBJECT        lpThis,
        LPFORMATETC         lpFormatetc,
        LPSTGMEDIUM         lpMedium
)
{
    LPOLEDOC lpOleDoc = ((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    HRESULT hrErr;

    OLEDBG_BEGIN2("OleDoc_DataObj_GetDataHere\r\n")

#if defined( OLE_SERVER )
    // Call OLE Server specific version of this function
    hrErr = ServerDoc_GetDataHere(
            (LPSERVERDOC)lpOleDoc,
            lpFormatetc,
            lpMedium
    );

#elif defined( OLE_CNTR )
    // Call OLE Container specific version of this function
    hrErr = ContainerDoc_GetDataHere(
            (LPCONTAINERDOC)lpOleDoc,
            lpFormatetc,
            lpMedium
    );
#endif

    OLEDBG_END2
    return hrErr;
}


STDMETHODIMP OleDoc_DataObj_QueryGetData (
        LPDATAOBJECT        lpThis,
        LPFORMATETC         lpFormatetc
)
{
    LPOLEDOC lpOleDoc = ((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    HRESULT hrErr;

    OLEDBG_BEGIN2("OleDoc_DataObj_QueryGetData\r\n");

#if defined( OLE_SERVER )
    // Call OLE Server specific version of this function
    hrErr = ServerDoc_QueryGetData((LPSERVERDOC)lpOleDoc, lpFormatetc);

#elif defined( OLE_CNTR )
    // Call OLE Container specific version of this function
    hrErr = ContainerDoc_QueryGetData((LPCONTAINERDOC)lpOleDoc, lpFormatetc);
#endif

    OLEDBG_END2
    return hrErr;
}


STDMETHODIMP OleDoc_DataObj_GetCanonicalFormatEtc(
        LPDATAOBJECT        lpThis,
        LPFORMATETC         lpformatetc,
        LPFORMATETC         lpformatetcOut
)
{
    OleDbgOut2("OleDoc_DataObj_GetCanonicalFormatEtc\r\n");

    return ResultFromScode(E_NOTIMPL);
}


STDMETHODIMP OleDoc_DataObj_SetData (
        LPDATAOBJECT    lpThis,
        LPFORMATETC     lpFormatetc,
        LPSTGMEDIUM     lpMedium,
        BOOL            fRelease
)
{
    LPOLEDOC lpOleDoc = ((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
    LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    SCODE sc;

    OLEDBG_BEGIN2("OleDoc_DataObj_SetData\r\n")

    /* OLE2NOTE: a document that is used to transfer data (either via
    **    the clipboard or drag/drop) does NOT accept SetData on ANY
    **    format!
    */
    if (lpOutlineDoc->m_fDataTransferDoc) {
        sc = E_FAIL;
        goto error;
    }

#if defined( OLE_SERVER )
    {
        HRESULT hrErr;

        if (lpFormatetc->cfFormat == lpOutlineApp->m_cfOutline) {

            OLEDBG_BEGIN2("ServerDoc_SetData: CF_OUTLINE\r\n");

            OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );
            OutlineDoc_ClearAllLines(lpOutlineDoc);
            OutlineDoc_PasteOutlineData(lpOutlineDoc,lpMedium->u.hGlobal,-1);
            OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

            OLEDBG_END3

        } else if (lpFormatetc->cfFormat == CF_TEXT) {

            OLEDBG_BEGIN2("ServerDoc_SetData: CF_TEXT\r\n");

            OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );
            OutlineDoc_ClearAllLines(lpOutlineDoc);
            OutlineDoc_PasteTextData(lpOutlineDoc,lpMedium->u.hGlobal,-1);
            OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

            OLEDBG_END3

        } else {
            sc = DATA_E_FORMATETC;
            goto error;
        }

        OLEDBG_END2
        return hrErr;
    }

#elif defined( OLE_CNTR )
    {
        /* the Container-Only version of Outline does NOT offer
        **    IDataObject interface from its User documents. this is
        **    required by objects which can be embedded or linked. the
        **    Container-only app only allows linking to its contained
        **    objects, NOT the data of the container itself.
        */
        OleDbgAssertSz(0, "User documents do NOT support IDataObject\r\n");
        sc = E_NOTIMPL;
        goto error;
    }
#endif

error:
    OLEDBG_END2
    return ResultFromScode(sc);

}


STDMETHODIMP OleDoc_DataObj_EnumFormatEtc(
        LPDATAOBJECT            lpThis,
        DWORD                   dwDirection,
        LPENUMFORMATETC FAR*    lplpenumFormatEtc
)
{
    LPOLEDOC lpOleDoc=((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    HRESULT hrErr;

    OLEDBG_BEGIN2("OleDoc_DataObj_EnumFormatEtc\r\n")

    /* OLE2NOTE: we must make sure to set all out parameters to NULL. */
    *lplpenumFormatEtc = NULL;

#if defined( OLE_SERVER )
    /* OLE2NOTE: a user document only needs to enumerate the static list
    **    of formats that are registered for our app in the
    **    registration database. OLE provides a default enumerator
    **    which enumerates from the registration database. this default
    **    enumerator is requested by returning OLE_S_USEREG. it is NOT
    **    required that a user document (ie. non-DataTransferDoc)
    **    enumerate the OLE formats: CF_LINKSOURCE, CF_EMBEDSOURCE, or
    **    CF_EMBEDDEDOBJECT. 
    */
    if (! ((LPOUTLINEDOC)lpOleDoc)->m_fDataTransferDoc) 
        return ResultFromScode(OLE_S_USEREG);

    // Call OLE Server specific version of this function
    hrErr = ServerDoc_EnumFormatEtc(
            (LPSERVERDOC)lpOleDoc,
            dwDirection,
            lplpenumFormatEtc
    );
#elif defined( OLE_CNTR )
    // Call OLE Container specific version of this function
    hrErr = ContainerDoc_EnumFormatEtc(
            (LPCONTAINERDOC)lpOleDoc,
            dwDirection,
            lplpenumFormatEtc
    );
#endif

    OLEDBG_END2
    return hrErr;
}


STDMETHODIMP OleDoc_DataObj_DAdvise(
        LPDATAOBJECT        lpThis,
        FORMATETC FAR*      lpFormatetc,
        DWORD               advf,
        LPADVISESINK        lpAdvSink,
        DWORD FAR*          lpdwConnection
)
{
    LPOLEDOC lpOleDoc=((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
    SCODE sc;

    OLEDBG_BEGIN2("OleDoc_DataObj_DAdvise\r\n")

    /* OLE2NOTE: we must make sure to set all out parameters to NULL. */
    *lpdwConnection = 0;

    /* OLE2NOTE: a document that is used to transfer data (either via
    **    the clipboard or drag/drop) does NOT support Advise notifications.
    */
    if (lpOutlineDoc->m_fDataTransferDoc) {
        sc = OLE_E_ADVISENOTSUPPORTED;
        goto error;
    }

#if defined( OLE_SERVER )
    {
        HRESULT hrErr;
        LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;

        if (lpServerDoc->m_lpDataAdviseHldr == NULL &&
            CreateDataAdviseHolder(&lpServerDoc->m_lpDataAdviseHldr) 
                                                                != NOERROR) {
                sc = E_OUTOFMEMORY;
                goto error;
        }

        OLEDBG_BEGIN2("IDataAdviseHolder::Advise called\r\n");
        hrErr = lpServerDoc->m_lpDataAdviseHldr->lpVtbl->Advise(
                lpServerDoc->m_lpDataAdviseHldr,
                (LPDATAOBJECT)&lpOleDoc->m_DataObject,
                lpFormatetc,
                advf,
                lpAdvSink,
                lpdwConnection
        );
        OLEDBG_END2

        OLEDBG_END2
        return hrErr;
    }

#elif defined( OLE_CNTR )
    {
        /* the Container-Only version of Outline does NOT offer
        **    IDataObject interface from its User documents. this is
        **    required by objects which can be embedded or linked. the
        **    Container-only app only allows linking to its contained
        **    objects, NOT the data of the container itself.
        */
        OleDbgAssertSz(0, "User documents do NOT support IDataObject\r\n");
        sc = E_NOTIMPL;
        goto error;
    }
#endif

error:
    OLEDBG_END2
    return ResultFromScode(sc);
}


STDMETHODIMP OleDoc_DataObj_DUnadvise(LPDATAOBJECT lpThis, DWORD dwConnection)
{
    LPOLEDOC lpOleDoc=((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
    SCODE sc;

    OLEDBG_BEGIN2("OleDoc_DataObj_DUnadvise\r\n")

    /* OLE2NOTE: a document that is used to transfer data (either via
    **    the clipboard or drag/drop) does NOT support Advise notifications.
    */
    if (lpOutlineDoc->m_fDataTransferDoc) {
        sc = OLE_E_ADVISENOTSUPPORTED;
        goto error;
    }

#if defined( OLE_SERVER )
    {
        LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
        HRESULT hrErr;

        if (lpServerDoc->m_lpDataAdviseHldr == NULL) {
            sc = E_FAIL;
            goto error;
        }

        OLEDBG_BEGIN2("IDataAdviseHolder::Unadvise called\r\n");
        hrErr = lpServerDoc->m_lpDataAdviseHldr->lpVtbl->Unadvise(
                lpServerDoc->m_lpDataAdviseHldr,
                dwConnection
        );
        OLEDBG_END2

        OLEDBG_END2
        return hrErr;
    }
#elif defined( OLE_CNTR )
    {
        /* the Container-Only version of Outline does NOT offer
        **    IDataObject interface from its User documents. this is
        **    required by objects which can be embedded or linked. the
        **    Container-only app only allows linking to its contained
        **    objects, NOT the data of the container itself.
        */
        OleDbgAssertSz(0, "User documents do NOT support IDataObject\r\n");
        sc = E_NOTIMPL;
        goto error;
    }
#endif

error:
    OLEDBG_END2
    return ResultFromScode(sc);
}


STDMETHODIMP OleDoc_DataObj_EnumDAdvise(
        LPDATAOBJECT        lpThis,
        LPENUMSTATDATA FAR* lplpenumAdvise
)
{
    LPOLEDOC lpOleDoc=((struct CDocDataObjectImpl FAR*)lpThis)->lpOleDoc;
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
    SCODE sc;

    OLEDBG_BEGIN2("OleDoc_DataObj_EnumDAdvise\r\n")

    /* OLE2NOTE: we must make sure to set all out parameters to NULL. */
    *lplpenumAdvise = NULL;

    /* OLE2NOTE: a document that is used to transfer data (either via
    **    the clipboard or drag/drop) does NOT support Advise notifications.
    */
    if (lpOutlineDoc->m_fDataTransferDoc) {
        sc = OLE_E_ADVISENOTSUPPORTED;
        goto error;
    }

#if defined( OLE_SERVER )
    {
        LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
        HRESULT hrErr;

        if (lpServerDoc->m_lpDataAdviseHldr == NULL) {
            sc = E_FAIL;
            goto error;
        }

        OLEDBG_BEGIN2("IDataAdviseHolder::EnumAdvise called\r\n");
        hrErr = lpServerDoc->m_lpDataAdviseHldr->lpVtbl->EnumAdvise(
                lpServerDoc->m_lpDataAdviseHldr,
                lplpenumAdvise
        );
        OLEDBG_END2

        OLEDBG_END2
        return hrErr;
    }
#elif defined( OLE_CNTR )
    {
        /* the Container-Only version of Outline does NOT offer
        **    IDataObject interface from its User documents. this is
        **    required by objects which can be embedded or linked. the
        **    Container-only app only allows linking to its contained
        **    objects, NOT the data of the container itself.
        */
        OleDbgAssertSz(0, "User documents do NOT support IDataObject\r\n");
        sc = E_NOTIMPL;
        goto error;
    }
#endif

error:
    OLEDBG_END2
    return ResultFromScode(sc);
}



/*************************************************************************
** OleDoc Supprt Functions common to both Container and Server versions
*************************************************************************/

/* OleDoc_PasteCommand
** -------------------
**    Paste default format data from the clipboard.
**    In this function we choose the highest fidelity format that the
**    source clipboard IDataObject* offers that we understand.
**
**    OLE2NOTE: clipboard handling in an OLE 2.0 application is
**    different than normal Windows clipboard handling. Data from the
**    clipboard is retieved by getting the IDataObject* pointer
**    returned by calling OleGetClipboard.
*/
void OleDoc_PasteCommand(LPOLEDOC lpOleDoc)
{
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
    LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    LPDATAOBJECT lpClipboardDataObj = NULL;
    BOOL fLink = FALSE;
    BOOL fLocalDataObj = FALSE;
    BOOL fStatus;
    HRESULT hrErr;

    hrErr = OleGetClipboard((LPDATAOBJECT FAR*)&lpClipboardDataObj);
    if (hrErr != NOERROR)
        return;     // Clipboard seems to be empty or can't be accessed

    OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );

    /* check if the data on the clipboard is local to our application
    **    instance.
    */
    if (lpOutlineApp->m_lpClipboardDoc) {
        LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineApp->m_lpClipboardDoc;
        if (lpClipboardDataObj == (LPDATAOBJECT)&lpOleDoc->m_DataObject)
            fLocalDataObj = TRUE;
    }

    fStatus = OleDoc_PasteFromData(
            lpOleDoc,
            lpClipboardDataObj,
            fLocalDataObj,
            fLink
    );

    OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

    if (! fStatus)
        OutlineApp_ErrorMessage(g_lpApp,"Could not paste data from clipboard!");

    if (lpClipboardDataObj)
        OleStdRelease((LPUNKNOWN)lpClipboardDataObj);
}


/* OleDoc_PasteSpecialCommand
** --------------------------
**    Allow the user to paste data in a particular format from the
**    clipboard. The paste special command displays a dialog to the
**    user that allows him to choose the format to be pasted from the
**    list of formats available.
**
**    OLE2NOTE: the PasteSpecial dialog is one of the standard OLE 2.0
**    UI dialogs for which the dialog is implemented and in the OLE2UI
**    library.
**
**    OLE2NOTE: clipboard handling in an OLE 2.0 application is
**    different than normal Windows clipboard handling. Data from the
**    clipboard is retieved by getting the IDataObject* pointer
**    returned by calling OleGetClipboard.
*/
void OleDoc_PasteSpecialCommand(LPOLEDOC lpOleDoc)
{
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
    LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
    LPDATAOBJECT lpClipboardDataObj = NULL;
    CLIPFORMAT cfFormat;
    int nFmtEtc;
    UINT uInt;
    BOOL fLink = FALSE;
    BOOL fLocalDataObj = FALSE;
    BOOL fStatus;
    HRESULT hrErr;
    OLEUIPASTESPECIAL ouiPasteSpl;
    BOOL fDisplayAsIcon;

    hrErr = OleGetClipboard((LPDATAOBJECT FAR*)&lpClipboardDataObj);
    if (hrErr != NOERROR)
        return;     // Clipboard seems to be empty or can't be accessed

    /* check if the data on the clipboard is local to our application
    **    instance.
    */
    if (lpOutlineApp->m_lpClipboardDoc) {
        LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineApp->m_lpClipboardDoc;
        if (lpClipboardDataObj == (LPDATAOBJECT)&lpOleDoc->m_DataObject)
            fLocalDataObj = TRUE;
    }

    /* Display the PasteSpecial dialog and allow the user to select the
    **    format to paste.
    */
    ouiPasteSpl.cbStruct = sizeof(OLEUIPASTESPECIAL);       //Structure Size
    ouiPasteSpl.dwFlags =  PSF_SELECTPASTE | PSF_SHOWHELP;                 //IN-OUT:  Flags
    ouiPasteSpl.hWndOwner = lpOutlineApp->m_lpDoc->m_hWndDoc;        //Owning window
    ouiPasteSpl.lpszCaption = "Paste Special";    //Dialog caption bar contents
    ouiPasteSpl.lpfnHook = NULL;                  //Hook callback
    ouiPasteSpl.lCustData = 0;                    //Custom data to pass to hook
    ouiPasteSpl.hInstance = NULL;      //Instance for customized template name
    ouiPasteSpl.lpszTemplate = NULL;   //Customized template name
    ouiPasteSpl.hResource = NULL;      //Customized template handle

    ouiPasteSpl.arrPasteEntries = lpOleApp->m_arrPasteEntries;
    ouiPasteSpl.cPasteEntries = lpOleApp->m_nPasteEntries;
    ouiPasteSpl.lpSrcDataObj = lpClipboardDataObj;
    ouiPasteSpl.arrLinkTypes = lpOleApp->m_arrLinkTypes;
    ouiPasteSpl.cLinkTypes = lpOleApp->m_nLinkTypes;


    OLEDBG_BEGIN3("OleUIPasteSpecial called\r\n")
    uInt = OleUIPasteSpecial(&ouiPasteSpl);
    OLEDBG_END3

    fDisplayAsIcon =
            (ouiPasteSpl.dwFlags & PSF_CHECKDISPLAYASICON ? TRUE : FALSE);

    if (uInt == OLEUI_OK) {
        nFmtEtc = ouiPasteSpl.nSelectedIndex;
        fLink =  ouiPasteSpl.fLink;

        if (nFmtEtc < 0 || nFmtEtc >= lpOleApp->m_nPasteEntries) {
            OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgBadFmt);
            goto error;
        }

        OutlineDoc_SetRedraw ( lpOutlineDoc, FALSE );

        cfFormat = lpOleApp->m_arrPasteEntries[nFmtEtc].fmtetc.cfFormat;

        fStatus = OleDoc_PasteFormatFromData(
                lpOleDoc,
                cfFormat,
                lpClipboardDataObj,
                fLocalDataObj,
                fLink,
                fDisplayAsIcon,
                ouiPasteSpl.hMetaPict
        );

        OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

        if (! fStatus) {
            OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgPasteFailed);
            goto error;
        }

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

    }

    if (lpClipboardDataObj)
        OleStdRelease((LPUNKNOWN)lpClipboardDataObj);

    return;

error:

    if (lpClipboardDataObj)
        OleStdRelease((LPUNKNOWN)lpClipboardDataObj);

    if (uInt == OLEUI_OK && ouiPasteSpl.hMetaPict)
        // clean up metafile
        OleUIMetafilePictIconFree(ouiPasteSpl.hMetaPict);
}


/* OleDoc_PasteFromData
** --------------------
**
**    Paste data from an IDataObject*. The IDataObject* may come from
**    the clipboard (GetClipboard) or from a drag/drop operation.
**    In this function we choose the best format that we prefer.
**
**    Returns TRUE if data was successfully pasted.
**            FALSE if data could not be pasted.
*/

BOOL OleDoc_PasteFromData(
        LPOLEDOC            lpOleDoc,
        LPDATAOBJECT        lpSrcDataObj,
        BOOL                fLocalDataObj,
        BOOL                fLink
)
{
    LPOLEAPP        lpOleApp = (LPOLEAPP)g_lpApp;
    CLIPFORMAT      cfFormat;
    BOOL            fDisplayAsIcon = FALSE;
    HGLOBAL         hMem = NULL;
    HGLOBAL         hMetaPict = NULL;
    STGMEDIUM       medium;

    if (fLink) {
#if defined( OLE_SERVER )

        return FALSE;       // server version of app does NOT support links

#elif defined( OLE_CNTR )

        // container version of app only supports OLE object type links
        cfFormat = lpOleApp->m_cfLinkSource;
#endif

    } else {

        int nFmtEtc;

        nFmtEtc = OleStdGetPriorityClipboardFormat(
                lpSrcDataObj,
                lpOleApp->m_arrPasteEntries,
                lpOleApp->m_nPasteEntries
        );

        if (nFmtEtc < 0)
            return FALSE;   // there is no format we like

        cfFormat = lpOleApp->m_arrPasteEntries[nFmtEtc].fmtetc.cfFormat;
    }

    /* OLE2NOTE: we need to check what dwDrawAspect is being
    **    transfered. if the data is an object that is displayed as an
    **    icon in the source, then we want to keep it as an icon. the
    **    aspect the object is displayed in at the source is transfered
    **    via the CF_OBJECTDESCRIPTOR format for a Paste operation. 
    */
    if (hMem = OleStdGetData(
            lpSrcDataObj,
            lpOleApp->m_cfObjectDescriptor,
            NULL,
            DVASPECT_CONTENT,
            (LPSTGMEDIUM)&medium)) {
        LPOBJECTDESCRIPTOR lpOD = GlobalLock(hMem);
        fDisplayAsIcon = (lpOD->dwDrawAspect == DVASPECT_ICON ? TRUE : FALSE);
        GlobalUnlock(hMem);
        ReleaseStgMedium((LPSTGMEDIUM)&medium);     // equiv to GlobalFree

        if (fDisplayAsIcon) {
            hMetaPict = OleStdGetData(
                    lpSrcDataObj,
                    CF_METAFILEPICT,
                    NULL,
                    DVASPECT_ICON,
                    (LPSTGMEDIUM)&medium
            );
            if (hMetaPict == NULL) 
                fDisplayAsIcon = FALSE; // give up; failed to get icon MFP
        }
    }

    return OleDoc_PasteFormatFromData(
            lpOleDoc,
            cfFormat,
            lpSrcDataObj,
            fLocalDataObj,
            fLink,
            fDisplayAsIcon,
            hMetaPict
    );

    if (hMetaPict)
        ReleaseStgMedium((LPSTGMEDIUM)&medium);  // properly free METAFILEPICT
}


/* OleDoc_PasteFormatFromData
** --------------------------
**
**    Paste a particular data format from a IDataObject*. The
**    IDataObject* may come from the clipboard (GetClipboard) or from a
**    drag/drop operation.
**
**    Returns TRUE if data was successfully pasted.
**            FALSE if data could not be pasted.
*/

BOOL OleDoc_PasteFormatFromData(
        LPOLEDOC            lpOleDoc,
        CLIPFORMAT          cfFormat,
        LPDATAOBJECT        lpSrcDataObj,
        BOOL                fLocalDataObj,
        BOOL                fLink,
        BOOL                fDisplayAsIcon,
        HGLOBAL             hMetaPict
)
{
#if defined( OLE_SERVER )

    /* call server specific version of the function. */
    return ServerDoc_PasteFormatFromData(
            (LPSERVERDOC)lpOleDoc,
            cfFormat,
            lpSrcDataObj,
            fLocalDataObj,
            fLink
    );

#elif defined( OLE_CNTR )

    /* call container specific version of the function. */
    return ContainerDoc_PasteFormatFromData(
            (LPCONTAINERDOC)lpOleDoc,
            cfFormat,
            lpSrcDataObj,
            fLocalDataObj,
            fLink,
            fDisplayAsIcon,
            hMetaPict
    );

#endif
}


/* OleDoc_QueryPasteFromData
** -------------------------
**
**    Check if the IDataObject* offers data in a format that we can
**    paste. The IDataObject* may come from the clipboard
**    (GetClipboard) or from a drag/drop operation.
**
**    Returns TRUE if paste can be performed
**            FALSE if paste is not possible.
*/

BOOL OleDoc_QueryPasteFromData(
        LPOLEDOC            lpOleDoc,
        LPDATAOBJECT        lpSrcDataObj,
        BOOL                fLink
)
{
#if defined( OLE_SERVER )

    return ServerDoc_QueryPasteFromData(
            (LPSERVERDOC) lpOleDoc,
            lpSrcDataObj,
            fLink
    );

#elif defined( OLE_CNTR )

    return ContainerDoc_QueryPasteFromData(
            (LPCONTAINERDOC) lpOleDoc,
            lpSrcDataObj,
            fLink
    );

#endif
}


/* OleDoc_GetExtent
 * ----------------
 *
 *      Get the extent (width, height) of the entire document in Himetric.
 */
void OleDoc_GetExtent(LPOLEDOC lpOleDoc, LPSIZEL lpsizel)
{
    LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;

    LineList_CalcSelExtentInHimetric(lpLL, NULL, lpsizel);
}


/* OleDoc_GetObjectDescriptorData
 * ------------------------------
 *
 * Return a handle to an object's data in CF_OBJECTDESCRIPTOR form
 *
 */
HGLOBAL OleDoc_GetObjectDescriptorData(LPOLEDOC lpOleDoc, LPLINERANGE lplrSel)
{
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
    
    /* Only our data transfer doc renders CF_OBJECTDESCRIPTOR */
    OleDbgAssert(lpOutlineDoc->m_fDataTransferDoc);

#if defined( OLE_SERVER )
    {
        LPSERVERDOC   lpServerDoc = (LPSERVERDOC)lpOleDoc;
        SIZEL         sizel;
        POINTL        pointl;
        LPSTR         lpszSrcOfCopy = NULL;
        IBindCtx  FAR *pbc = NULL;
        HGLOBAL       hObjDesc;
        DWORD         dwStatus = 0;
        LPOUTLINEDOC  lpSrcDocOfCopy=(LPOUTLINEDOC)lpOleDoc->m_lpSrcDocOfCopy;
        LPMONIKER lpSrcMonikerOfCopy = ServerDoc_GetSelFullMoniker(
                (LPSERVERDOC)lpOleDoc->m_lpSrcDocOfCopy,
                &lpServerDoc->m_lrSrcSelOfCopy,
                GETMONIKER_TEMPFORUSER
        );

        SvrDoc_OleObj_GetMiscStatus(
                (LPOLEOBJECT)&lpServerDoc->m_OleObject, 
                DVASPECT_CONTENT, 
                &dwStatus
        );

        OleDoc_GetExtent(lpOleDoc, &sizel);
        pointl.x = pointl.y = 0;

        if (lpSrcMonikerOfCopy) {
            CreateBindCtx(0, (LPBC FAR*)&pbc);
            lpSrcMonikerOfCopy->lpVtbl->GetDisplayName(
                lpSrcMonikerOfCopy, pbc, NULL, &lpszSrcOfCopy);
            pbc->lpVtbl->Release(pbc);
            lpSrcMonikerOfCopy->lpVtbl->Release(lpSrcMonikerOfCopy);
        } else {
            /* this document has no moniker; use our FullUserTypeName
            **    as the description of the source of copy.
            */
            lpszSrcOfCopy = FULLUSERTYPENAME;
        }

        hObjDesc =  OleStdGetObjectDescriptorData(
                CLSID_APP,
                DVASPECT_CONTENT,
                sizel,
                pointl,
                dwStatus,
                FULLUSERTYPENAME,
                lpszSrcOfCopy
        );

        if (lpSrcMonikerOfCopy && lpszSrcOfCopy)
            OleStdFreeString(lpszSrcOfCopy, NULL);
        return hObjDesc;

    }
#elif defined( OLE_CNTR )
    {
        LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc;
        LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
        LPCONTAINERLINE lpContainerLine;
        HGLOBAL hObjDesc;
        BOOL fSelIsOleObject = FALSE;
        LPOLEOBJECT lpOleObj;
        SIZEL sizel;
        POINTL pointl;

        if ( lpLL->m_nNumLines == 1 ) {
            fSelIsOleObject = ContainerDoc_IsSelAnOleObject(
                    lpContainerDoc,
                    &IID_IOleObject,
                    (LPUNKNOWN FAR*)&lpOleObj,
                    NULL,    /* we don't need the line index */
                    (LPCONTAINERLINE FAR*)&lpContainerLine
            );
        }

        pointl.x = pointl.y = 0;

        if (fSelIsOleObject) {
            /* OLE2NOTE: a single OLE object is being transfered via
            **    this DataTransferDoc. we need to generate the
            **    CF_ObjectDescrioptor which describes the OLE object.
            */

            LPOUTLINEDOC lpSrcOutlineDoc = 
                    (LPOUTLINEDOC)lpOleDoc->m_lpSrcDocOfCopy;
            LPSTR lpszSrcOfCopy = lpSrcOutlineDoc->m_szFileName;
            BOOL fFreeSrcOfCopy = FALSE;

            /* if the object copied can be linked to then get a
            **    TEMPFORUSER form of the moniker which identifies the
            **    source of copy. we do not want to force the
            **    assignment of the moniker until CF_LINKSOURCE is
            **    rendered. 
            **    if the object copied can not be a link source then use
            **    the source filename to identify the source of copy.
            **    there is no need to generate a moniker for the object
            **    copied.
            */
            if (lpOleDoc->m_fLinkSourceAvail && 
                    lpContainerDoc->m_lpSrcContainerLine) {
                LPBINDCTX pbc = NULL;
                LPMONIKER lpSrcMonikerOfCopy = ContainerLine_GetFullMoniker(
                        lpContainerDoc->m_lpSrcContainerLine,
                        GETMONIKER_TEMPFORUSER
                );
                if (lpSrcMonikerOfCopy) {
                    CreateBindCtx(0, (LPBC FAR*)&pbc);
                    if (pbc != NULL) {
                        lpSrcMonikerOfCopy->lpVtbl->GetDisplayName(
                                lpSrcMonikerOfCopy, pbc, NULL,&lpszSrcOfCopy);
                        pbc->lpVtbl->Release(pbc);
                        fFreeSrcOfCopy = TRUE;
                    }
                    lpSrcMonikerOfCopy->lpVtbl->Release(lpSrcMonikerOfCopy);
                }
            }

            hObjDesc = OleStdGetObjectDescriptorDataFromOleObject(
                    lpOleObj,
                    lpszSrcOfCopy,
                    lpContainerLine->m_dwDrawAspect,
                    pointl
            );

            if (fFreeSrcOfCopy && lpszSrcOfCopy) 
                OleStdFreeString(lpszSrcOfCopy, NULL);
            OleStdRelease((LPUNKNOWN)lpOleObj);
            return hObjDesc;
        } else {
            /* OLE2NOTE: the data being transfered via this
            **    DataTransferDoc is NOT a single OLE object. thus in
            **    this case the CF_ObjectDescriptor data should
            **    describe our container app itself.
            */
            OleDoc_GetExtent(lpOleDoc, &sizel);
            return OleStdGetObjectDescriptorData(
                    CLSID_NULL, /* not used if no object formats */
                    DVASPECT_CONTENT,
                    sizel,
                    pointl,
                    0,
                    NULL,       /* UserTypeName not used if no obj fmt's */
                    FULLUSERTYPENAME   /* string to identify source of copy */
            );

        }
    }
#endif
}


#if defined( OLE_SERVER )

/*************************************************************************
** ServerDoc Supprt Functions Used by Server versions
*************************************************************************/


/* ServerDoc_PasteFormatFromData
** -----------------------------
**    
**    Paste a particular data format from a IDataObject*. The
**    IDataObject* may come from the clipboard (GetClipboard) or from a
**    drag/drop operation. 
**    
**    NOTE: fLink is specified then FALSE if returned because the
**    Server only version of the app can not support links.
**    
**    Returns TRUE if data was successfully pasted.
**            FALSE if data could not be pasted.
*/
BOOL ServerDoc_PasteFormatFromData(
        LPSERVERDOC             lpServerDoc, 
        CLIPFORMAT              cfFormat, 
        LPDATAOBJECT            lpSrcDataObj, 
        BOOL                    fLocalDataObj, 
        BOOL                    fLink
)
{
    LPLINELIST   lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpServerDoc)->m_LineList;
    LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    LPOLEAPP     lpOleApp = (LPOLEAPP)g_lpApp;
    int          nIndex;
    int          nCount = 0;
    HGLOBAL      hData;
    STGMEDIUM    medium;
    LINERANGE    lrSel;

    if (LineList_GetCount(lpLL) == 0) 
        nIndex = -1;    // pasting to empty list
    else  
        nIndex=LineList_GetFocusLineIndex(lpLL);
    
    if (fLink) {
        /* We should paste a Link to the data, but we do not support links */
        return FALSE;

    } else {

        if (cfFormat == lpOutlineApp->m_cfOutline) {

            hData = OleStdGetData(
                    lpSrcDataObj, 
                    lpOutlineApp->m_cfOutline, 
                    NULL,
					DVASPECT_CONTENT,
                    (LPSTGMEDIUM)&medium
            );
			if (hData == NULL) 
				return FALSE;

            nCount = OutlineDoc_PasteOutlineData(
                    (LPOUTLINEDOC)lpServerDoc, 
                    hData,
                    nIndex
            );
            // OLE2NOTE: we must free data handle by releasing the medium
            ReleaseStgMedium((LPSTGMEDIUM)&medium);  

        } else if(cfFormat == CF_TEXT) {

            hData = OleStdGetData(
                    lpSrcDataObj, 
                    CF_TEXT, 
                    NULL,
					DVASPECT_CONTENT,
                    (LPSTGMEDIUM)&medium
            );
			if (hData == NULL) 
				return FALSE;

            nCount = OutlineDoc_PasteTextData(
                    (LPOUTLINEDOC)lpServerDoc, 
                    hData, 
                    nIndex
            );
            // OLE2NOTE: we must free data handle by releasing the medium
            ReleaseStgMedium((LPSTGMEDIUM)&medium);  
        }
    }

    lrSel.m_nEndLine   = nIndex + 1;
    lrSel.m_nStartLine = nIndex + nCount;
    LineList_SetSel(lpLL, &lrSel);
    return TRUE;
}


/* ServerDoc_QueryPasteFromData
** ----------------------------
**    
**    Check if the IDataObject* offers data in a format that we can
**    paste. The IDataObject* may come from the clipboard
**    (GetClipboard) or from a drag/drop operation. 
**    In this function we look if one of the following formats is
**    offered: 
**              CF_OUTLINE
**              CF_TEXT
**    
**    NOTE: fLink is specified then FALSE if returned because the
**    Server only version of the app can not support links.
**    
**    Returns TRUE if paste can be performed
**            FALSE if paste is not possible.
*/
BOOL ServerDoc_QueryPasteFromData(
        LPSERVERDOC             lpServerDoc, 
        LPDATAOBJECT            lpSrcDataObj, 
        BOOL                    fLink
)
{
    LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;

    if (fLink) {
        /* we do not support links */
        return FALSE;

    } else {

        int nFmtEtc;

        nFmtEtc = OleStdGetPriorityClipboardFormat(
                lpSrcDataObj,
                lpOleApp->m_arrPasteEntries,
                lpOleApp->m_nPasteEntries
            );
        
        if (nFmtEtc < 0) 
            return FALSE;   // there is no format we like
    }

    return TRUE;
}


/* ServerDoc_GetData
 * -----------------
 *
 * Render data from the document on a CALLEE allocated STGMEDIUM.
 *      This routine is called via IDataObject::GetData.
 */

HRESULT ServerDoc_GetData (
        LPSERVERDOC             lpServerDoc, 
        LPFORMATETC             lpformatetc, 
        LPSTGMEDIUM             lpMedium
)
{
    LPOLEDOC  lpOleDoc = (LPOLEDOC)lpServerDoc;
    LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
    LPSERVERAPP lpServerApp = (LPSERVERAPP)g_lpApp;
    LPOLEAPP  lpOleApp = (LPOLEAPP)lpServerApp;
    LPOUTLINEAPP  lpOutlineApp = (LPOUTLINEAPP)lpServerApp;
    HRESULT hrErr;
    SCODE sc;

    // OLE2NOTE: we must set out pointer parameters to NULL
    lpMedium->pUnkForRelease = NULL;

    /* OLE2NOTE: we must make sure to set all out parameters to NULL. */
    lpMedium->tymed = TYMED_NULL;
    lpMedium->pUnkForRelease = NULL;    // we transfer ownership to caller
    lpMedium->u.hGlobal = NULL;

    if(lpformatetc->cfFormat == lpOutlineApp->m_cfOutline) {
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }
        lpMedium->u.hGlobal = OutlineDoc_GetOutlineData (lpOutlineDoc,NULL);
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_HGLOBAL;
        OleDbgOut3("ServerDoc_GetData: rendered CF_OUTLINE\r\n");
		return NOERROR;

    } else if (lpformatetc->cfFormat == CF_METAFILEPICT &&
		(lpformatetc->dwAspect & (DVASPECT_CONTENT | DVASPECT_DOCPRINT)) ) {
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_MFPICT)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }

        lpMedium->u.hGlobal = ServerDoc_GetMetafilePictData(lpServerDoc,NULL);
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_MFPICT;
        OleDbgOut3("ServerDoc_GetData: rendered CF_METAFILEPICT\r\n");
		return NOERROR;

    } else if (lpformatetc->cfFormat == CF_METAFILEPICT &&
		(lpformatetc->dwAspect & DVASPECT_ICON) ) {
		CLSID clsid;
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_MFPICT)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }

		/* OLE2NOTE: we should return the default icon for our class.
		**    we must be carefull to use the correct CLSID here.
		**    if we are currently preforming a "TreatAs (aka. ActivateAs)"
		**    operation then we need to use the class of the object
		**    written in the storage of the object. otherwise we would
		**    use our own class id. 
		*/
		if (ServerDoc_GetClassID(lpServerDoc, (LPCLSID)&clsid) != NOERROR) {
			sc = DATA_E_FORMATETC;
            goto error;
		}			

        lpMedium->u.hGlobal=GetIconOfClass(g_lpApp->m_hInst,(REFCLSID)&clsid, NULL, FALSE);
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_MFPICT;
        OleDbgOut3("ServerDoc_GetData: rendered CF_METAFILEPICT (icon)\r\n");
		return NOERROR;

    } else if (lpformatetc->cfFormat == CF_TEXT) {
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }

        lpMedium->u.hGlobal = OutlineDoc_GetTextData (
                (LPOUTLINEDOC)lpServerDoc, 
                NULL
        );
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_HGLOBAL;
        OleDbgOut3("ServerDoc_GetData: rendered CF_TEXT\r\n");
		return NOERROR;
    } 
	
	/* the above are the only formats supports by a user document (ie.
	**    a non-data transfer doc). if the document is used for
	**    purposes of data transfer, then additional formats are offered.
	*/
	if (! lpOutlineDoc->m_fDataTransferDoc) {
        sc = DATA_E_FORMATETC;
		goto error;
	}

	/* OLE2NOTE: ObjectDescriptor and LinkSrcDescriptor will
	**    contain the same data for the pure container and pure server
	**    type applications. only a container/server application may
	**    have different content for ObjectDescriptor and
	**    LinkSrcDescriptor. if a container/server copies a link for
	**    example, then the ObjectDescriptor would give the class
	**    of the link source but the LinkSrcDescriptor would give the
	**    class of the container/server itself. in this situation if a
	**    paste operation occurs, an equivalent link is pasted, but if
	**    a pastelink operation occurs, then a link to a pseudo object
	**    in the container/server is created.
	*/
	if (lpformatetc->cfFormat == lpOleApp->m_cfObjectDescriptor || 
		(lpformatetc->cfFormat == lpOleApp->m_cfLinkSrcDescriptor &&
		        lpOleDoc->m_fLinkSourceAvail)) {
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }

        lpMedium->u.hGlobal = OleDoc_GetObjectDescriptorData (
                (LPOLEDOC)lpServerDoc, 
                NULL
        );
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_HGLOBAL;
        OleDbgOut3("ServerDoc_GetData: rendered CF_OBJECTDESCRIPTOR\r\n");
		return NOERROR;

	} else if (lpformatetc->cfFormat == lpOleApp->m_cfEmbedSource) {
        hrErr = OleStdGetOleObjectData(
                (LPPERSISTSTORAGE)&lpServerDoc->m_PersistStorage,
                lpformatetc,
                lpMedium,
                FALSE   /* fUseMemory -- (use file-base stg) */

        );
        if (hrErr != NOERROR) {
            sc = GetScode(hrErr);
            goto error;
        }
        OleDbgOut3("ServerDoc_GetData: rendered CF_EMBEDSOURCE\r\n");
		return NOERROR;

    } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSource) {     
        if (lpOleDoc->m_fLinkSourceAvail) {
            LPMONIKER lpmk;

            lpmk = ServerDoc_GetSelFullMoniker(
                    (LPSERVERDOC)lpOleDoc->m_lpSrcDocOfCopy,
                    &lpServerDoc->m_lrSrcSelOfCopy,
                    GETMONIKER_FORCEASSIGN
            );
            if (lpmk) {
                hrErr = OleStdGetLinkSourceData(
                        lpmk,
                        (LPCLSID)&CLSID_APP,
                        lpformatetc,
                        lpMedium
                );
                OleStdRelease((LPUNKNOWN)lpmk);
                if (hrErr != NOERROR) {
                    sc = GetScode(hrErr);
                    goto error;
                }
                OleDbgOut3("ServerDoc_GetData: rendered CF_LINKSOURCE\r\n");
				return NOERROR;

            } else {
                sc = E_FAIL;
                goto error;
            }
        } else {
            sc = DATA_E_FORMATETC;
            goto error;
        }

    } else {
        sc = DATA_E_FORMATETC;
		goto error;
    }

    return NOERROR;

error:
    return ResultFromScode(sc);
}


/* ServerDoc_GetDataHere
 * ---------------------
 *
 * Render data from the document on a CALLER allocated STGMEDIUM.
 *      This routine is called via IDataObject::GetDataHere.
 */
HRESULT ServerDoc_GetDataHere (
        LPSERVERDOC             lpServerDoc, 
        LPFORMATETC             lpformatetc, 
        LPSTGMEDIUM             lpMedium
)
{
    LPOLEDOC        lpOleDoc = (LPOLEDOC)lpServerDoc;
    LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
    LPSERVERAPP     lpServerApp = (LPSERVERAPP)g_lpApp;
    LPOLEAPP        lpOleApp = (LPOLEAPP)lpServerApp;
    LPOUTLINEAPP    lpOutlineApp = (LPOUTLINEAPP)lpServerApp;
    HRESULT         hrErr;
    SCODE           sc;

    // OLE2NOTE: we must set out pointer parameters to NULL
    lpMedium->pUnkForRelease = NULL;
	
	/* our user document does not support any formats for GetDataHere.
	**    if the document is used for
	**    purposes of data transfer, then additional formats are offered.
	*/
	if (! lpOutlineDoc->m_fDataTransferDoc) {
        sc = DATA_E_FORMATETC;
		goto error;
	}

    if (lpformatetc->cfFormat == lpOleApp->m_cfEmbedSource) {
        hrErr = OleStdGetOleObjectData(
                (LPPERSISTSTORAGE)&lpServerDoc->m_PersistStorage,
                lpformatetc,
                lpMedium,
                FALSE   /* fUseMemory -- (use file-base stg) */
        );
        if (hrErr != NOERROR) {
            sc = GetScode(hrErr);
            goto error;
        }
        OleDbgOut3("ServerDoc_GetDataHere: rendered CF_EMBEDSOURCE\r\n");
		return NOERROR;

    } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSource) {     
        if (lpOleDoc->m_fLinkSourceAvail) {
            LPMONIKER lpmk;

            lpmk = ServerDoc_GetSelFullMoniker(
                    (LPSERVERDOC)lpOleDoc->m_lpSrcDocOfCopy,
                    &lpServerDoc->m_lrSrcSelOfCopy,
                    GETMONIKER_FORCEASSIGN
            );
            if (lpmk) {
                hrErr = OleStdGetLinkSourceData(
                        lpmk,
                        (LPCLSID)&CLSID_APP,
                        lpformatetc,
                        lpMedium
                );
                OleStdRelease((LPUNKNOWN)lpmk);
                if (hrErr != NOERROR) {
                    sc = GetScode(hrErr);
                    goto error;
                }

                OleDbgOut3(
                        "ServerDoc_GetDataHere: rendered CF_LINKSOURCE\r\n");
				return NOERROR;

            } else {
                sc = E_FAIL;
                goto error;
            }
        } else {
            sc = DATA_E_FORMATETC;
            goto error;
        }
    } else {

        /* Caller is requesting data to be returned in Caller allocated
        **    medium, but we do NOT support this. we only support
        **    global memory blocks that WE allocate for the caller.
        */
        sc = DATA_E_FORMATETC;
        goto error;
    }

    return NOERROR;

error:
    return ResultFromScode(sc);
}


/* ServerDoc_QueryGetData
 * ----------------------
 *
 * Answer if a particular data format is supported via GetData/GetDataHere.
 *      This routine is called via IDataObject::QueryGetData.
 */

HRESULT ServerDoc_QueryGetData (LPSERVERDOC lpServerDoc,LPFORMATETC lpformatetc)
{
    LPOLEDOC        lpOleDoc = (LPOLEDOC)lpServerDoc;
    LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
    LPSERVERAPP     lpServerApp = (LPSERVERAPP)g_lpApp;
    LPOLEAPP        lpOleApp = (LPOLEAPP)lpServerApp;
    LPOUTLINEAPP    lpOutlineApp = (LPOUTLINEAPP)lpServerApp;

    /* Caller is querying if we support certain format but does not
    **    want any data actually returned.
    */
    if (lpformatetc->cfFormat == lpOutlineApp->m_cfOutline ||
            lpformatetc->cfFormat == CF_TEXT) {
        // we only support HGLOBAL
        return OleStdQueryFormatMedium(lpformatetc, TYMED_HGLOBAL);
	} else if (lpformatetc->cfFormat == CF_METAFILEPICT &&
		(lpformatetc->dwAspect & 
			(DVASPECT_CONTENT | DVASPECT_CONTENT | DVASPECT_DOCPRINT)) ) {
        return OleStdQueryFormatMedium(lpformatetc, TYMED_MFPICT);
    }
	
	/* the above are the only formats supports by a user document (ie.
	**    a non-data transfer doc). if the document is used for
	**    purposes of data transfer, then additional formats are offered.
	*/
	if (! lpOutlineDoc->m_fDataTransferDoc) 
		return ResultFromScode(DATA_E_FORMATETC);
		
    if (lpformatetc->cfFormat == lpOleApp->m_cfEmbedSource) {
        return OleStdQueryOleObjectData(lpformatetc);

    } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSource &&
        lpOleDoc->m_fLinkSourceAvail) {
        return OleStdQueryLinkSourceData(lpformatetc);

    } else if (lpformatetc->cfFormat == lpOleApp->m_cfObjectDescriptor) {
        return OleStdQueryObjectDescriptorData(lpformatetc);

    } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSrcDescriptor &&
		        lpOleDoc->m_fLinkSourceAvail) {
        return OleStdQueryObjectDescriptorData(lpformatetc);
    }

    return ResultFromScode(DATA_E_FORMATETC);
}


/* ServerDoc_EnumFormatEtc
 * -----------------------
 *
 * Return an enumerator which enumerates the data accepted/offered by 
 *      the document. 
 *      This routine is called via IDataObject::EnumFormatEtc.
 */
HRESULT ServerDoc_EnumFormatEtc(
        LPSERVERDOC             lpServerDoc, 
        DWORD                   dwDirection, 
        LPENUMFORMATETC FAR*    lplpenumFormatEtc
)
{
    LPOLEDOC lpOleDoc = (LPOLEDOC)lpServerDoc;
    LPOLEAPP  lpOleApp = (LPOLEAPP)g_lpApp;
    int nActualFmts;
    SCODE sc = S_OK;

    /* OLE2NOTE: the enumeration of formats for a data transfer
    **    document is not a static list. the list of formats offered
    **    may or may not include CF_LINKSOURCE depending on whether a
    **    moniker is available for our document. thus we can NOT use
    **    the default OLE enumerator which enumerates the formats that
    **    are registered for our app in the registration database.
    */
    if (dwDirection == DATADIR_GET) {
        nActualFmts = lpOleApp->m_nDocGetFmts;
        
        /* If the document does not have a Moniker, then exclude
        **    CF_LINKSOURCE and CF_LINKSRCDESCRIPTOR from the list of
		**    formats available. these formats are deliberately listed
		**    last in the array of possible "Get" formats.
        */
        if (! lpOleDoc->m_fLinkSourceAvail)
            nActualFmts -= 2;

        *lplpenumFormatEtc = OleStdEnumFmtEtc_Create(
                nActualFmts, lpOleApp->m_arrDocGetFmts);
        if (*lplpenumFormatEtc == NULL)
            sc = E_OUTOFMEMORY;

    } else if (dwDirection == DATADIR_SET) {
        /* OLE2NOTE: a document that is used to transfer data
        **    (either via the clipboard or drag/drop does NOT
        **    accept SetData on ANY format! 
        */
        sc = E_NOTIMPL;
        goto error;
    } else {
        sc = E_INVALIDARG;
        goto error;
    }
    
error:
    return ResultFromScode(sc);
}


/* ServerDoc_GetMetafilePictData
 * -----------------------------
 *
 * Return a handle to an object's picture data in metafile format.
 *
 * 
 * RETURNS: A handle to the object's data in metafile format.
 *
 */
HGLOBAL ServerDoc_GetMetafilePictData(
        LPSERVERDOC         lpServerDoc, 
        LPLINERANGE         lplrSel
)
{
    LPOUTLINEAPP	lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    LPOUTLINEDOC    lpOutlineDoc=(LPOUTLINEDOC)lpServerDoc;
    LPLINELIST      lpLL=(LPLINELIST)&lpOutlineDoc->m_LineList;
    LPLINE          lpLine;
    LPMETAFILEPICT  lppict = NULL;
    HGLOBAL         hMFPict = NULL;
    HMETAFILE       hMF = NULL;
    RECT            rect;
    HDC             hDC;
    int             i;
    int             nWidth;
    int     nStart = (lplrSel ? lplrSel->m_nStartLine : 0);
    int     nEnd =(lplrSel ? lplrSel->m_nEndLine : LineList_GetCount(lpLL)-1);
    int     nLines = nEnd - nStart + 1;
	UINT	fuAlign;
    POINT point;
    SIZE  size;
     
    hDC = CreateMetaFile(NULL);

    rect.left = 0;
    rect.right = 0;
    rect.bottom = 0;

	if (nLines > 0) {
	// calculate the total height/width of LineList in HIMETRIC
		for(i = nStart; i <= nEnd; i++) {
			lpLine = LineList_GetLine(lpLL,i);
			nWidth = Line_GetTotalWidthInHimetric(lpLine);
			rect.right = max(rect.right, nWidth);
			rect.bottom -= Line_GetHeightInHimetric(lpLine);
		}


		SetMapMode(hDC, MM_ANISOTROPIC);

		SetWindowOrgEx(hDC, 0, 0, &point);
		SetWindowExtEx(hDC, rect.right, rect.bottom, &size);

		// Set the default font size, and font face name
		SelectObject(hDC, OutlineApp_GetActiveFont(lpOutlineApp));

		FillRect(hDC, (LPRECT) &rect, GetStockObject(WHITE_BRUSH));

		rect.bottom = 0;
		
		fuAlign = SetTextAlign(hDC, TA_LEFT | TA_TOP | TA_NOUPDATECP);
		
		/* While more lines print out the text */
		for(i = nStart; i <= nEnd; i++) {
			lpLine = LineList_GetLine(lpLL,i);

			rect.top = rect.bottom;
			rect.bottom -= Line_GetHeightInHimetric(lpLine);

			/* Draw the line */
			Line_Draw(lpLine, hDC, &rect);
		}
		
		SetTextAlign(hDC, fuAlign);
	}
    
    // Get handle to the metafile.
    if (!(hMF = CloseMetaFile (hDC)))
        return NULL;

    if (!(hMFPict = GlobalAlloc (GMEM_SHARE | GMEM_ZEROINIT, 
                    sizeof (METAFILEPICT)))) {
        DeleteMetaFile (hMF);
        return NULL;
    }

    if (!(lppict = (LPMETAFILEPICT)GlobalLock(hMFPict))) {
        DeleteMetaFile (hMF);
        GlobalFree (hMFPict);
        return NULL;
    }

    lppict->mm   =  MM_ANISOTROPIC;
    lppict->hMF  =  hMF;
    lppict->xExt =  rect.right;
    lppict->yExt =  - rect.bottom;  // add minus sign to make it +ve
    GlobalUnlock (hMFPict);
    
    return hMFPict;
}

#endif  // OLE_SERVER



#if defined( OLE_CNTR )

/*************************************************************************
** ContainerDoc Supprt Functions Used by Container versions
*************************************************************************/


/* Paste OLE Link from clipboard */
void ContainerDoc_PasteLinkCommand(LPCONTAINERDOC lpContainerDoc)
{
    LPOUTLINEAPP    lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    LPOLEAPP        lpOleApp = (LPOLEAPP)g_lpApp;
    LPDATAOBJECT    lpClipboardDataObj = NULL;
    BOOL            fLink = TRUE;
    BOOL            fLocalDataObj = FALSE;
    BOOL            fDisplayAsIcon = FALSE;
    HCURSOR         hPrevCursor;
    HGLOBAL         hMem = NULL;
    HGLOBAL         hMetaPict = NULL;
    STGMEDIUM       medium;
    BOOL            fStatus;
    HRESULT         hrErr;

    hrErr = OleGetClipboard((LPDATAOBJECT FAR*)&lpClipboardDataObj);
    if (hrErr != NOERROR)
        return;     // Clipboard seems to be empty or can't be accessed

    // this may take a while, put up hourglass cursor
    hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

    /* check if the data on the clipboard is local to our application
    **    instance.
    */
    if (lpOutlineApp->m_lpClipboardDoc) {
        LPOLEDOC lpOleDoc = (LPOLEDOC)lpOutlineApp->m_lpClipboardDoc;
        if (lpClipboardDataObj == (LPDATAOBJECT)&lpOleDoc->m_DataObject)
            fLocalDataObj = TRUE;
    }

    /* OLE2NOTE: we need to check what dwDrawAspect is being
    **    transfered. if the data is an object that is displayed as an
    **    icon in the source, then we want to keep it as an icon. the
    **    aspect the object is displayed in at the source is transfered
    **    via the CF_LINKSOURCEDESCRIPTOR format for a PasteLink
    **    operation. 
    */
    if (hMem = OleStdGetData(
            lpClipboardDataObj,
            lpOleApp->m_cfLinkSrcDescriptor,
            NULL,
            DVASPECT_CONTENT,
            (LPSTGMEDIUM)&medium)) {
        LPOBJECTDESCRIPTOR lpOD = GlobalLock(hMem);
        fDisplayAsIcon = (lpOD->dwDrawAspect == DVASPECT_ICON ? TRUE : FALSE);
        GlobalUnlock(hMem);
        ReleaseStgMedium((LPSTGMEDIUM)&medium);     // equiv to GlobalFree

        if (fDisplayAsIcon) {
            hMetaPict = OleStdGetData(
                    lpClipboardDataObj,
                    CF_METAFILEPICT,
                    NULL,
                    DVASPECT_ICON,
                    (LPSTGMEDIUM)&medium
            );
            if (hMetaPict == NULL) 
                fDisplayAsIcon = FALSE; // give up; failed to get icon MFP
        }
    }

    fStatus = ContainerDoc_PasteFormatFromData(
            lpContainerDoc,
            lpOleApp->m_cfLinkSource,
            lpClipboardDataObj,
            fLocalDataObj,
            fLink,
            fDisplayAsIcon,
            hMetaPict
    );

    if (!fStatus)
        OutlineApp_ErrorMessage(g_lpApp, ErrMsgPasting);

    if (hMetaPict)
        ReleaseStgMedium((LPSTGMEDIUM)&medium);  // properly free METAFILEPICT

    if (lpClipboardDataObj)
        OleStdRelease((LPUNKNOWN)lpClipboardDataObj);

    SetCursor(hPrevCursor);     // restore original cursor
}


/* ContainerDoc_PasteFormatFromData
** --------------------------------
**
**    Paste a particular data format from a IDataObject*. The
**    IDataObject* may come from the clipboard (GetClipboard) or from a
**    drag/drop operation.
**
**    Returns TRUE if data was successfully pasted.
**            FALSE if data could not be pasted.
*/
BOOL ContainerDoc_PasteFormatFromData(
        LPCONTAINERDOC          lpContainerDoc,
        CLIPFORMAT              cfFormat,
        LPDATAOBJECT            lpSrcDataObj,
        BOOL                    fLocalDataObj,
        BOOL                    fLink,
        BOOL                    fDisplayAsIcon,
        HGLOBAL                 hMetaPict
)
{
    LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    LPOUTLINEAPP    lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    LPOLEAPP        lpOleApp = (LPOLEAPP)g_lpApp;
    LPCONTAINERAPP  lpContainerApp = (LPCONTAINERAPP)g_lpApp;
    int             nIndex;
    int             nCount = 0;
    HGLOBAL         hData;
    STGMEDIUM       medium;
    FORMATETC       formatetc;
    HRESULT         hrErr;
    LINERANGE       lrSel;

    if (LineList_GetCount(lpLL) == 0)
        nIndex = -1;    // pasting to empty list
    else
        nIndex=LineList_GetFocusLineIndex(lpLL);

    if (fLink) {

        /* We should paste a Link to the data */

        if (cfFormat != lpOleApp->m_cfLinkSource)
            return FALSE;   // we only support OLE object type links

        nCount = ContainerDoc_PasteOleObject(
                lpContainerDoc,
                lpSrcDataObj,
                OLECREATEFROMDATA_LINK,
                cfFormat,
                nIndex,
                fDisplayAsIcon,
                hMetaPict
            );
        return (nCount > 0 ? TRUE : FALSE);

    } else {

        if (cfFormat == lpContainerApp->m_cfCntrOutl) {
            if (fLocalDataObj) {

                /* CASE I: IDataObject* is local to our app
                **
                **    if the source of the data is local to our
                **    application instance, then we can get direct
                **    access to the original OleDoc object that
                **    corresponds to the IDataObject* given.
                **    CF_CNTROUTL data is passed through a LPSTORAGE.
                **    if we call OleGetData asking for CF_CNTROUTL, we
                **    will be returned a copy of the existing open pStg
                **    of the original source document. we can NOT open
                **    streams and sub-storages again via this pStg
                **    since it is already open within our same
                **    application instance. we must copy the data from
                **    the original OleDoc source document.
                */
                LPLINELIST lpSrcLL;
                LPOLEDOC lpLocalSrcDoc =
                    ((struct CDocDataObjectImpl FAR*)lpSrcDataObj)->lpOleDoc;

                /* copy all lines from SrcDoc to DestDoc. */
                lpSrcLL = &((LPOUTLINEDOC)lpLocalSrcDoc)->m_LineList;
                nCount = LineList_CopySelToDoc(
                        lpSrcLL,
                        NULL,
                        (LPOUTLINEDOC)lpContainerDoc
                );

            } else {

                /* CASE II: IDataObject* is NOT local to our app
                **
                **    if the source of the data comes from another
                **    application instance. we can call GetDataHere to
                **    retrieve the CF_CNTROUTL data. CF_CNTROUTL data
                **    is passed through a LPSTORAGE. we MUST use
                **    IDataObject::GetDataHere. calling
                **    IDataObject::GetData does NOT work because OLE
                **    currently does NOT support remoting of a callee
                **    allocated root storage back to the caller. this
                **    hopefully will be supported in a future version.
                **    in order to call GetDataHere we must allocate an
                **    IStorage instance for the callee to write into.
                **    we will allocate an IStorage docfile that will
                **    delete-on-release. we could use either a
                **    memory-based storage or a file-based storage.
                */
                LPSTORAGE lpTmpStg = OleStdCreateTempStorage(
                        FALSE /*fUseMemory*/, 
                        STGM_READWRITE | STGM_TRANSACTED |STGM_SHARE_EXCLUSIVE
                );
                if (! lpTmpStg) 
                    return FALSE;

                formatetc.cfFormat = cfFormat;
                formatetc.ptd = NULL;
                formatetc.dwAspect = DVASPECT_CONTENT;
                formatetc.tymed = TYMED_ISTORAGE;
                formatetc.lindex = -1;
                
                medium.tymed = TYMED_ISTORAGE;
                medium.u.pstg = lpTmpStg;
                medium.pUnkForRelease = NULL;

                OLEDBG_BEGIN2("IDataObject::GetDataHere called\r\n")
                hrErr = lpSrcDataObj->lpVtbl->GetDataHere(
                        lpSrcDataObj,
                        (LPFORMATETC)&formatetc,
                        (LPSTGMEDIUM)&medium
                );
                OLEDBG_END2

                if (hrErr == NOERROR) {
                    nCount = ContainerDoc_PasteCntrOutlData(
                            lpContainerDoc,
                            lpTmpStg,
                            nIndex
                    );
                }
                OleStdVerifyRelease(
                    (LPUNKNOWN)lpTmpStg, "Temp stg NOT released!\r\n");
                return ((hrErr == NOERROR) ? TRUE : FALSE);
            }

        } else if (cfFormat == lpOutlineApp->m_cfOutline) {

            hData = OleStdGetData(
                    lpSrcDataObj,
                    lpOutlineApp->m_cfOutline,
                    NULL,
                    DVASPECT_CONTENT,
                    (LPSTGMEDIUM)&medium
            );
            nCount = OutlineDoc_PasteOutlineData(
                    (LPOUTLINEDOC)lpContainerDoc,
                    hData,
                    nIndex
                );
            // OLE2NOTE: we must free data handle by releasing the medium
            ReleaseStgMedium((LPSTGMEDIUM)&medium);

        } else if (cfFormat == lpOleApp->m_cfEmbedSource ||
            cfFormat == lpOleApp->m_cfEmbeddedObject ||
            cfFormat == lpOleApp->m_cfFileName) {
            /* OLE2NOTE: OleCreateFromData API creates an OLE object if
            **    CF_EMBEDDEDOBJECT, CF_EMBEDSOURCE, or CF_FILENAME are
            **    available from the source data object. the
            **    CF_FILENAME case arises when a file is copied to the
            **    clipboard from the FileManager. if the file has an
            **    associated class (see GetClassFile API), then an
            **    object of that class is created. otherwise an OLE 1.0
            **    Packaged object is created.
            */
            nCount = ContainerDoc_PasteOleObject(
                    lpContainerDoc,
                    lpSrcDataObj,
                    OLECREATEFROMDATA_OBJECT,
                    0,   /* N/A -- cfFormat */
                    nIndex,
                    fDisplayAsIcon,
                    hMetaPict
            );
            return (nCount > 0 ? TRUE : FALSE);

        } else if (cfFormat == CF_METAFILEPICT 
                    || cfFormat == CF_DIB
                    || cfFormat == CF_BITMAP) {

            /* OLE2NOTE: OleCreateStaticFromData API creates an static
            **    OLE object if CF_METAFILEPICT, CF_DIB, or CF_BITMAP is
            **    CF_EMBEDDEDOBJECT, CF_EMBEDSOURCE, or CF_FILENAME are
            **    available from the source data object. 
            */
            nCount = ContainerDoc_PasteOleObject(
                    lpContainerDoc,
                    lpSrcDataObj,
                    OLECREATEFROMDATA_STATIC,
                    cfFormat,
                    nIndex,
                    fDisplayAsIcon,
                    hMetaPict
            );
            return (nCount > 0 ? TRUE : FALSE);

        } else if(cfFormat == CF_TEXT) {

            hData = OleStdGetData(
                    lpSrcDataObj,
                    CF_TEXT,
                    NULL,
                    DVASPECT_CONTENT,
                    (LPSTGMEDIUM)&medium
            );
            nCount = OutlineDoc_PasteTextData(
                    (LPOUTLINEDOC)lpContainerDoc,
                    hData,
                    nIndex
                );
            // OLE2NOTE: we must free data handle by releasing the medium
            ReleaseStgMedium((LPSTGMEDIUM)&medium);

        } else {
            return FALSE;   // no acceptable format available to paste
        }
    }

    lrSel.m_nStartLine = nIndex + nCount;
    lrSel.m_nEndLine = nIndex + 1;
    LineList_SetSel(lpLL, &lrSel);
    return TRUE;
}


/* ContainerDoc_PasteCntrOutlData
 * -------------------------------
 *
 *      Load the lines stored in a lpSrcStg (stored in CF_CNTROUTL format)
 *  into the document.
 *
 * Return the number of items added
 */
int ContainerDoc_PasteCntrOutlData(
        LPCONTAINERDOC          lpDestContainerDoc,
        LPSTORAGE               lpSrcStg,
        int                     nStartIndex
)
{
    int nCount;
    LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
    LPOUTLINEDOC lpDestOutlineDoc = (LPOUTLINEDOC)lpDestContainerDoc;
    LPOUTLINEDOC lpSrcOutlineDoc;
    LPLINELIST   lpSrcLL;

    // create a temp document that will be used to load the lpSrcStg data.
    lpSrcOutlineDoc = (LPOUTLINEDOC)OutlineApp_CreateDoc(lpOutlineApp, FALSE);
    if ( ! lpSrcOutlineDoc )
        return 0;

    if (! OutlineDoc_LoadFromStg(lpSrcOutlineDoc, lpSrcStg))
        goto error;

    /* copy all lines from the SrcDoc to the DestDoc. */
    lpSrcLL = &lpSrcOutlineDoc->m_LineList;
    nCount = LineList_CopySelToDoc(lpSrcLL, NULL, lpDestOutlineDoc);

    if (lpSrcOutlineDoc)            // destroy temporary document.
        OutlineDoc_Close(lpSrcOutlineDoc, OLECLOSE_NOSAVE);      

    return nCount;

error:
    if (lpSrcOutlineDoc)            // destroy temporary document.
        OutlineDoc_Close(lpSrcOutlineDoc, OLECLOSE_NOSAVE);

    return 0;
}


/* ContainerDoc_QueryPasteFromData
** -------------------------------
**
**    Check if the IDataObject* offers data in a format that we can
**    paste. The IDataObject* may come from the clipboard
**    (GetClipboard) or from a drag/drop operation.
**    In this function we look if one of the following formats is
**    offered:
**              CF_OUTLINE
**              <OLE object -- CF_EMBEDSOURCE or CF_EMBEDDEDOBJECT>
**              CF_TEXT
**
**    NOTE: fLink is specified and CF_LINKSOURCE is available then TRUE
**    is returned, else FALSE.
**
**    Returns TRUE if paste can be performed
**            FALSE if paste is not possible.
*/
BOOL ContainerDoc_QueryPasteFromData(
        LPCONTAINERDOC          lpContainerDoc,
        LPDATAOBJECT            lpSrcDataObj,
        BOOL                    fLink
)
{
    LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;

    if (fLink) {
        /* check if we can paste a Link to the data */
        if (OleQueryLinkFromData(lpSrcDataObj) != NOERROR)
            return FALSE;   // linking is NOT possible
    } else {

        int nFmtEtc;

        nFmtEtc = OleStdGetPriorityClipboardFormat(
                lpSrcDataObj,
                lpOleApp->m_arrPasteEntries,
                lpOleApp->m_nPasteEntries
            );

        if (nFmtEtc < 0)
            return FALSE;   // there is no format we like
    }

    return TRUE;
}


/* ContainerDoc_PasteOleObject
** ---------------------------
**
**    Embed or link an OLE object. the source of the data is a pointer
**    to an IDataObject. normally this lpSrcDataObj comes from the
**    clipboard after call OleGetClipboard.
**
**    dwCreateType controls what type of object will created:
**    OLECREATEFROMDATA_LINK -- OleCreateLinkFromData will be called
**    OLECREATEFROMDATA_OBJECT -- OleCreateFromData will be called
**    OLECREATEFROMDATA_STATIC -- OleCreateStaticFromData will be called
**                                  cfFormat controls the type of static
**    a CONTAINERLINE object is created to manage the OLE object. this
**    CONTAINERLINE is added to the ContainerDoc after line nIndex.
**
*/
int ContainerDoc_PasteOleObject(
        LPCONTAINERDOC          lpContainerDoc,
        LPDATAOBJECT            lpSrcDataObj,
        DWORD                   dwCreateType,
        CLIPFORMAT              cfFormat,
        int                     nIndex,
        BOOL                    fDisplayAsIcon,
        HGLOBAL                 hMetaPict
)
{
    LPLINELIST          lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
    LPLINE              lpLine = NULL;
    HDC                 hDC;
    int                 nTab = 0;
    char                szStgName[CWCSTORAGENAME];
    LPCONTAINERLINE     lpContainerLine = NULL;

    ContainerDoc_GetNextStgName(lpContainerDoc, szStgName, sizeof(szStgName));

    /* default the new line to have the same indent as previous line */
    lpLine = LineList_GetLine(lpLL, nIndex);
    if (lpLine)
        nTab = Line_GetTabLevel(lpLine);

    hDC = LineList_GetDC(lpLL);

    lpContainerLine = ContainerLine_CreateFromData(
            hDC,
            nTab,
            lpContainerDoc,
            lpSrcDataObj,
            dwCreateType,
            cfFormat,
            fDisplayAsIcon,
            hMetaPict,
            szStgName
        );
    LineList_ReleaseDC(lpLL, hDC);

    if (! lpContainerLine)
        goto error;

    /* add a ContainerLine object to the document's LineList. The
    **    ContainerLine manages the rectangle on the screen occupied by
    **    the OLE object. later when the app is updated to support
    **    extended layout, there could be more than one Line associated
    **    with the OLE object.
    */

    LineList_AddLine(lpLL, (LPLINE)lpContainerLine, nIndex);

    // check to see if the object has any initial extents.
    ContainerLine_UpdateExtent(lpContainerLine, NULL);

    OutlineDoc_SetModified((LPOUTLINEDOC)lpContainerDoc, TRUE, TRUE, TRUE);

    return 1;   // one line added to LineList

error:
    // NOTE: if ContainerLine_CreateFromClip failed
    OutlineApp_ErrorMessage(g_lpApp, "Paste Object failed!");
    return 0;       // no lines added to line list
}


/* ContainerDoc_GetData
 * --------------------
 *
 * Render data from the document on a CALLEE allocated STGMEDIUM.
 *      This routine is called via IDataObject::GetData.
 */
HRESULT ContainerDoc_GetData (
        LPCONTAINERDOC          lpContainerDoc,
        LPFORMATETC             lpformatetc,
        LPSTGMEDIUM             lpMedium
)
{
    LPOLEDOC  lpOleDoc = (LPOLEDOC)lpContainerDoc;
    LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
    LPOLEAPP  lpOleApp = (LPOLEAPP)lpContainerApp;
    LPOUTLINEAPP  lpOutlineApp = (LPOUTLINEAPP)lpContainerApp;
    HRESULT hrErr;
    SCODE sc;

    // OLE2NOTE: we must set out pointer parameters to NULL
    lpMedium->pUnkForRelease = NULL;

    /* OLE2NOTE: we must set all out pointer parameters to NULL. */
    lpMedium->tymed = TYMED_NULL;
    lpMedium->pUnkForRelease = NULL;    // we transfer ownership to caller
    lpMedium->u.hGlobal = NULL;

    if (lpformatetc->cfFormat == lpContainerApp->m_cfCntrOutl) {
        
        /* OLE2NOTE: currently OLE does NOT support remoting a root
        **    level IStorage (either memory or file based) as an OUT
        **    parameter. thus, we can NOT support GetData for this
        **    TYMED_ISTORAGE based format. the caller MUST call GetDataHere.
        */
        sc = DATA_E_FORMATETC;
        goto error;

    } else if (lpformatetc->cfFormat == lpOutlineApp->m_cfOutline) {
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }

        lpMedium->u.hGlobal = OutlineDoc_GetOutlineData(lpOutlineDoc, NULL);
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_HGLOBAL;
        OleDbgOut3("ContainerDoc_GetData: rendered CF_OUTLINE\r\n");
        return NOERROR;

    } else if (lpformatetc->cfFormat == CF_TEXT) {
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }

        lpMedium->u.hGlobal = OutlineDoc_GetTextData (
                (LPOUTLINEDOC)lpContainerDoc,
                NULL
        );
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_HGLOBAL;
        OleDbgOut3("ContainerDoc_GetData: rendered CF_TEXT\r\n");
        return NOERROR;

    } else if ( lpformatetc->cfFormat == lpOleApp->m_cfObjectDescriptor ||
        (lpformatetc->cfFormat == lpOleApp->m_cfLinkSrcDescriptor &&
            lpOleDoc->m_fLinkSourceAvail) ) {
        // Verify caller asked for correct medium
        if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
            sc = DATA_E_FORMATETC;
            goto error;
        }

        lpMedium->u.hGlobal = OleDoc_GetObjectDescriptorData (
                (LPOLEDOC)lpContainerDoc,
                NULL
        );
        if (! lpMedium->u.hGlobal) {
            sc = E_OUTOFMEMORY;
            goto error;
        }

        lpMedium->tymed = TYMED_HGLOBAL;
#if defined( _DEBUG )
        if (lpformatetc->cfFormat == lpOleApp->m_cfObjectDescriptor)
            OleDbgOut3(
                "ContainerDoc_GetData: rendered CF_OBJECTDESCRIPTOR\r\n");
        else
            OleDbgOut3(
                "ContainerDoc_GetData: rendered CF_LINKSRCDESCRIPTOR\r\n");
#endif
        return NOERROR;

    } else if (lpContainerDoc->m_fEmbeddedObjectAvail) {

        /* OLE2NOTE: if this document contains a single OLE object
        **    (ie. cfEmbeddedObject data format is available), then
        **    the formats offered via our IDataObject must include
        **    the formats available from the OLE object itself.
        **    thus, we delegate this call to the IDataObject* of the
        **    OLE object.
        */

        if (lpformatetc->cfFormat == lpOleApp->m_cfEmbeddedObject) {
            LPPERSISTSTORAGE lpPersistStg =
                    (LPPERSISTSTORAGE)ContainerDoc_GetSingleOleObject(
                            lpContainerDoc,
                            &IID_IPersistStorage,
                            NULL
            );

            if (! lpPersistStg)
                return ResultFromScode(DATA_E_FORMATETC);

            /* render CF_EMBEDDEDOBJECT by asking the storage to save
            **    into the pStg of the caller.
            */

            hrErr = OleStdGetOleObjectData(
                    lpPersistStg,
                    lpformatetc,
                    lpMedium,
                    FALSE   /* fUseMemory -- (use file-base stg) */
            );
            OleStdRelease((LPUNKNOWN)lpPersistStg);
            if (hrErr != NOERROR) {
                sc = GetScode(hrErr);
                goto error;
            }
            OleDbgOut3(
                    "ContainerDoc_GetData: rendered CF_EMBEDDEDOBJECT\r\n");
            return hrErr;

        } else if (lpformatetc->cfFormat == CF_METAFILEPICT) {

            /* OLE2NOTE: as a container which draws objects, when a single
            **    OLE object is copied, we can give the Metafile picture of
            **    the object. we will offer this picture when asked either
            **    for the actual drawing aspect that we are displaying for
            **    the object or for DVASPECT_DOCPRINT (if asked to render
            **    it to a printer)
            */
            LPCONTAINERLINE lpContainerLine;
            LPOLEOBJECT lpOleObj;

            // Verify caller asked for correct medium
            if (!(lpformatetc->tymed & TYMED_MFPICT)) {
                sc = DATA_E_FORMATETC;
                goto error;
            }

            lpOleObj = (LPOLEOBJECT)ContainerDoc_GetSingleOleObject(
                    lpContainerDoc,
                    &IID_IOleObject,
                    (LPCONTAINERLINE FAR*)&lpContainerLine
            );

            if (! lpOleObj) {
                sc = E_OUTOFMEMORY;     // could not load object
                goto error;
            }
            if (lpformatetc->dwAspect &
                    (lpContainerLine->m_dwDrawAspect | DVASPECT_DOCPRINT)) {
                /* render CF_METAFILEPICT by drawing the object into
                **    a metafile DC
                */
                lpMedium->u.hGlobal = OleStdGetMetafilePictFromOleObject(
                        lpOleObj, lpContainerLine->m_dwDrawAspect);
                OleStdRelease((LPUNKNOWN)lpOleObj);
                if (! lpMedium->u.hGlobal) {
                    sc = E_OUTOFMEMORY;
                    goto error;
                }

                lpMedium->tymed = TYMED_MFPICT;
                OleDbgOut3(
                    "ContainerDoc_GetData: rendered CF_METAFILEPICT\r\n");
                return NOERROR;
            } else {
                // improper aspect requested
                OleStdRelease((LPUNKNOWN)lpOleObj);
                return ResultFromScode(DATA_E_FORMATETC);
            }

        } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSource) {
            if (lpOleDoc->m_fLinkSourceAvail) {
                LPMONIKER lpmk;

                lpmk = ContainerLine_GetFullMoniker(
                        lpContainerDoc->m_lpSrcContainerLine,
                        GETMONIKER_FORCEASSIGN
                );
                if (lpmk) {
                    hrErr = OleStdGetLinkSourceData(
                            lpmk,
                            &lpContainerDoc->m_clsidOleObjCopied,
                            lpformatetc,
                            lpMedium
                    );
                    OleStdRelease((LPUNKNOWN)lpmk);
                    if (hrErr != NOERROR) {
                        sc = GetScode(hrErr);
                        goto error;
                    }
                    OleDbgOut3(
                        "ContainerDoc_GetData: rendered CF_LINKSOURCE\r\n");
                    return hrErr;
                } else {
                    sc = DATA_E_FORMATETC;
                    goto error;
                }
            } else {
                sc = DATA_E_FORMATETC;
                goto error;
            }

        } 
#if defined( OPTIONAL_ADVANCED_DATA_TRANSFER )
        /* OLE2NOTE: optionally, a container that wants to have a
        **    potentially richer data transfer, can enumerate the data
        **    formats from the OLE object's cache and offer them too. if
        **    the object has a special handler, then it might be able to
        **    render additional data formats. in this case, the
        **    container must delegate the GetData call to the object if
        **    it does not directly support the format.
        **    
        **    CNTROUTL does NOT enumerate the cache; it implements the
        **    simpler strategy of offering a static list of formats.
        **    thus the delegation is NOT required.
        */
      else {

            /* OLE2NOTE: we delegate this call to the IDataObject* of the
            **    OLE object.
            */
            LPDATAOBJECT lpDataObj;

            lpDataObj = (LPDATAOBJECT)ContainerDoc_GetSingleOleObject(
                    lpContainerDoc,
                    &IID_IDataObject,
                    NULL
            );

            if (! lpDataObj) {
                sc = DATA_E_FORMATETC;
                goto error;
            }

            OLEDBG_BEGIN2("ContainerDoc_GetData: delegate to OLE obj\r\n")
            hrErr=lpDataObj->lpVtbl->GetData(lpDataObj,lpformatetc,lpMedium);
            OLEDBG_END2

            OleStdRelease((LPUNKNOWN)lpDataObj);
            return hrErr;
        }
#endif  // ! OPTIONAL_ADVANCED_DATA_TRANSFER

    }

    // if we get here then we do NOT support the requested format
    sc = DATA_E_FORMATETC;

error:
    return ResultFromScode(sc);
}


/* ContainerDoc_GetDataHere
 * ------------------------
 *
 * Render data from the document on a CALLER allocated STGMEDIUM.
 *      This routine is called via IDataObject::GetDataHere.
 */
HRESULT ContainerDoc_GetDataHere (
        LPCONTAINERDOC          lpContainerDoc,
        LPFORMATETC             lpformatetc,
        LPSTGMEDIUM             lpMedium
)
{
    LPOLEDOC  lpOleDoc = (LPOLEDOC)lpContainerDoc;
    LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
    LPOLEAPP  lpOleApp = (LPOLEAPP)lpContainerApp;
    LPOUTLINEAPP  lpOutlineApp = (LPOUTLINEAPP)lpContainerApp;
    HRESULT hrErr;

    // OLE2NOTE: we must set out pointer parameters to NULL
    lpMedium->pUnkForRelease = NULL;

    // we only support  IStorage medium
    if (lpformatetc->cfFormat == lpContainerApp->m_cfCntrOutl) {
        if (!(lpformatetc->tymed & TYMED_ISTORAGE))
            return ResultFromScode(DATA_E_FORMATETC);

        if (lpMedium->tymed == TYMED_ISTORAGE) {
            /* Caller has allocated the storage. we must copy all of our
            **    data into his storage.
            */

            /*  OLE2NOTE: we must be sure to write our class ID into our
            **    storage. this information is used by OLE to determine the
            **    class of the data stored in our storage. 
            */
            if((hrErr=WriteClassStg(lpMedium->u.pstg,&CLSID_APP)) != NOERROR) 
                return hrErr;
            
            OutlineDoc_SaveSelToStg(
                    (LPOUTLINEDOC)lpContainerDoc,
                    NULL,   /* entire doc */
                    lpContainerApp->m_cfCntrOutl,
                    lpMedium->u.pstg,
                    FALSE
            );
            OleStdCommitStorage(lpMedium->u.pstg);

            OleDbgOut3("ContainerDoc_GetDataHere: rendered CF_CNTROUTL\r\n");
            return NOERROR;
        } else {
            // we only support IStorage medium
            return ResultFromScode(DATA_E_FORMATETC);
        }

    } else if (lpContainerDoc->m_fEmbeddedObjectAvail) {

        /* OLE2NOTE: if this document contains a single OLE object
        **    (ie. cfEmbeddedObject data format is available), then
        **    the formats offered via our IDataObject must include
        **    CF_EMBEDDEDOBJECT and the formats available from the OLE
        **    object itself.
        */

        if (lpformatetc->cfFormat == lpOleApp->m_cfEmbeddedObject) {
            LPPERSISTSTORAGE lpPersistStg =
                    (LPPERSISTSTORAGE)ContainerDoc_GetSingleOleObject(
                            lpContainerDoc,
                            &IID_IPersistStorage,
                            NULL
            );

            if (! lpPersistStg) {
                return ResultFromScode(E_OUTOFMEMORY);
            }
            /* render CF_EMBEDDEDOBJECT by asking the storage to save
            **    into the pStg of the caller.
            */

            hrErr = OleStdGetOleObjectData(
                    lpPersistStg,
                    lpformatetc,
                    lpMedium,
                    FALSE   /* fUseMemory -- (use file-base stg) */
            );
            OleStdRelease((LPUNKNOWN)lpPersistStg);
            if (hrErr != NOERROR) {
                return hrErr;
            }
            OleDbgOut3(
                "ContainerDoc_GetDataHere: rendered CF_EMBEDDEDOBJECT\r\n");
            return hrErr;

        } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSource) {
            if (lpOleDoc->m_fLinkSourceAvail) {
                LPMONIKER lpmk;

                lpmk = ContainerLine_GetFullMoniker(
                        lpContainerDoc->m_lpSrcContainerLine,
                        GETMONIKER_FORCEASSIGN
                );
                if (lpmk) {
                    hrErr = OleStdGetLinkSourceData(
                            lpmk,
                            &lpContainerDoc->m_clsidOleObjCopied,
                            lpformatetc,
                            lpMedium
                    );
                    OleStdRelease((LPUNKNOWN)lpmk);
                    OleDbgOut3(
                      "ContainerDoc_GetDataHere: rendered CF_LINKSOURCE\r\n");
                    return hrErr;
                } else {
                    return ResultFromScode(E_FAIL);
                }
            } else {
                return ResultFromScode(DATA_E_FORMATETC);
            }

        } 
#if defined( OPTIONAL_ADVANCED_DATA_TRANSFER )
        /* OLE2NOTE: optionally, a container that wants to have a
        **    potentially richer data transfer, can enumerate the data
        **    formats from the OLE object's cache and offer them too. if
        **    the object has a special handler, then it might be able to
        **    render additional data formats. in this case, the
        **    container must delegate the GetData call to the object if
        **    it does not directly support the format.
        **    
        **    CNTROUTL does NOT enumerate the cache; it implements the
        **    simpler strategy of offering a static list of formats.
        **    thus the delegation is NOT required.
        */
        else {

            /* OLE2NOTE: we delegate this call to the IDataObject* of the
            **    OLE object.
            */
            LPDATAOBJECT lpDataObj;

            lpDataObj = (LPDATAOBJECT)ContainerDoc_GetSingleOleObject(
                    lpContainerDoc,
                    &IID_IDataObject,
                    NULL
            );

            if (! lpDataObj)
                return ResultFromScode(DATA_E_FORMATETC);

            OLEDBG_BEGIN2("ContainerDoc_GetDataHere: delegate to OLE obj\r\n")
            hrErr = lpDataObj->lpVtbl->GetDataHere(
                    lpDataObj,
                    lpformatetc,
                    lpMedium
            );
            OLEDBG_END2

            OleStdRelease((LPUNKNOWN)lpDataObj);
            return hrErr;
        }
#endif  // OPTIONAL_ADVANCED_DATA_TRANSFER
    } else {
        return ResultFromScode(DATA_E_FORMATETC);
    }
}


/* ContainerDoc_QueryGetData
 * -------------------------
 *
 * Answer if a particular data format is supported via GetData/GetDataHere.
 *      This routine is called via IDataObject::QueryGetData.
 */
HRESULT ContainerDoc_QueryGetData (
        LPCONTAINERDOC          lpContainerDoc,
        LPFORMATETC             lpformatetc
)
{
    LPOLEDOC  lpOleDoc = (LPOLEDOC)lpContainerDoc;
    LPOUTLINEDOC  lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
    LPOLEAPP  lpOleApp = (LPOLEAPP)lpContainerApp;
    LPOUTLINEAPP  lpOutlineApp = (LPOUTLINEAPP)lpContainerApp;
    LPDATAOBJECT  lpDataObj = NULL;
    LPCONTAINERLINE lpContainerLine = NULL;
    SCODE sc;
    HRESULT hrErr;

    if (lpContainerDoc->m_fEmbeddedObjectAvail) {
        lpDataObj = (LPDATAOBJECT)ContainerDoc_GetSingleOleObject(
                    lpContainerDoc,
                    &IID_IDataObject,
                    (LPCONTAINERLINE FAR*)&lpContainerLine
        );
    }

    /* Caller is querying if we support certain format but does not
    **    want any data actually returned.
    */
    if (lpformatetc->cfFormat == lpContainerApp->m_cfCntrOutl) {
        // we only support ISTORAGE medium
        sc = GetScode( OleStdQueryFormatMedium(lpformatetc, TYMED_ISTORAGE) );

    } else if (lpformatetc->cfFormat == lpOleApp->m_cfEmbeddedObject &&
            lpContainerDoc->m_fEmbeddedObjectAvail ) {
        sc = GetScode( OleStdQueryOleObjectData(lpformatetc) );

    } else if (lpformatetc->cfFormat == lpOleApp->m_cfLinkSource &&
            lpOleDoc->m_fLinkSourceAvail) {
        sc = GetScode( OleStdQueryLinkSourceData(lpformatetc) );

    } else if((lpformatetc->cfFormat == (lpOutlineApp)->m_cfOutline ||
            lpformatetc->cfFormat == CF_TEXT) ) {
        // we only support HGLOBAL medium
        sc = GetScode( OleStdQueryFormatMedium(lpformatetc, TYMED_HGLOBAL) );

    } else if ( lpformatetc->cfFormat == lpOleApp->m_cfObjectDescriptor ||
        (lpformatetc->cfFormat == lpOleApp->m_cfLinkSrcDescriptor &&
            lpOleDoc->m_fLinkSourceAvail) ) {
        sc = GetScode( OleStdQueryObjectDescriptorData(lpformatetc) );

    } else if (lpformatetc->cfFormat == CF_METAFILEPICT &&
            lpContainerDoc->m_fEmbeddedObjectAvail && lpContainerLine &&
            (lpformatetc->dwAspect &
                (lpContainerLine->m_dwDrawAspect | DVASPECT_DOCPRINT))) {

        /* OLE2NOTE: as a container which draws objects, when a single
        **    OLE object is copied, we can give the Metafile picture of
        **    the object. we will offer this picture when asked either
        **    for the actual drawing aspect that we are displaying for
        **    the object or for DVASPECT_DOCPRINT (if asked to render
        **    it to a printer)
        */
        // we only support MFPICT medium
        sc = GetScode( OleStdQueryFormatMedium(lpformatetc, TYMED_MFPICT) );

    } else if (lpDataObj) {

        /* OLE2NOTE: if this document contains a single OLE object
        **    (ie. cfEmbeddedObject data format is available), then
        **    the formats offered via our IDataObject must include
        **    the formats available from the OLE object itself.
        **    thus we delegate this call to the IDataObject* of the
        **    OLE object.
        */
        OLEDBG_BEGIN2("ContainerDoc_QueryGetData: delegate to OLE obj\r\n")
        hrErr = lpDataObj->lpVtbl->QueryGetData(lpDataObj, lpformatetc);
        OLEDBG_END2

        sc = GetScode(hrErr);

    } else {
        sc = DATA_E_FORMATETC;
    }

    if (lpDataObj)
        OleStdRelease((LPUNKNOWN)lpDataObj);
    return ResultFromScode(sc);
}



/* ContainerDoc_SetData
 * --------------------
 *
 * Set (modify) data of the document.
 *      This routine is called via IDataObject::SetData.
 */
HRESULT ContainerDoc_SetData (
        LPCONTAINERDOC          lpContainerDoc,
        LPFORMATETC             lpformatetc,
        LPSTGMEDIUM             lpmedium,
        BOOL                    fRelease
)
{
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
    LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

    if (lpformatetc->cfFormat == lpOutlineApp->m_cfOutline)
        OutlineDoc_PasteOutlineData(lpOutlineDoc,lpmedium->u.hGlobal,-1);
    else if (lpformatetc->cfFormat == CF_TEXT)
        OutlineDoc_PasteTextData(lpOutlineDoc,lpmedium->u.hGlobal,-1);
    else
        return ResultFromScode(DATA_E_FORMATETC);

    return NOERROR;
}


/* ContainerDoc_EnumFormatEtc
 * --------------------------
 *
 * Return an enumerator which enumerates the data accepted/offered by
 *      the document.
 *      This routine is called via IDataObject::SetData.
 */
HRESULT ContainerDoc_EnumFormatEtc(
        LPCONTAINERDOC          lpContainerDoc,
        DWORD                   dwDirection,
        LPENUMFORMATETC FAR*    lplpenumFormatEtc
)
{
    LPOLEDOC lpOleDoc = (LPOLEDOC)lpContainerDoc;
    LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
    LPOLEAPP  lpOleApp = (LPOLEAPP)g_lpApp;
    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)lpOleApp;
    int nActualFmts;
    int i;
    SCODE sc = S_OK;

    /* the Container-Only version of Outline does NOT offer
    **    IDataObject interface from its User documents.
    */
    if (! lpOutlineDoc->m_fDataTransferDoc)
        return ResultFromScode(E_FAIL);

    if (dwDirection == DATADIR_GET) {
        if (lpContainerDoc->m_fEmbeddedObjectAvail) {

            /* OLE2NOTE: if this document contains a single OLE object
            **    (ie. cfEmbeddedObject data format is available), then
            **    the formats offered via our enumerator must include
            **    the formats available from the OLE object itself. we
            **    have previously set up a special array of FORMATETC's
            **    in OutlineDoc_CreateDataTransferDoc routine which includes
            **    the combination of data we offer directly and data
            **    offered by the OLE object.
            */

            /* If the document does not have a Moniker, then exclude
            **    CF_LINKSOURCE CF_LINKSRCDESCRIPTOR from the list of
            **    formats available. these formats are deliberately
            **    listed last in the array of possible "Get" formats.
            */
            nActualFmts = lpContainerApp->m_nSingleObjGetFmts;
            if (! lpOleDoc->m_fLinkSourceAvail)
                nActualFmts -= 2;

            // set correct dwDrawAspect for METAFILEPICT of object copied
            for (i = 0; i < nActualFmts; i++) {
                if (lpContainerApp->m_arrSingleObjGetFmts[i].cfFormat == 
                                                            CF_METAFILEPICT) {
                    lpContainerApp->m_arrSingleObjGetFmts[i].dwAspect = 
                            lpContainerDoc->m_dwAspectOleObjCopied;
                    break;  // DONE
                }
            }
            *lplpenumFormatEtc = OleStdEnumFmtEtc_Create(
                    nActualFmts, lpContainerApp->m_arrSingleObjGetFmts);
            if (*lplpenumFormatEtc == NULL)
                sc = E_OUTOFMEMORY;

        } else {

            /* This document does NOT offer cfEmbeddedObject,
            **    therefore we can simply enumerate the
            **    static list of formats that we handle directly.
            */
            *lplpenumFormatEtc = OleStdEnumFmtEtc_Create(
                    lpOleApp->m_nDocGetFmts, lpOleApp->m_arrDocGetFmts);
            if (*lplpenumFormatEtc == NULL)
                sc = E_OUTOFMEMORY;
        }
    } else if (dwDirection == DATADIR_SET) {
        /* OLE2NOTE: a document that is used to transfer data
        **    (either via the clipboard or drag/drop does NOT
        **    accept SetData on ANY format!
        */
        sc = E_NOTIMPL;

    } else {
        sc = E_NOTIMPL;
    }

    return ResultFromScode(sc);
}


#if defined( OPTIONAL_ADVANCED_DATA_TRANSFER )
/* OLE2NOTE: optionally, a container that wants to have a
**    potentially richer data transfer, can enumerate the data
**    formats from the OLE object's cache and offer them too. if
**    the object has a special handler, then it might be able to
**    render additional data formats.
**    
**    CNTROUTL does NOT enumerate the cache; it implements the simpler
**    strategy of offering a static list of formats. the following
**    function is included in order to illustrates how enumerating the
**    cache could be done. CNTROUTL does NOT call this function.
**    
*/

/* ContainerDoc_SetupDocGetFmts
** ----------------------------
**    Setup the combined list of formats that this data transfer
**    ContainerDoc which contains a single OLE object should offer.
**
**    OLE2NOTE: The list of formats that should be offered when a
**    single OLE object is being transfered include the following:
**          * any formats the container app wants to give
**          * CF_EMBEDDEDOBJECT
**          * CF_METAFILEPICT
**          * any formats that the OLE object's cache can offer directly
**
**    We will offer the following formats in the order given:
**                  1. CF_CNTROUTL
**                  2. CF_EMBEDDEDOBJECT
**                  3. CF_OBJECTDESCRIPTOR
**                  4. CF_METAFILEPICT
**                  5. <data formats from OLE object's cache>
**                  6. CF_OUTLINE
**                  7. CF_TEXT
**                  8. CF_LINKSOURCE
**                  9. CF_LINKSRCDESCRIPTOR
*/
BOOL ContainerDoc_SetupDocGetFmts(
        LPCONTAINERDOC          lpContainerDoc,
        LPCONTAINERLINE         lpContainerLine
)
{
    LPOLEDOC lpOleDoc = (LPOLEDOC)lpContainerDoc;
    LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
    LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
    LPOLECACHE lpOleCache;
    HRESULT hrErr;
    STATDATA        StatData;
    LPENUMSTATDATA  lpEnumStatData = NULL;
    LPFORMATETC lparrDocGetFmts = NULL;
    UINT nOleObjFmts = 0;
    UINT nTotalFmts;
    UINT i;
    UINT iFmt;

    lpOleCache = (LPOLECACHE)OleStdQueryInterface(
            (LPUNKNOWN)lpContainerLine->m_lpOleObj,
            &IID_IOleCache
    );
    if (lpOleCache) {
        OLEDBG_BEGIN2("IOleCache::EnumCache called\r\n")
        hrErr = lpOleCache->lpVtbl->EnumCache(
                lpOleCache,
                (LPENUMSTATDATA FAR*)&lpEnumStatData
        );
        OLEDBG_END2
    }

    if (lpEnumStatData) {
        /* Cache enumerator is available. count the number of
        **    formats that the OLE object's cache offers.
        */
        while(lpEnumStatData->lpVtbl->Next(
                    lpEnumStatData,
                    1,
                    (LPSTATDATA)&StatData,
                    NULL) == NOERROR) {
            nOleObjFmts++;
            // OLE2NOTE: we MUST free the TargetDevice
            OleStdFree(StatData.formatetc.ptd);
        }
        lpEnumStatData->lpVtbl->Reset(lpEnumStatData);  // reset for next loop
    }

    /* OLE2NOTE: the maximum total number of formats that our IDataObject
    **    could offer equals the sum of the following:
    **          n offered by the OLE object's cache
    **       +  n normally offered by our app
    **       +  1 CF_EMBEDDEDOBJECT
    **       +  1 CF_METAFILEPICT
    **       +  1 CF_LINKSOURCE
    **       +  1 CF_LINKSRCDESCRIPTOR
    **    the actual number of formats that we can offer could be less
    **    than this total if there is any clash between the formats
    **    that we offer directly and those offered by the cache. if
    **    there is a clash, the container's rendering overrides that of
    **    the object. eg.: as a container transfering an OLE object we
    **    should directly offer CF_METAFILEPICT to guarantee that this
    **    format is always available. thus, if the cache offers
    **    CF_METAFILEPICT then it is skipped.
    */
    nTotalFmts = nOleObjFmts + lpOleApp->m_nDocGetFmts + 4;
    lparrDocGetFmts = (LPFORMATETC)New (nTotalFmts * sizeof(FORMATETC));

    if (! OleDbgVerifySz(lparrDocGetFmts != NULL,
                                            "Error allocating arrDocGetFmts"))
            return FALSE;

    for (i = 0, iFmt = 0; i < lpOleApp->m_nDocGetFmts; i++) {
        _fmemcpy((LPFORMATETC)&lparrDocGetFmts[iFmt++],
                (LPFORMATETC)&lpOleApp->m_arrDocGetFmts[i],
                sizeof(FORMATETC)
        );
        if (lpOleApp->m_arrDocGetFmts[i].cfFormat ==
            lpContainerApp->m_cfCntrOutl) {
            /* insert CF_EMBEDDEDOBJECT, CF_METAFILEPICT, and formats
            **    available from the OLE object's cache following
            **    CF_CNTROUTL.
            */
            lparrDocGetFmts[iFmt].cfFormat = lpOleApp->m_cfEmbeddedObject;
            lparrDocGetFmts[iFmt].ptd      = NULL;
            lparrDocGetFmts[iFmt].dwAspect = DVASPECT_CONTENT;
            lparrDocGetFmts[iFmt].tymed    = TYMED_ISTORAGE;
            lparrDocGetFmts[iFmt].lindex   = -1;
            iFmt++;
            lparrDocGetFmts[iFmt].cfFormat = CF_METAFILEPICT;
            lparrDocGetFmts[iFmt].ptd      = NULL;
            lparrDocGetFmts[iFmt].dwAspect = lpContainerLine->m_dwDrawAspect;
            lparrDocGetFmts[iFmt].tymed    = TYMED_MFPICT;
            lparrDocGetFmts[iFmt].lindex   = -1;
            iFmt++;

            if (lpEnumStatData) {
                /* Cache enumerator is available. enumerate all of
                **    the formats that the OLE object's cache offers.
                */
                while(lpEnumStatData->lpVtbl->Next(
                        lpEnumStatData,
                        1,
                        (LPSTATDATA)&StatData,
                        NULL) == NOERROR) {
                    /* check if the format clashes with one of our fmts */
                    if (StatData.formatetc.cfFormat != CF_METAFILEPICT
                        && ! OleStdIsDuplicateFormat(
                                (LPFORMATETC)&StatData.formatetc,
                                lpOleApp->m_arrDocGetFmts,
                                lpOleApp->m_nDocGetFmts)) {
                        OleStdCopyFormatEtc(
                                &(lparrDocGetFmts[iFmt]),&StatData.formatetc);
                        iFmt++;
                    }
                    // OLE2NOTE: we MUST free the TargetDevice
                    OleStdFree(StatData.formatetc.ptd);
                }
            }
        }
    }

    if (lpOleCache)
        OleStdRelease((LPUNKNOWN)lpOleCache);

    /* append CF_LINKSOURCE format */
    lparrDocGetFmts[iFmt].cfFormat = lpOleApp->m_cfLinkSource;
    lparrDocGetFmts[iFmt].ptd      = NULL;
    lparrDocGetFmts[iFmt].dwAspect = DVASPECT_CONTENT;
    lparrDocGetFmts[iFmt].tymed    = TYMED_ISTREAM;
    lparrDocGetFmts[iFmt].lindex   = -1;
    iFmt++;

    /* append CF_LINKSRCDESCRIPTOR format */
    lparrDocGetFmts[iFmt].cfFormat = lpOleApp->m_cfLinkSrcDescriptor;
    lparrDocGetFmts[iFmt].ptd      = NULL;
    lparrDocGetFmts[iFmt].dwAspect = DVASPECT_CONTENT;
    lparrDocGetFmts[iFmt].tymed    = TYMED_HGLOBAL;
    lparrDocGetFmts[iFmt].lindex   = -1;
    iFmt++;

    lpContainerDoc->m_lparrDocGetFmts = lparrDocGetFmts;
    lpContainerDoc->m_nDocGetFmts = iFmt;

    if (lpEnumStatData)
        OleStdVerifyRelease(
                (LPUNKNOWN)lpEnumStatData,
                "Cache enumerator not released properly"
        );

    return TRUE;
}
#endif  // OPTIONAL_ADVANCED_DATA_TRANSFER

#endif  // OLE_CNTR

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.