/*************************************************************************
**
**    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"

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!";
char ErrMsgClipboardChanged[] = "Contents of clipboard have changed!\r\nNo paste performed.";



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

// IDataObject::QueryInterface
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);
}


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

	OleDbgAddRefMethod(lpThis, "IDataObject");

	return OleDoc_AddRef((LPOLEDOC)lpOleDoc);
}


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

	OleDbgReleaseMethod(lpThis, "IDataObject");

	return OleDoc_Release((LPOLEDOC)lpOleDoc);
}


// IDataObject::GetData
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);
#endif
#if defined( OLE_CNTR )
	// Call OLE Container specific version of this function
	hrErr = ContainerDoc_GetData(
			(LPCONTAINERDOC)lpOleDoc,
			lpFormatetc,
			lpMedium
	);
#endif

	OLEDBG_END2
	return hrErr;
}


// IDataObject::GetDataHere
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
	);
#endif
#if defined( OLE_CNTR )
	// Call OLE Container specific version of this function
	hrErr = ContainerDoc_GetDataHere(
			(LPCONTAINERDOC)lpOleDoc,
			lpFormatetc,
			lpMedium
	);
#endif

	OLEDBG_END2
	return hrErr;
}


// IDataObject::QueryGetData
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);
#endif
#if defined( OLE_CNTR )
	// Call OLE Container specific version of this function
	hrErr = ContainerDoc_QueryGetData((LPCONTAINERDOC)lpOleDoc, lpFormatetc);
#endif

	OLEDBG_END2
	return hrErr;
}


// IDataObject::GetCanonicalFormatEtc
STDMETHODIMP OleDoc_DataObj_GetCanonicalFormatEtc(
		LPDATAOBJECT        lpThis,
		LPFORMATETC         lpformatetc,
		LPFORMATETC         lpformatetcOut
)
{
	HRESULT hrErr;
	OleDbgOut2("OleDoc_DataObj_GetCanonicalFormatEtc\r\n");

	if (!lpformatetcOut)
		return ResultFromScode(E_INVALIDARG);

	/* OLE2NOTE: we must make sure to set all out parameters to NULL. */
	lpformatetcOut->ptd = NULL;

	if (!lpformatetc)
		return ResultFromScode(E_INVALIDARG);

	// OLE2NOTE: we must validate that the format requested is supported
	if ((hrErr=lpThis->lpVtbl->QueryGetData(lpThis,lpformatetc)) != NOERROR)
		return hrErr;

	/* OLE2NOTE: an app that is insensitive to target device (as the
	**    Outline Sample is) should fill in the lpformatOut parameter
	**    but NULL out the "ptd" field; it should return NOERROR if the
	**    input formatetc->ptd what non-NULL. this tells the caller
	**    that it is NOT necessary to maintain a separate screen
	**    rendering and printer rendering. if should return
	**    DATA_S_SAMEFORMATETC if the input and output formatetc's are
	**    identical.
	*/

	*lpformatetcOut = *lpformatetc;
	if (lpformatetc->ptd == NULL)
		return ResultFromScode(DATA_S_SAMEFORMATETC);
	else {
		lpformatetcOut->ptd = NULL;
		return NOERROR;
	}
}


// IDataObject::SetData
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 = S_OK;
	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 )
	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->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->hGlobal,-1);
		OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );
		OLEDBG_END3
	} else {
		sc = DV_E_FORMATETC;
	}
#endif  // OLE_SERVER
#if 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;
#endif  // OLE_CNTR

error:

	/* OLE2NOTE: if fRelease==TRUE, then we must take
	**    responsibility to release the lpMedium. we should only do
	**    this if we are going to return NOERROR. if we do NOT
	**    accept the data, then we should NOT release the lpMedium.
	**    if fRelease==FALSE, then the caller retains ownership of
	**    the data.
	*/
	if (sc == S_OK && fRelease)
		ReleaseStgMedium(lpMedium);

	OLEDBG_END2
	return ResultFromScode(sc);

}


// IDataObject::EnumFormatEtc
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.
	**
	**    An object implemented as a server EXE (as this sample
	**    is) may simply return OLE_S_USEREG to instruct the OLE
	**    DefHandler to call the OleReg* helper API which uses info in
	**    the registration database. Alternatively, the OleRegEnumFormatEtc
	**    API may be called directly. Objects implemented as a server
	**    DLL may NOT return OLE_S_USEREG; they must call the OleReg*
	**    API or provide their own implementation. For EXE based
	**    objects it is more efficient to return OLE_S_USEREG, because
	**    in then the enumerator is instantiated in the callers
	**    process space and no LRPC remoting is required.
	*/
	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
	);
#endif
#if defined( OLE_CNTR )
	// Call OLE Container specific version of this function
	hrErr = ContainerDoc_EnumFormatEtc(
			(LPCONTAINERDOC)lpOleDoc,
			dwDirection,
			lplpenumFormatEtc
	);
#endif

	OLEDBG_END2
	return hrErr;
}


// IDataObject::DAdvise
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;

		/* OLE2NOTE: we should validate if the caller is setting up an
		**    Advise for a data type that we support. we must
		**    explicitly allow an advise for the "wildcard" advise.
		*/
		if ( !( lpFormatetc->cfFormat == 0 &&
				lpFormatetc->ptd == NULL &&
				lpFormatetc->dwAspect == -1L &&
				lpFormatetc->lindex == -1L &&
				lpFormatetc->tymed == -1L) &&
			 (hrErr = OleDoc_DataObj_QueryGetData(lpThis, lpFormatetc))
																!= NOERROR) {
			sc = GetScode(hrErr);
			goto error;
		}

                if (lpServerDoc->m_OleDoc.m_fObjIsClosing)
                {
                    //  We don't accept any more Advise's once we're closing
                    sc = OLE_E_ADVISENOTSUPPORTED;
                    goto error;
                }

		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;
	}
#endif  // OLE_SVR
#if 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  // OLE_CNTR

error:
	OLEDBG_END2
	return ResultFromScode(sc);
}



// IDataObject::DUnadvise
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;
	}
#endif
#if 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);
}


// IDataObject::EnumDAdvise
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;
	}
#endif
#if 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_CopyCommand
 * ------------------
 *  Copy selection to clipboard.
 *  Post to the clipboard the formats that the app can render.
 *  the actual data is not rendered at this time. using the
 *  delayed rendering technique, Windows will send the clipboard
 *  owner window either a WM_RENDERALLFORMATS or a WM_RENDERFORMAT
 *  message when the actual data is requested.
 *
 *    OLE2NOTE: the normal delayed rendering technique where Windows
 *    sends the clipboard owner window either a WM_RENDERALLFORMATS or
 *    a WM_RENDERFORMAT message when the actual data is requested is
 *    NOT exposed to the app calling OleSetClipboard. OLE internally
 *    creates its own window as the clipboard owner and thus our app
 *    will NOT get these WM_RENDER messages.
 */
void OleDoc_CopyCommand(LPOLEDOC lpSrcOleDoc)
{
	LPOUTLINEDOC lpSrcOutlineDoc = (LPOUTLINEDOC)lpSrcOleDoc;
	LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
	LPOUTLINEDOC lpClipboardDoc;

	/* squirrel away a copy of the current selection to the ClipboardDoc */
	lpClipboardDoc = OutlineDoc_CreateDataTransferDoc(lpSrcOutlineDoc);

	if (! lpClipboardDoc)
		return;     // Error: could not create DataTransferDoc

	lpOutlineApp->m_lpClipboardDoc = (LPOUTLINEDOC)lpClipboardDoc;

	/* OLE2NOTE: initially the Doc object is created with a 0 ref
	**    count. in order to have a stable Doc object during the
	**    process of initializing the Doc instance and transfering it
	**    to the clipboard, we intially AddRef the Doc ref cnt and later
	**    Release it. This initial AddRef is artificial; it is simply
	**    done to guarantee that a harmless QueryInterface followed by
	**    a Release does not inadvertantly force our object to destroy
	**    itself prematurely.
	*/
	OleDoc_AddRef((LPOLEDOC)lpClipboardDoc);

	/* OLE2NOTE: the OLE 2.0 style to put data onto the clipboard is to
	**    give the clipboard a pointer to an IDataObject interface that
	**    is able to statisfy IDataObject::GetData calls to render
	**    data. in our case we give the pointer to the ClipboardDoc
	**    which holds a cloned copy of the current user's selection.
	*/
	OLEDBG_BEGIN2("OleSetClipboard called\r\n")
	OleSetClipboard((LPDATAOBJECT)&((LPOLEDOC)lpClipboardDoc)->m_DataObject);
	OLEDBG_END2

	OleDoc_Release((LPOLEDOC)lpClipboardDoc);   // rel artificial AddRef above
}


/* 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.
	*/
	_fmemset((LPOLEUIPASTESPECIAL)&ouiPasteSpl, 0, sizeof(ouiPasteSpl));
	ouiPasteSpl.cbStruct = sizeof(ouiPasteSpl);       //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;
	ouiPasteSpl.cClsidExclude = 0;

	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,
				(LPSIZEL)&ouiPasteSpl.sizel
		);

		OutlineDoc_SetRedraw ( lpOutlineDoc, TRUE );

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

	} else if (uInt == OLEUI_PSERR_CLIPBOARDCHANGED) {
		/* OLE2NOTE: this error code is returned when the contents of
		**    the clipboard change while the PasteSpecial dialog is up.
		**    in this situation the PasteSpecial dialog automatically
		**    brings itself down and NO paste operation should be performed.
		*/
		OutlineApp_ErrorMessage(lpOutlineApp, ErrMsgClipboardChanged);
	}

error:

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

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



/* OleDoc_CreateDataTransferDoc
 * ----------------------------
 *
 *      Create a document to be use to transfer data (either via a
 *  drag/drop operation of the clipboard). Copy the selection of the
 *  source doc to the data transfer document. A data transfer document is
 *  the same as a document that is created by the user except that it is
 *  NOT made visible to the user. it is specially used to hold a copy of
 *  data that the user should not be able to change.
 *
 *  OLE2NOTE: in the OLE version the data transfer document is used
 *      specifically to provide an IDataObject* that renders the data copied.
 */
LPOUTLINEDOC OleDoc_CreateDataTransferDoc(LPOLEDOC lpSrcOleDoc)
{
	LPOUTLINEDOC lpSrcOutlineDoc = (LPOUTLINEDOC)lpSrcOleDoc;
	LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
	LPOUTLINEDOC lpDestOutlineDoc;
	LPLINELIST lpSrcLL = &lpSrcOutlineDoc->m_LineList;
	LINERANGE lrSel;
	int nCopied;

	lpDestOutlineDoc = OutlineApp_CreateDoc(lpOutlineApp, TRUE);
	if (! lpDestOutlineDoc) return NULL;

	// set the ClipboardDoc to an (Untitled) doc.
	if (! OutlineDoc_InitNewFile(lpDestOutlineDoc))
		goto error;

	LineList_GetSel(lpSrcLL, (LPLINERANGE)&lrSel);
	nCopied = LineList_CopySelToDoc(
			lpSrcLL,
			(LPLINERANGE)&lrSel,
			lpDestOutlineDoc
	);

	if (nCopied != (lrSel.m_nEndLine - lrSel.m_nStartLine + 1)) {
		OleDbgAssertSz(FALSE,"OleDoc_CreateDataTransferDoc: entire selection NOT copied\r\n");
		goto error;     // ERROR: all lines could NOT be copied
	}

#if defined( OLE_SERVER )
	{
		LPOLEDOC lpSrcOleDoc = (LPOLEDOC)lpSrcOutlineDoc;
		LPOLEDOC lpDestOleDoc = (LPOLEDOC)lpDestOutlineDoc;
		LPSERVERDOC lpDestServerDoc = (LPSERVERDOC)lpDestOutlineDoc;
		LPMONIKER lpmkDoc = NULL;
		LPMONIKER lpmkItem = NULL;

		/* If source document is able to provide a moniker, then the
		**    destination document (lpDestOutlineDoc) should offer
		**    CF_LINKSOURCE via its IDataObject interface that it gives
		**    to the clipboard or the drag/drop operation.
		**
		**    OLE2NOTE: we want to ask the source document if it can
		**    produce a moniker, but we do NOT want to FORCE moniker
		**    assignment at this point. we only want to FORCE moniker
		**    assignment later if a Paste Link occurs (ie. GetData for
		**    CF_LINKSOURCE). if the source document is able to give
		**    a moniker, then we store a pointer to the source document
		**    so we can ask it at a later time to get the moniker. we
		**    also save the range of the current selection so we can
		**    generate a proper item name later when Paste Link occurs.
		**    Also we need to give a string which identifies the source
		**    of the copy in the CF_OBJECTDESCRIPTOR format. this
		**    string is used to display in the PasteSpecial dialog. we
		**    get and store a TEMPFORUSER moniker which identifies the
		**    source of copy.
		*/
		lpDestOleDoc->m_lpSrcDocOfCopy = lpSrcOleDoc;
		lpmkDoc = OleDoc_GetFullMoniker(lpSrcOleDoc, GETMONIKER_TEMPFORUSER);
		if (lpmkDoc != NULL) {
			lpDestOleDoc->m_fLinkSourceAvail = TRUE;
			lpDestServerDoc->m_lrSrcSelOfCopy = lrSel;
			OleStdRelease((LPUNKNOWN)lpmkDoc);
		}
	}
#endif
#if defined( OLE_CNTR )
	{
		LPOLEDOC lpSrcOleDoc = (LPOLEDOC)lpSrcOutlineDoc;
		LPOLEDOC lpDestOleDoc = (LPOLEDOC)lpDestOutlineDoc;
		LPCONTAINERDOC lpDestContainerDoc = (LPCONTAINERDOC)lpDestOutlineDoc;

		/* If one line was copied from the source document, and it was a
		**    single OLE object, then the destination document should
		**    offer additional data formats to allow the transfer of
		**    the OLE object via IDataObject::GetData. Specifically, the
		**    following additional data formats are offered if a single
		**    OLE object is copied:
		**          CF_EMBEDDEDOBJECT
		**          CF_OBJECTDESCRIPTOR     (should be given even w/o object)
		**          CF_METAFILEPICT         (note: dwAspect depends on object)
		**          CF_LINKSOURCE           -- if linking is possible
		**          CF_LINKSOURCEDESCRIPTOR -- if linking is possible
		**
		**    optionally the container may give
		**          <data format available in OLE object's cache>
		*/

		if (nCopied == 1) {
			LPOLEOBJECT lpSrcOleObj;
			LPCONTAINERLINE lpSrcContainerLine;
			DWORD dwStatus;

			lpSrcContainerLine = (LPCONTAINERLINE)LineList_GetLine(
					lpSrcLL,
					lrSel.m_nStartLine
			);

			if (! lpSrcContainerLine)
				goto error;

			lpDestOleDoc->m_lpSrcDocOfCopy = lpSrcOleDoc;

			if ((((LPLINE)lpSrcContainerLine)->m_lineType==CONTAINERLINETYPE)
					&& ((lpSrcOleObj=lpSrcContainerLine->m_lpOleObj)!=NULL)) {

				lpDestContainerDoc->m_fEmbeddedObjectAvail = TRUE;
				lpSrcOleObj->lpVtbl->GetUserClassID(
						lpSrcOleObj,
						&lpDestContainerDoc->m_clsidOleObjCopied
				);
				lpDestContainerDoc->m_dwAspectOleObjCopied =
							lpSrcContainerLine->m_dwDrawAspect;

				/* OLE2NOTE: if the object is allowed to be linked
				**    to from the inside (ie. we are allowed to
				**    give out a moniker which binds to the running
				**    OLE object), then we want to offer
				**    CF_LINKSOURCE format. if the object is an OLE
				**    2.0 embedded object then it is allowed to be
				**    linked to from the inside. if the object is
				**    either an OleLink or an OLE 1.0 embedding
				**    then it can not be linked to from the inside.
				**    if we were a container/server app then we
				**    could offer linking to the outside of the
				**    object (ie. a pseudo object within our
				**    document). we are a container only app that
				**    does not support linking to ranges of its data.
				*/

				lpSrcOleObj->lpVtbl->GetMiscStatus(
						lpSrcOleObj,
						DVASPECT_CONTENT, /* aspect is not important */
						(LPDWORD)&dwStatus
				);
				if (! (dwStatus & OLEMISC_CANTLINKINSIDE)) {
					/* Our container supports linking to an embedded
					**    object. We want the lpDestContainerDoc to
					**    offer CF_LINKSOURCE via the IDataObject
					**    interface that it gives to the clipboard or
					**    the drag/drop operation. The link source will
					**    be identified by a composite moniker
					**    comprised of the FileMoniker of the source
					**    document and an ItemMoniker which identifies
					**    the OLE object inside the container. we do
					**    NOT want to force moniker assignment to the
					**    OLE object now (at copy time); we only want
					**    to FORCE moniker assignment later if a Paste
					**    Link occurs (ie. GetData for CF_LINKSOURCE).
					**    thus we store a pointer to the source document
					**    and the source ContainerLine so we can
					**    generate a proper ItemMoniker later when
					**    Paste Link occurs.
					*/
					lpDestOleDoc->m_fLinkSourceAvail = TRUE;
					lpDestContainerDoc->m_lpSrcContainerLine =
							lpSrcContainerLine;
				}
			}
		}
	}

#endif  // OLE_CNTR

	return lpDestOutlineDoc;

error:
	if (lpDestOutlineDoc)
		OutlineDoc_Destroy(lpDestOutlineDoc);

	return NULL;
}


/* 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;
	SIZEL           sizelInSrc = {0, 0};
	HGLOBAL         hMem = NULL;
	HGLOBAL         hMetaPict = NULL;
	STGMEDIUM       medium;

	if (fLink) {
#if defined( OLE_SERVER )
		return FALSE;       // server version of app does NOT support links
#endif
#if 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);
		sizelInSrc = lpOD->sizel;   // size of object/picture in source (opt.)
		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,
			(LPSIZEL)&sizelInSrc
	);

	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,
		LPSIZEL             lpSizelInSrc
)
{
#if defined( OLE_SERVER )
	/* call server specific version of the function. */
	return ServerDoc_PasteFormatFromData(
			(LPSERVERDOC)lpOleDoc,
			cfFormat,
			lpSrcDataObj,
			fLocalDataObj,
			fLink
	);
#endif
#if defined( OLE_CNTR )

	/* call container specific version of the function. */
	return ContainerDoc_PasteFormatFromData(
			(LPCONTAINERDOC)lpOleDoc,
			cfFormat,
			lpSrcDataObj,
			fLocalDataObj,
			fLink,
			fDisplayAsIcon,
			hMetaPict,
			lpSizelInSrc
	);
#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
	);
#endif
#if 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);
			CallIMonikerGetDisplayNameA(
				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;

	}
#endif
#if 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;
			SIZEL sizelOleObject;
			LPLINE lpLine = (LPLINE)lpContainerLine;

			/* 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) {
						CallIMonikerGetDisplayNameA(
							lpSrcMonikerOfCopy, pbc, NULL, &lpszSrcOfCopy);

						pbc->lpVtbl->Release(pbc);
						fFreeSrcOfCopy = TRUE;
					}
					lpSrcMonikerOfCopy->lpVtbl->Release(lpSrcMonikerOfCopy);
				}
			}

			/* OLE2NOTE: Get size that object is being drawn. If the
			**    object has been scaled because the user resized the
			**    object, then we want to pass the scaled size of the
			**    object in the ObjectDescriptor rather than the size
			**    that the object would return via
			**    IOleObject::GetExtent and IViewObject2::GetExtent. in
			**    this way if the object is transfered to another container
			**    (via clipboard or drag/drop), then the object will
			**    remain the scaled size.
			*/
			sizelOleObject.cx = lpLine->m_nWidthInHimetric;
			sizelOleObject.cy = lpLine->m_nHeightInHimetric;

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

			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  // OLE_CNTR
}


#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->hGlobal = NULL;

	if(lpformatetc->cfFormat == lpOutlineApp->m_cfOutline) {
		// Verify caller asked for correct medium
		if (!(lpformatetc->tymed & TYMED_HGLOBAL)) {
			sc = DV_E_FORMATETC;
			goto error;
		}
		lpMedium->hGlobal = OutlineDoc_GetOutlineData (lpOutlineDoc,NULL);
		if (! lpMedium->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) ) {
		// Verify caller asked for correct medium
		if (!(lpformatetc->tymed & TYMED_MFPICT)) {
			sc = DV_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal = ServerDoc_GetMetafilePictData(lpServerDoc,NULL);
		if (! lpMedium->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 = DV_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 = DV_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal=GetIconOfClass(g_lpApp->m_hInst,(REFCLSID)&clsid, NULL, FALSE);
		if (! lpMedium->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 = DV_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal = OutlineDoc_GetTextData (
				(LPOUTLINEDOC)lpServerDoc,
				NULL
		);
		if (! lpMedium->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 = DV_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 = DV_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal = OleDoc_GetObjectDescriptorData (
				(LPOLEDOC)lpServerDoc,
				NULL
		);
		if (! lpMedium->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 = DV_E_FORMATETC;
			goto error;
		}

	} else {
		sc = DV_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: lpMedium is an IN parameter. we should NOT set
	//           lpMedium->pUnkForRelease to 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 = DV_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 = DV_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 = DV_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_ICON)) ) {
		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(DV_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(DV_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;
	RECT            rectWBounds;
	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);
			if (! lpLine)
				continue;

			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);
		rectWBounds = rect;

		// 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);
			if (! lpLine)
				continue;

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

			/* Draw the line */
			Line_Draw(lpLine, hDC, &rect, &rectWBounds, FALSE /*fHighlight*/);
		}

		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;
	SIZEL           sizelInSrc;
	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);
		sizelInSrc = lpOD->sizel;   // size of object/picture in source (opt.)
		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,
			(LPSIZEL)&sizelInSrc
	);

	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,
		LPSIZEL                 lpSizelInSrc
)
{
	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,
				lpSizelInSrc
			);
		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.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,
					lpSizelInSrc
			);
			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,
					lpSizelInSrc
			);
			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,
		LPSIZEL                 lpSizelInSrc
)
{
	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);

	/* OLE2NOTE: if the source of the OLE object just pasted, passed a
	**    non-zero sizel in the ObjectDescriptor, then we will try to
	**    keep the object the same size as it is in the source. this
	**    may be a scaled size if the object had been resized in the
	**    source container. if the source did not give a valid sizel,
	**    then we will retrieve the size of the object by calling
	**    IViewObject2::GetExtent.
	*/
	if (lpSizelInSrc && (lpSizelInSrc->cx != 0 || lpSizelInSrc->cy != 0)) {
		ContainerLine_UpdateExtent(lpContainerLine, lpSizelInSrc);
	} else
		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->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 = DV_E_FORMATETC;
		goto error;

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

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

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

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

		lpMedium->hGlobal = OutlineDoc_GetTextData (
				(LPOUTLINEDOC)lpContainerDoc,
				NULL
		);
		if (! lpMedium->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 = DV_E_FORMATETC;
			goto error;
		}

		lpMedium->hGlobal = OleDoc_GetObjectDescriptorData (
				(LPOLEDOC)lpContainerDoc,
				NULL
		);
		if (! lpMedium->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(DV_E_FORMATETC);

			/* render CF_EMBEDDEDOBJECT by asking the object to save
			**    into a temporary, DELETEONRELEASE pStg allocated by us.
			*/

			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.
			*/
			LPCONTAINERLINE lpContainerLine;
			LPOLEOBJECT lpOleObj;
			SIZEL sizelOleObject;

			// Verify caller asked for correct medium
			if (!(lpformatetc->tymed & TYMED_MFPICT)) {
				sc = DV_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) {
				LPLINE lpLine = (LPLINE)lpContainerLine;

				/* render CF_METAFILEPICT by drawing the object into
				**    a metafile DC
				*/

				/* OLE2NOTE: Get size that object is being drawn. If the
				**    object has been scaled because the user resized the
				**    object, then we want to render a metafile with the
				**    scaled size.
				*/
				sizelOleObject.cx = lpLine->m_nWidthInHimetric;
				sizelOleObject.cy = lpLine->m_nHeightInHimetric;

				lpMedium->hGlobal = OleStdGetMetafilePictFromOleObject(
						lpOleObj,
						lpContainerLine->m_dwDrawAspect,
						(LPSIZEL)&sizelOleObject,
						lpformatetc->ptd
				);
				OleStdRelease((LPUNKNOWN)lpOleObj);
				if (! lpMedium->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(DV_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 = DV_E_FORMATETC;
					goto error;
				}
			} else {
				sc = DV_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 = DV_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 = DV_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: lpMedium is an IN parameter. we should NOT set
	//           lpMedium->pUnkForRelease to NULL

	// we only support  IStorage medium
	if (lpformatetc->cfFormat == lpContainerApp->m_cfCntrOutl) {
		if (!(lpformatetc->tymed & TYMED_ISTORAGE))
			return ResultFromScode(DV_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->pstg,&CLSID_APP)) != NOERROR)
				return hrErr;

			OutlineDoc_SaveSelToStg(
					(LPOUTLINEDOC)lpContainerDoc,
					NULL,   /* entire doc */
					lpContainerApp->m_cfCntrOutl,
					lpMedium->pstg,
					FALSE,  /* fSameAsLoad */
					FALSE   /* fRemember */
			);
			OleStdCommitStorage(lpMedium->pstg);

			OleDbgOut3("ContainerDoc_GetDataHere: rendered CF_CNTROUTL\r\n");
			return NOERROR;
		} else {
			// we only support IStorage medium
			return ResultFromScode(DV_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 object to save
			**    into the IStorage allocated by the caller.
			*/

			hrErr = OleStdGetOleObjectData(
					lpPersistStg,
					lpformatetc,
					lpMedium,
					FALSE   /* fUseMemory -- N/A */
			);
			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(DV_E_FORMATETC);
			}

		} else {
#if !defined( OPTIONAL_ADVANCED_DATA_TRANSFER )
			return ResultFromScode(DV_E_FORMATETC);
#endif
#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.
			*/
			/* 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(DV_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(DV_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) );

	// CF_TEXT and CF_OUTLINE are NOT supported when single object is copied
	} else if (!lpContainerDoc->m_fEmbeddedObjectAvail &&
			(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)) {

		/* OLE2NOTE: as a container which draws objects, when a single
		**    OLE object is copied, we can give the Metafile picture of
		**    the object.
		*/
		// 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 = DV_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
)
{
	/* in the container version of Outline, only DataTransferDoc's support
	**    IDataObject interface; the user documents do not support
	**    IDataObject. DataTransferDoc's do not accept SetData calls.
	*/
	return ResultFromScode(DV_E_FORMATETC);
}


/* 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_LINKSOURCE
**                  7. 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));

	OleDbgAssertSz(lparrDocGetFmts != NULL,"Error allocating arrDocGetFmts");
	if (lparrDocGetFmts == NULL)
			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