/*************************************************************************
**
**    OLE 2 Container Sample Code
**
**    cntrline.c
**
**    This file contains ContainerLine methods.
**
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/

#include "outline.h"

OLEDBGDATA



extern LPOUTLINEAPP         g_lpApp;
extern IUnknownVtbl         g_CntrLine_UnknownVtbl;
extern IOleClientSiteVtbl   g_CntrLine_OleClientSiteVtbl;
extern IAdviseSinkVtbl      g_CntrLine_AdviseSinkVtbl;

#if defined( INPLACE_CNTR )
extern IOleInPlaceSiteVtbl  g_CntrLine_OleInPlaceSiteVtbl;
extern BOOL g_fInsideOutContainer;
#endif  // INPLACE_CNTR

// REVIEW: should use string resource for messages
char ErrMsgDoVerb[] = "OLE object action failed!";


/* prototype for static functions */
static void InvertDiffRect(LPRECT lprcPix, LPRECT lprcObj, HDC hDC);


/*************************************************************************
** ContainerLine
**    This object represents the location within the container where
**    the embedded/linked object lives. It exposes interfaces to the
**    object that allow the object to get information about its
**    embedding site and to announce notifications of important events
**    (changed, closed, saved)
**
**    The ContainerLine exposes the following interfaces:
**      IUnknown
**      IOleClientSite
**      IAdviseSink
*************************************************************************/



/*************************************************************************
** ContainerLine::IUnknown interface implementation
*************************************************************************/


// IUnknown::QueryInterface
STDMETHODIMP CntrLine_Unk_QueryInterface(
		LPUNKNOWN           lpThis,
		REFIID              riid,
		LPVOID FAR*         lplpvObj
)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	return ContainerLine_QueryInterface(lpContainerLine, riid, lplpvObj);
}


// IUnknown::AddRef
STDMETHODIMP_(ULONG) CntrLine_Unk_AddRef(LPUNKNOWN lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	OleDbgAddRefMethod(lpThis, "IUnknown");

	return ContainerLine_AddRef(lpContainerLine);
}


// IUnknown::Release
STDMETHODIMP_(ULONG) CntrLine_Unk_Release(LPUNKNOWN lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	OleDbgReleaseMethod(lpThis, "IUnknown");

	return ContainerLine_Release(lpContainerLine);
}


/*************************************************************************
** ContainerLine::IOleClientSite interface implementation
*************************************************************************/

// IOleClientSite::QueryInterface
STDMETHODIMP CntrLine_CliSite_QueryInterface(
		LPOLECLIENTSITE     lpThis,
		REFIID              riid,
		LPVOID FAR*         lplpvObj
)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	return ContainerLine_QueryInterface(lpContainerLine, riid, lplpvObj);
}


// IOleClientSite::AddRef
STDMETHODIMP_(ULONG) CntrLine_CliSite_AddRef(LPOLECLIENTSITE lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	OleDbgAddRefMethod(lpThis, "IOleClientSite");

	return ContainerLine_AddRef(lpContainerLine);
}


// IOleClientSite::Release
STDMETHODIMP_(ULONG) CntrLine_CliSite_Release(LPOLECLIENTSITE lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	OleDbgReleaseMethod(lpThis, "IOleClientSite");

	return ContainerLine_Release(lpContainerLine);
}


// IOleClientSite::SaveObject
STDMETHODIMP CntrLine_CliSite_SaveObject(LPOLECLIENTSITE lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
	LPPERSISTSTORAGE lpPersistStg = lpContainerLine->m_lpPersistStg;
	SCODE sc = S_OK;
	HRESULT hrErr;

	OLEDBG_BEGIN2("CntrLine_CliSite_SaveObject\r\n")

	if (! lpPersistStg) {
		/* OLE2NOTE: The object is NOT loaded. a container must be
		**    prepared for the fact that an object that it thinks it
		**    has unloaded may still call IOleClientSite::SaveObject.
		**    in this case the container should fail the save call and
		**    NOT try to reload the object. this scenario arises if you
		**    have a in-process server (DLL object) which has a
		**    connected linking client. even after the embedding
		**    container unloads the DLL object, the link connection
		**    keeps the object alive. it may then as part of its
		**    shutdown try to call IOCS::SaveObject, but then it is too
		**    late.
		*/
		OLEDBG_END2
		return ResultFromScode(E_FAIL);
	}

	// mark ContainerDoc as now dirty
	OutlineDoc_SetModified(
			(LPOUTLINEDOC)lpContainerLine->m_lpDoc, TRUE, TRUE, FALSE);

	/* Tell OLE object to save itself
	** OLE2NOTE: it is NOT sufficient to ONLY call
	**    IPersistStorage::Save method. it is also necessary to call
	**    WriteClassStg. the helper API OleSave does this automatically.
	*/
	OLEDBG_BEGIN2("OleSave called\r\n")
	hrErr=OleSave(lpPersistStg,lpContainerLine->m_lpStg, TRUE/*fSameAsLoad*/);
	OLEDBG_END2

	if (hrErr != NOERROR) {
		OleDbgOutHResult("WARNING: OleSave returned", hrErr);
		sc = GetScode(hrErr);
	}

	// OLE2NOTE: even if OleSave fails, SaveCompleted must be called.
	OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n")
	hrErr = lpPersistStg->lpVtbl->SaveCompleted(lpPersistStg, NULL);
	OLEDBG_END2

	if (hrErr != NOERROR) {
		OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr);
		if (sc == S_OK)
			sc = GetScode(hrErr);
	}

	OLEDBG_END2
	return ResultFromScode(sc);
}


// IOleClientSite::GetMoniker
STDMETHODIMP CntrLine_CliSite_GetMoniker(
		LPOLECLIENTSITE     lpThis,
		DWORD               dwAssign,
		DWORD               dwWhichMoniker,
		LPMONIKER FAR*      lplpmk
)
{
	LPCONTAINERLINE lpContainerLine;

	lpContainerLine=((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	OLEDBG_BEGIN2("CntrLine_CliSite_GetMoniker\r\n")

	// OLE2NOTE: we must make sure to set output pointer parameters to NULL
	*lplpmk = NULL;

	switch (dwWhichMoniker) {

		case OLEWHICHMK_CONTAINER:
			/* OLE2NOTE: create a FileMoniker which identifies the
			**    entire container document.
			*/
			*lplpmk = OleDoc_GetFullMoniker(
					(LPOLEDOC)lpContainerLine->m_lpDoc,
					dwAssign
			);
			break;

		case OLEWHICHMK_OBJREL:

			/* OLE2NOTE: create an ItemMoniker which identifies the
			**    OLE object relative to the container document.
			*/
			*lplpmk = ContainerLine_GetRelMoniker(lpContainerLine, dwAssign);
			break;

		case OLEWHICHMK_OBJFULL:
		{
			/* OLE2NOTE: create an absolute moniker which identifies the
			**    OLE object in the container document. this moniker is
			**    created as a composite of the absolute moniker for the
			**    entire document appended with an item moniker which
			**    identifies the OLE object relative to the document.
			*/

			*lplpmk = ContainerLine_GetFullMoniker(lpContainerLine, dwAssign);
			break;
		}
	}

	OLEDBG_END2

	if (*lplpmk != NULL)
		return NOERROR;
	else
		return ResultFromScode(E_FAIL);
}


// IOleClientSite::GetContainer
STDMETHODIMP CntrLine_CliSite_GetContainer(
		LPOLECLIENTSITE     lpThis,
		LPOLECONTAINER FAR* lplpContainer
)
{
	LPCONTAINERLINE lpContainerLine;
	HRESULT hrErr;

	OLEDBG_BEGIN2("CntrLine_CliSite_GetContainer\r\n")

	lpContainerLine=((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	hrErr = OleDoc_QueryInterface(
			(LPOLEDOC)lpContainerLine->m_lpDoc,
			&IID_IOleContainer,
			(LPVOID FAR*)lplpContainer
	);

	OLEDBG_END2
	return hrErr;
}


// IOleClientSite::ShowObject
STDMETHODIMP CntrLine_CliSite_ShowObject(LPOLECLIENTSITE lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
	LPLINELIST lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
	int nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);
	HWND hWndFrame = OutlineApp_GetFrameWindow(g_lpApp);

	OLEDBG_BEGIN2("CntrLine_CliSite_ShowObject\r\n")

	/* make sure our doc window is visible and not minimized.
	**    the OutlineDoc_ShowWindow function will cause the app window
	**    to show itself SW_SHOWNORMAL.
	*/
	if (! IsWindowVisible(hWndFrame) || IsIconic(hWndFrame))
		OutlineDoc_ShowWindow(lpOutlineDoc);

	BringWindowToTop(hWndFrame);

	/* make sure that the OLE object is currently in view. if necessary
	**    scroll the document in order to bring it into view.
	*/
	LineList_ScrollLineIntoView(lpLL, nIndex);

#if defined( INPLACE_CNTR )
	/* after the in-place object is scrolled into view, we need to ask
	**    it to update its rect for the new clip rect coordinates
	*/
	ContainerDoc_UpdateInPlaceObjectRects((LPCONTAINERDOC)lpOutlineDoc, 0);
#endif

	OLEDBG_END2
	return NOERROR;
}


// IOleClientSite::OnShowWindow
STDMETHODIMP CntrLine_CliSite_OnShowWindow(LPOLECLIENTSITE lpThis, BOOL fShow)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
	LPLINELIST lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
	int nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);

	if (fShow) {
		OLEDBG_BEGIN2("CntrLine_CliSite_OnShowWindow(TRUE)\r\n")

		/* OLE2NOTE: we need to hatch out the OLE object now; it has
		**    just been opened in a window elsewhere (open editing as
		**    opposed to in-place activation).
		**    force the line to re-draw with the hatch.
		*/
		lpContainerLine->m_fObjWinOpen = TRUE;
		LineList_ForceLineRedraw(lpLL, nIndex, FALSE /*fErase*/);

	} else {
		OLEDBG_BEGIN2("CntrLine_CliSite_OnShowWindow(FALSE)\r\n")

		/* OLE2NOTE: the object associated with this container site has
		**    just closed its server window. we should now remove the
		**    hatching that indicates that the object is open
		**    elsewhere. also our window should now come to the top.
		**    force the line to re-draw without the hatch.
		*/
		lpContainerLine->m_fObjWinOpen = FALSE;
		LineList_ForceLineRedraw(lpLL, nIndex, TRUE /*fErase*/);

		BringWindowToTop(lpOutlineDoc->m_hWndDoc);
		SetFocus(lpOutlineDoc->m_hWndDoc);
	}

	OLEDBG_END2
	return NOERROR;
}


// IOleClientSite::RequestNewObjectLayout
STDMETHODIMP CntrLine_CliSite_RequestNewObjectLayout(LPOLECLIENTSITE lpThis)
{
	OleDbgOut2("CntrLine_CliSite_RequestNewObjectLayout\r\n");

	/* OLE2NOTE: this method is NOT yet used. it is for future layout
	**    negotiation support.
	*/
	return ResultFromScode(E_NOTIMPL);
}


/*************************************************************************
** ContainerLine::IAdviseSink interface implementation
*************************************************************************/

// IAdviseSink::QueryInterface
STDMETHODIMP CntrLine_AdvSink_QueryInterface(
		LPADVISESINK        lpThis,
		REFIID              riid,
		LPVOID FAR*         lplpvObj
)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	return ContainerLine_QueryInterface(lpContainerLine, riid, lplpvObj);
}


// IAdviseSink::AddRef
STDMETHODIMP_(ULONG) CntrLine_AdvSink_AddRef(LPADVISESINK lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	OleDbgAddRefMethod(lpThis, "IAdviseSink");

	return ContainerLine_AddRef(lpContainerLine);
}


// IAdviseSink::Release
STDMETHODIMP_(ULONG) CntrLine_AdvSink_Release (LPADVISESINK lpThis)
{
	LPCONTAINERLINE lpContainerLine =
			((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;

	OleDbgReleaseMethod(lpThis, "IAdviseSink");

	return ContainerLine_Release(lpContainerLine);
}


// IAdviseSink::OnDataChange
STDMETHODIMP_(void) CntrLine_AdvSink_OnDataChange(
		LPADVISESINK        lpThis,
		FORMATETC FAR*      lpFormatetc,
		STGMEDIUM FAR*      lpStgmed
)
{
	OleDbgOut2("CntrLine_AdvSink_OnDataChange\r\n");
	// We are not interested in data changes (only view changes)
	//      (ie. nothing to do)
}


STDMETHODIMP_(void) CntrLine_AdvSink_OnViewChange(
		LPADVISESINK        lpThis,
		DWORD               aspects,
		LONG                lindex
)
{
	LPCONTAINERLINE lpContainerLine;
	LPOUTLINEDOC lpOutlineDoc;
	HWND hWndDoc;
	LPLINELIST lpLL;
	MSG msg;
	int nIndex;

	OLEDBG_BEGIN2("CntrLine_AdvSink_OnViewChange\r\n")

	lpContainerLine = ((struct CAdviseSinkImpl FAR*)lpThis)->lpContainerLine;
	lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;

	/* OLE2NOTE: at this point we simply invalidate the rectangle of
	**    the object to force a repaint in the future, we mark
	**    that the extents of the object may have changed
	**    (m_fDoGetExtent=TRUE), and we post a message
	**    (WM_U_UPDATEOBJECTEXTENT) to our document that one or more
	**    OLE objects may need to have their extents updated. later
	**    when this message is processed, the document loops through
	**    all lines to see if any are marked as needing an extent update.
	**    if infact the extents did change. this can be done by calling
	**    IViewObject2::GetExtent to retreive the object's current
	**    extents and comparing with the last known extents for the
	**    object. if the extents changed, then relayout space for the
	**    object before drawing. we postpone the check to get
	**    the extents now because OnViewChange is an asyncronis method,
	**    and we have to careful not to call any syncronis methods back
	**    to the object. while it WOULD be OK to call the
	**    IViewObject2::GetExtent method within the asyncronis
	**    OnViewChange method (because this method is handled by the
	**    object handler and never remoted), it is good practise to not
	**    call any object methods from within an asyncronis
	**    notification method.
	**    if there is already WM_U_UPDATEOBJECTEXTENT message waiting
	**    in our message queue, there is no need to post another one.
	**    in this way, if the server is updating quicker than we can
	**    keep up, we do not make unneccsary GetExtent calls. also if
	**    drawing is disabled, we postpone updating the extents of any
	**    objects until drawing is re-enabled.
	*/
	lpContainerLine->m_fDoGetExtent = TRUE;
	hWndDoc = OutlineDoc_GetWindow((LPOUTLINEDOC)lpContainerLine->m_lpDoc);

	if (lpOutlineDoc->m_nDisableDraw == 0 &&
		! PeekMessage(&msg, hWndDoc,
			WM_U_UPDATEOBJECTEXTENT, WM_U_UPDATEOBJECTEXTENT,
			PM_NOREMOVE | PM_NOYIELD)) {
		PostMessage(hWndDoc, WM_U_UPDATEOBJECTEXTENT, 0, 0L);
	}

	// force the modified line to redraw.
	lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
	nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);
	LineList_ForceLineRedraw(lpLL, nIndex, TRUE /*fErase*/);

	OLEDBG_END2
}


// IAdviseSink::OnRename
STDMETHODIMP_(void) CntrLine_AdvSink_OnRename(
		LPADVISESINK        lpThis,
		LPMONIKER           lpmk
)
{
	OleDbgOut2("CntrLine_AdvSink_OnRename\r\n");
	/* OLE2NOTE: the Embedding Container has nothing to do here. this
	**    notification is important for linking situations. it tells
	**    the OleLink objects to update their moniker because the
	**    source object has been renamed (track the link source).
	*/
}


// IAdviseSink::OnSave
STDMETHODIMP_(void) CntrLine_AdvSink_OnSave(LPADVISESINK lpThis)
{
	OleDbgOut2("CntrLine_AdvSink_OnSave\r\n");
	/* OLE2NOTE: the Embedding Container has nothing to do here. this
	**    notification is only useful to clients which have set up a
	**    data cache with the ADVFCACHE_ONSAVE flag.
	*/
}


// IAdviseSink::OnClose
STDMETHODIMP_(void) CntrLine_AdvSink_OnClose(LPADVISESINK lpThis)
{
	OleDbgOut2("CntrLine_AdvSink_OnClose\r\n");
	/* OLE2NOTE: the Embedding Container has nothing to do here. this
	**    notification is important for the OLE's default object handler
	**    and the OleLink object. it tells them the remote object is
	**    shutting down.
	*/
}


/*************************************************************************
** ContainerLine Support Functions
*************************************************************************/


/* ContainerLine_Init
** ------------------
**  Initialize fields in a newly constructed ContainerLine line object.
**  NOTE: ref cnt of ContainerLine initialized to 0
*/
void ContainerLine_Init(LPCONTAINERLINE lpContainerLine, int nTab, HDC hDC)
{
	Line_Init((LPLINE)lpContainerLine, nTab, hDC);  // init base class fields

	((LPLINE)lpContainerLine)->m_lineType           = CONTAINERLINETYPE;
	((LPLINE)lpContainerLine)->m_nWidthInHimetric   = DEFOBJWIDTH;
	((LPLINE)lpContainerLine)->m_nHeightInHimetric  = DEFOBJHEIGHT;
	lpContainerLine->m_cRef                         = 0;
	lpContainerLine->m_szStgName[0]                 = '\0';
	lpContainerLine->m_fObjWinOpen                  = FALSE;
	lpContainerLine->m_fMonikerAssigned             = FALSE;
	lpContainerLine->m_dwDrawAspect                 = DVASPECT_CONTENT;

	lpContainerLine->m_fGuardObj                    = FALSE;
	lpContainerLine->m_fDoGetExtent                 = FALSE;
	lpContainerLine->m_fDoSetExtent                 = FALSE;
	lpContainerLine->m_sizeInHimetric.cx            = -1;
	lpContainerLine->m_sizeInHimetric.cy            = -1;

	lpContainerLine->m_lpStg                        = NULL;
	lpContainerLine->m_lpDoc                        = NULL;
	lpContainerLine->m_lpOleObj                     = NULL;
	lpContainerLine->m_lpViewObj2                   = NULL;
	lpContainerLine->m_lpPersistStg                 = NULL;
	lpContainerLine->m_lpOleLink                    = NULL;
	lpContainerLine->m_dwLinkType                   = 0;
	lpContainerLine->m_fLinkUnavailable             = FALSE;
	lpContainerLine->m_lpszShortType                = NULL;

#if defined( INPLACE_CNTR )
	lpContainerLine->m_fIpActive                    = FALSE;
	lpContainerLine->m_fUIActive                    = FALSE;
	lpContainerLine->m_fIpVisible                   = FALSE;
	lpContainerLine->m_lpOleIPObj                   = NULL;
	lpContainerLine->m_fInsideOutObj                = FALSE;
	lpContainerLine->m_fIpChangesUndoable           = FALSE;
	lpContainerLine->m_fIpServerRunning             = FALSE;
	lpContainerLine->m_hWndIpObject                 = NULL;
	lpContainerLine->m_nHorizScrollShift            = 0;
#endif  // INPLACE_CNTR

	INIT_INTERFACEIMPL(
			&lpContainerLine->m_Unknown,
			&g_CntrLine_UnknownVtbl,
			lpContainerLine
	);

	INIT_INTERFACEIMPL(
			&lpContainerLine->m_OleClientSite,
			&g_CntrLine_OleClientSiteVtbl,
			lpContainerLine
	);

	INIT_INTERFACEIMPL(
			&lpContainerLine->m_AdviseSink,
			&g_CntrLine_AdviseSinkVtbl,
			lpContainerLine
	);

#if defined( INPLACE_CNTR )
	INIT_INTERFACEIMPL(
			&lpContainerLine->m_OleInPlaceSite,
			&g_CntrLine_OleInPlaceSiteVtbl,
			lpContainerLine
	);
#endif  // INPLACE_CNTR
}


/* Setup the OLE object associated with the ContainerLine */
BOOL ContainerLine_SetupOleObject(
		LPCONTAINERLINE         lpContainerLine,
		BOOL                    fDisplayAsIcon,
		HGLOBAL                 hMetaPict
)
{
	DWORD dwDrawAspect = (fDisplayAsIcon ? DVASPECT_ICON : DVASPECT_CONTENT);
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;

	/* Cache a pointer to the IViewObject2* interface. *Required*
	**      we need this everytime we draw the object.
	**
	** OLE2NOTE: We require the object to support IViewObject2
	**    interface. this is an extension to the IViewObject interface
	**    that was added with the OLE 2.01 release. This interface must
	**    be supported by all object handlers and DLL-based objects.
	*/
	lpContainerLine->m_lpViewObj2 = (LPVIEWOBJECT2)OleStdQueryInterface(
			(LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IViewObject2);
	if (! lpContainerLine->m_lpViewObj2) {
#if defined( _DEBUG )
		OleDbgAssertSz(
			lpContainerLine->m_lpViewObj2,"IViewObject2 NOT supported\r\n");
#endif
		return FALSE;
	}

	// Cache a pointer to the IPersistStorage* interface. *Required*
	//      we need this everytime we save the object.
	lpContainerLine->m_lpPersistStg = (LPPERSISTSTORAGE)OleStdQueryInterface(
			(LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IPersistStorage);
	if (! lpContainerLine->m_lpPersistStg) {
		OleDbgAssert(lpContainerLine->m_lpPersistStg);
		return FALSE;
	}

	// Cache a pointer to the IOleLink* interface if supported. *Optional*
	//      if supported the object is a link. we need this to manage the link
	lpContainerLine->m_lpOleLink = (LPOLELINK)OleStdQueryInterface(
			(LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IOleLink);
	if (lpContainerLine->m_lpOleLink) {
		OLEDBG_BEGIN2("IOleLink::GetUpdateOptions called\r\n")
		lpContainerLine->m_lpOleLink->lpVtbl->GetUpdateOptions(
				lpContainerLine->m_lpOleLink, &lpContainerLine->m_dwLinkType);
		OLEDBG_END2
	} else
		lpContainerLine->m_dwLinkType = 0;  // NOT a link

	/* get the short user type name of the object. this
	**    is used all the time when we have to build the object
	**    verb menu. we will cache this information to make it
	**    quicker to build the verb menu.
	*/
	OleDbgAssert(lpContainerLine->m_lpszShortType == NULL);
	OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")
	{
	LPOLESTR polestr;

	lpContainerLine->m_lpOleObj->lpVtbl->GetUserType(
			lpContainerLine->m_lpOleObj,
			USERCLASSTYPE_SHORT,
			&polestr
	);
	CopyAndFreeOLESTR(polestr, &lpContainerLine->m_lpszShortType);
	}
	OLEDBG_END2

	/* Perform that standard setup for the OLE object. this includes:
	**      setup View advise
	**      Call IOleObject::SetHostNames
	**      Call OleSetContainedObject
	*/
	OleStdSetupAdvises(
			lpContainerLine->m_lpOleObj,
			dwDrawAspect,
			(LPSTR)APPNAME,
			lpOutlineDoc->m_lpszDocTitle,
			(LPADVISESINK)&lpContainerLine->m_AdviseSink,
			TRUE    /*fCreate*/
	);

#if defined( INPLACE_CNTR )
	/* OLE2NOTE: (INSIDE-OUT CONTAINER) An inside-out container should
	**    check if the object is an inside-out and prefers to be
	**    activated when visible type of object. if not the object
	**    should not be allowed to keep its window up after it gets
	**    UIDeactivated.
	*/
	if (g_fInsideOutContainer) {
		DWORD mstat;
		OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n")
		lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus(
				lpContainerLine->m_lpOleObj,
				DVASPECT_CONTENT,
				(DWORD FAR*)&mstat
		);
		OLEDBG_END2

		lpContainerLine->m_fInsideOutObj = (BOOL)
				(mstat & (OLEMISC_INSIDEOUT|OLEMISC_ACTIVATEWHENVISIBLE));
	}
#endif  // INPLACE_CNTR

	if (fDisplayAsIcon) {
		/* user has requested to display icon aspect instead of content
		**    aspect.
		**    NOTE: we do not have to delete the previous aspect cache
		**    because one did not get set up.
		*/
		OleStdSwitchDisplayAspect(
				lpContainerLine->m_lpOleObj,
				&lpContainerLine->m_dwDrawAspect,
				dwDrawAspect,
				hMetaPict,
				FALSE,  /* fDeleteOldAspect */
				TRUE,   /* fSetupViewAdvise */
				(LPADVISESINK)&lpContainerLine->m_AdviseSink,
				NULL /*fMustUpdate*/        // this can be ignored; update
											// for switch to icon not req'd
		);
	}
	return TRUE;
}


/* Create an ContainerLine object and return the pointer */
LPCONTAINERLINE ContainerLine_Create(
		DWORD                   dwOleCreateType,
		HDC                     hDC,
		UINT                    nTab,
		LPCONTAINERDOC          lpContainerDoc,
		LPCLSID                 lpclsid,
		LPSTR                   lpszFileName,
		BOOL                    fDisplayAsIcon,
		HGLOBAL                 hMetaPict,
		LPSTR                   lpszStgName
)
{
	LPCONTAINERLINE lpContainerLine = NULL;
	LPOLEOBJECT     lpObj = NULL;
	LPSTORAGE       lpDocStg = ContainerDoc_GetStg(lpContainerDoc);
	DWORD           dwDrawAspect =
						(fDisplayAsIcon ? DVASPECT_ICON : DVASPECT_CONTENT);
	DWORD           dwOleRenderOpt =
						(fDisplayAsIcon ? OLERENDER_NONE : OLERENDER_DRAW);
	HRESULT         hrErr;

	OLEDBG_BEGIN3("ContainerLine_Create\r\n")

	if (lpDocStg == NULL) {
		OleDbgAssertSz(lpDocStg != NULL, "Doc storage is NULL");
		goto error;
	}

	lpContainerLine=(LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE));
	if (lpContainerLine == NULL) {
		OleDbgAssertSz(lpContainerLine!=NULL,"Error allocating ContainerLine");
		goto error;
	}

	ContainerLine_Init(lpContainerLine, nTab, hDC);

	/* OLE2NOTE: in order to avoid re-entrancy we will set a flag to
	**    guard our object. if this guard is set, then the object is
	**    not ready to have any OLE interface methods called. it is
	**    necessary to guard the object this way while it is being
	**    created or loaded.
	*/
	lpContainerLine->m_fGuardObj = TRUE;

	/* OLE2NOTE: In order to have a stable ContainerLine object we must
	**    AddRef the object's refcnt. this will be later released when
	**    the ContainerLine is deleted.
	*/
	ContainerLine_AddRef(lpContainerLine);

	lstrcpy(lpContainerLine->m_szStgName, lpszStgName);
	lpContainerLine->m_lpDoc = lpContainerDoc;

	/* Create a new storage for the object inside the doc's storage */
	lpContainerLine->m_lpStg = OleStdCreateChildStorage(lpDocStg,lpszStgName);
	if (lpContainerLine->m_lpStg == NULL) {
		OleDbgAssert(lpContainerLine->m_lpStg != NULL);
		goto error;
	}

	lpContainerLine->m_dwLinkType = 0;

	switch (dwOleCreateType) {

		case IOF_SELECTCREATENEW:

			OLEDBG_BEGIN2("OleCreate called\r\n")
			hrErr = OleCreate (
					lpclsid,
					&IID_IOleObject,
					dwOleRenderOpt,
					NULL,
					(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
					lpContainerLine->m_lpStg,
					(LPVOID FAR*)&lpContainerLine->m_lpOleObj
			);
			OLEDBG_END2

#if defined( _DEBUG )
			if (hrErr != NOERROR)
				OleDbgOutHResult("OleCreate returned", hrErr);
#endif

			break;

		case IOF_SELECTCREATEFROMFILE:

			OLEDBG_BEGIN2("OleCreateFromFile called\r\n")
			{
			CREATEOLESTR(polestr, lpszFileName)

			hrErr = OleCreateFromFile (
					&CLSID_NULL,
					polestr,
					&IID_IOleObject,
					dwOleRenderOpt,
					NULL,
					(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
					lpContainerLine->m_lpStg,
					(LPVOID FAR*)&lpContainerLine->m_lpOleObj
			);

			FREEOLESTR(polestr)
			}
			OLEDBG_END2

#if defined( _DEBUG )
			if (hrErr != NOERROR)
				OleDbgOutHResult("OleCreateFromFile returned", hrErr);
#endif
			break;

		case IOF_CHECKLINK:

			OLEDBG_BEGIN2("OleCreateLinkToFile called\r\n")
			{
			CREATEOLESTR(polestr, lpszFileName)

			hrErr = OleCreateLinkToFile (
					polestr,
					&IID_IOleObject,
					dwOleRenderOpt,
					NULL,
					(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
					lpContainerLine->m_lpStg,
					(LPVOID FAR*)&lpContainerLine->m_lpOleObj
			);

			FREEOLESTR(polestr)
			}
			OLEDBG_END2

#if defined( _DEBUG )
			if (hrErr != NOERROR)
				OleDbgOutHResult("OleCreateLinkToFile returned", hrErr);
#endif
			break;
	}
	if (hrErr != NOERROR)
		goto error;

	if (! ContainerLine_SetupOleObject(
								lpContainerLine, fDisplayAsIcon, hMetaPict)) {
		goto error;
	}

	/* OLE2NOTE: clear our re-entrancy guard. the object is now ready
	**    to have interface methods called.
	*/
	lpContainerLine->m_fGuardObj = FALSE;

	OLEDBG_END3
	return lpContainerLine;

error:
	OutlineApp_ErrorMessage(g_lpApp, "Could not create object!");

	// Destroy partially created OLE object
	if (lpContainerLine)
		ContainerLine_Delete(lpContainerLine);
	OLEDBG_END3
	return NULL;
}


LPCONTAINERLINE ContainerLine_CreateFromData(
		HDC                     hDC,
		UINT                    nTab,
		LPCONTAINERDOC          lpContainerDoc,
		LPDATAOBJECT            lpSrcDataObj,
		DWORD                   dwCreateType,
		CLIPFORMAT              cfFormat,
		BOOL                    fDisplayAsIcon,
		HGLOBAL                 hMetaPict,
		LPSTR                   lpszStgName
)
{
	HGLOBAL         hData = NULL;
	LPCONTAINERLINE lpContainerLine = NULL;
	LPOLEOBJECT     lpObj = NULL;
	LPSTORAGE       lpDocStg = ContainerDoc_GetStg(lpContainerDoc);
	DWORD           dwDrawAspect =
						(fDisplayAsIcon ? DVASPECT_ICON : DVASPECT_CONTENT);
	DWORD           dwOleRenderOpt;
	FORMATETC       renderFmtEtc;
	LPFORMATETC     lpRenderFmtEtc = NULL;
	HRESULT         hrErr;
	LPUNKNOWN       lpUnk = NULL;

	OLEDBG_BEGIN3("ContainerLine_CreateFromData\r\n")

	if (dwCreateType == OLECREATEFROMDATA_STATIC && cfFormat != 0) {
		// a particular type of static object should be created

		dwOleRenderOpt = OLERENDER_FORMAT;
		lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc;

		if (cfFormat == CF_METAFILEPICT)
			SETDEFAULTFORMATETC(renderFmtEtc, cfFormat, TYMED_MFPICT);
		else if (cfFormat == CF_BITMAP)
			SETDEFAULTFORMATETC(renderFmtEtc, cfFormat, TYMED_GDI);
		else
			SETDEFAULTFORMATETC(renderFmtEtc, cfFormat, TYMED_HGLOBAL);

	} else if (dwCreateType == OLECREATEFROMDATA_STATIC && fDisplayAsIcon) {
		// a link that currently displayed as an icon needs to be
		// converted to a STATIC picture object. this case is driven
		// from "BreakLink" in the "Links" dialog. because the current
		// data in the source object's cache is DVASPECT_ICON we need
		// to tell the OleCreateStaticFromData API to look for
		// DVASPECT_ICON data. the static object that results however,
		// is considered to be displayed in the DVASPECT_CONTENT view.

		dwOleRenderOpt = OLERENDER_DRAW;
		lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc;
		SETFORMATETC(renderFmtEtc,0,DVASPECT_ICON,NULL,TYMED_NULL,-1);
		dwDrawAspect = DVASPECT_CONTENT;   // static obj displays only CONTENT

	} else if (fDisplayAsIcon && hMetaPict) {
		// a special icon should be used. first we create the object
		// OLERENDER_NONE and then we stuff the special icon into the cache.

		dwOleRenderOpt = OLERENDER_NONE;

	} else if (fDisplayAsIcon && hMetaPict == NULL) {
		// the object's default icon should be used

		dwOleRenderOpt = OLERENDER_DRAW;
		lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc;
		SETFORMATETC(renderFmtEtc,0,DVASPECT_ICON,NULL,TYMED_NULL,-1);

	} else {
		// create standard DVASPECT_CONTENT/OLERENDER_DRAW object
		dwOleRenderOpt = OLERENDER_DRAW;
	}

	if (lpDocStg == NULL) {
		OleDbgAssertSz(lpDocStg != NULL, "Doc storage is NULL");
		goto error;
	}

	lpContainerLine=(LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE));
	if (lpContainerLine == NULL) {
		OleDbgAssertSz(lpContainerLine!=NULL,"Error allocating ContainerLine");
		goto error;
	}

	ContainerLine_Init(lpContainerLine, nTab, hDC);

	/* OLE2NOTE: in order to avoid re-entrancy we will set a flag to
	**    guard our object. if this guard is set, then the object is
	**    not ready to have any OLE interface methods called. it is
	**    necessary to guard the object this way while it is being
	**    created or loaded.
	*/
	lpContainerLine->m_fGuardObj = TRUE;

	/* OLE2NOTE: In order to have a stable ContainerLine object we must
	**    AddRef the object's refcnt. this will be later released when
	**    the ContainerLine is deleted.
	*/
	ContainerLine_AddRef(lpContainerLine);

	lstrcpy(lpContainerLine->m_szStgName, lpszStgName);
	lpContainerLine->m_lpDoc = lpContainerDoc;

	/* Create a new storage for the object inside the doc's storage */
	lpContainerLine->m_lpStg = OleStdCreateChildStorage(lpDocStg,lpszStgName);
	if (lpContainerLine->m_lpStg == NULL) {
		OleDbgAssert(lpContainerLine->m_lpStg != NULL);
		goto error;
	}

	switch (dwCreateType) {

		case OLECREATEFROMDATA_LINK:

			OLEDBG_BEGIN2("OleCreateLinkFromData called\r\n")
			hrErr = OleCreateLinkFromData (
					lpSrcDataObj,
					&IID_IOleObject,
					dwOleRenderOpt,
					lpRenderFmtEtc,
					(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
					lpContainerLine->m_lpStg,
					(LPVOID FAR*)&lpContainerLine->m_lpOleObj
			);
			OLEDBG_END2

#if defined( _DEBUG )
			if (hrErr != NOERROR)
				OleDbgOutHResult("OleCreateLinkFromData returned", hrErr);
#endif
			break;

		case OLECREATEFROMDATA_OBJECT:

			OLEDBG_BEGIN2("OleCreateFromData called\r\n")
			hrErr = OleCreateFromData (
					lpSrcDataObj,
					&IID_IOleObject,
					dwOleRenderOpt,
					lpRenderFmtEtc,
					(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
					lpContainerLine->m_lpStg,
					(LPVOID FAR*)&lpContainerLine->m_lpOleObj
			);
			OLEDBG_END2

#if defined( _DEBUG )
			if (hrErr != NOERROR)
				OleDbgOutHResult("OleCreateFromData returned", hrErr);
#endif
			break;

		case OLECREATEFROMDATA_STATIC:

			OLEDBG_BEGIN2("OleCreateStaticFromData called\r\n")
			hrErr = OleCreateStaticFromData (
					lpSrcDataObj,
					&IID_IOleObject,
					dwOleRenderOpt,
					lpRenderFmtEtc,
					(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
					lpContainerLine->m_lpStg,
					(LPVOID FAR*)&lpContainerLine->m_lpOleObj
			);
			OLEDBG_END2

#if defined( _DEBUG )
			if (hrErr != NOERROR)
				OleDbgOutHResult("OleCreateStaticFromData returned", hrErr);
#endif
			break;
	}

	if (hrErr != NOERROR)
		goto error;

	if (! ContainerLine_SetupOleObject(
								lpContainerLine, fDisplayAsIcon, hMetaPict)) {
		goto error;
	}

	/* OLE2NOTE: clear our re-entrancy guard. the object is now ready
	**    to have interface methods called.
	*/
	lpContainerLine->m_fGuardObj = FALSE;

	OLEDBG_END3
	return lpContainerLine;

error:
	OutlineApp_ErrorMessage(g_lpApp, "Could not create object!");
	// Destroy partially created OLE object
	if (lpContainerLine)
		ContainerLine_Delete(lpContainerLine);
	OLEDBG_END3
	return NULL;
}


/* ContainerLine_AddRef
** --------------------
**
**  increment the ref count of the line object.
**
**    Returns the new ref count on the object
*/
ULONG ContainerLine_AddRef(LPCONTAINERLINE lpContainerLine)
{
	++lpContainerLine->m_cRef;

#if defined( _DEBUG )
	OleDbgOutRefCnt4(
			"ContainerLine_AddRef: cRef++\r\n",
			lpContainerLine,
			lpContainerLine->m_cRef
	);
#endif
	return lpContainerLine->m_cRef;
}


/* ContainerLine_Release
** ---------------------
**
**  decrement the ref count of the line object.
**    if the ref count goes to 0, then the line is destroyed.
**
**    Returns the remaining ref count on the object
*/
ULONG ContainerLine_Release(LPCONTAINERLINE lpContainerLine)
{
	ULONG cRef;

	/*********************************************************************
	** OLE2NOTE: when the obj refcnt == 0, then destroy the object.     **
	**     otherwise the object is still in use.                        **
	*********************************************************************/

	cRef = --lpContainerLine->m_cRef;

#if defined( _DEBUG )
	OleDbgAssertSz(
			lpContainerLine->m_cRef >= 0,"Release called with cRef == 0");

	OleDbgOutRefCnt4(
			"ContainerLine_Release: cRef--\r\n",
			lpContainerLine,
			cRef
	);
#endif
	if (cRef == 0)
		ContainerLine_Destroy(lpContainerLine);

	return cRef;
}


/* ContainerLine_QueryInterface
** ----------------------------
**
** Retrieve a pointer to an interface on the ContainerLine object.
**
**    Returns NOERROR if interface is successfully retrieved.
**            E_NOINTERFACE if the interface is not supported
*/
HRESULT ContainerLine_QueryInterface(
		LPCONTAINERLINE         lpContainerLine,
		REFIID                  riid,
		LPVOID FAR*             lplpvObj
)
{
	SCODE sc = E_NOINTERFACE;

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

	if (IsEqualIID(riid, &IID_IUnknown)) {
		OleDbgOut4("ContainerLine_QueryInterface: IUnknown* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpContainerLine->m_Unknown;
		ContainerLine_AddRef(lpContainerLine);
		sc = S_OK;
	}
	else if (IsEqualIID(riid, &IID_IOleClientSite)) {
		OleDbgOut4("ContainerLine_QueryInterface: IOleClientSite* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpContainerLine->m_OleClientSite;
		ContainerLine_AddRef(lpContainerLine);
		sc = S_OK;
	}
	else if (IsEqualIID(riid, &IID_IAdviseSink)) {
		OleDbgOut4("ContainerLine_QueryInterface: IAdviseSink* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpContainerLine->m_AdviseSink;
		ContainerLine_AddRef(lpContainerLine);
		sc = S_OK;
	}
#if defined( INPLACE_CNTR )
	else if (IsEqualIID(riid, &IID_IOleWindow)
			 || IsEqualIID(riid, &IID_IOleInPlaceSite)) {
		OleDbgOut4("ContainerLine_QueryInterface: IOleInPlaceSite* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpContainerLine->m_OleInPlaceSite;
		ContainerLine_AddRef(lpContainerLine);
		sc = S_OK;
	}
#endif  // INPLACE_CNTR

	OleDbgQueryInterfaceMethod(*lplpvObj);

	return ResultFromScode(sc);
}


BOOL ContainerLine_LoadOleObject(LPCONTAINERLINE lpContainerLine)
{
	LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
	LPSTORAGE       lpDocStg = ContainerDoc_GetStg(lpContainerLine->m_lpDoc);
	LPOLECLIENTSITE lpOleClientSite;
	LPMONIKER       lpmkObj;
	LPOLEAPP        lpOleApp = (LPOLEAPP)g_lpApp;
	BOOL            fPrevEnable1;
	BOOL            fPrevEnable2;
	HRESULT         hrErr;

	if (lpContainerLine->m_fGuardObj)
		return FALSE;                // object in process of creation

	if (lpContainerLine->m_lpOleObj)
		return TRUE;                // object already loaded

	OLEDBG_BEGIN3("ContainerLine_LoadOleObject\r\n")

	/* OLE2NOTE: in order to avoid re-entrancy we will set a flag to
	**    guard our object. if this guard is set, then the object is
	**    not ready to have any OLE interface methods called. it is
	**    necessary to guard the object this way while it is being
	**    created or loaded.
	*/
	lpContainerLine->m_fGuardObj = TRUE;

	/* if object storage is not already open, then open it */
	if (! lpContainerLine->m_lpStg) {
		lpContainerLine->m_lpStg = OleStdOpenChildStorage(
				lpDocStg,
				lpContainerLine->m_szStgName,
				STGM_READWRITE
		);
		if (lpContainerLine->m_lpStg == NULL) {
			OleDbgAssert(lpContainerLine->m_lpStg != NULL);
			goto error;
		}
	}

	/* OLE2NOTE: if the OLE object being loaded is in a data transfer
	**    document, then we should NOT pass a IOleClientSite* pointer
	**    to the OleLoad call. This particularly critical if the OLE
	**    object is an OleLink object. If a non-NULL client site is
	**    passed to the OleLoad function, then the link will bind to
	**    the source if its is running. in the situation that we are
	**    loading the object as part of a data transfer document we do
	**    not want this connection to be established. even worse, if
	**    the link source is currently blocked or busy, then this could
	**    hang the system. it is simplest to never pass a
	**    IOleClientSite* when loading an object in a data transfer
	**    document.
	*/
	lpOleClientSite = (lpOutlineDoc->m_fDataTransferDoc ?
			NULL : (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite);

	/* OLE2NOTE: we do not want to ever give the Busy/NotResponding
	**    dialogs when we are loading an object. if the object is a
	**    link, it will attempt to BindIfRunning to the link source. if
	**    the link source is currently busy, this could cause the Busy
	**    dialog to come up. even if the link source is busy,
	**    we do not want put up the busy dialog. thus we will disable
	**    the dialog and later re-enable them
	*/
	OleApp_DisableBusyDialogs(lpOleApp, &fPrevEnable1, &fPrevEnable2);

	OLEDBG_BEGIN2("OleLoad called\r\n")
	hrErr = OleLoad (
		   lpContainerLine->m_lpStg,
		   &IID_IOleObject,
		   lpOleClientSite,
		   (LPVOID FAR*)&lpContainerLine->m_lpOleObj
	);
	OLEDBG_END2

	// re-enable the Busy/NotResponding dialogs
	OleApp_EnableBusyDialogs(lpOleApp, fPrevEnable1, fPrevEnable2);

	if (hrErr != NOERROR) {
		OleDbgAssertSz(hrErr == NOERROR, "Could not load object!");
		OleDbgOutHResult("OleLoad returned", hrErr);
		goto error;
	}

	/* Cache a pointer to the IViewObject2* interface. *Required*
	**      we need this everytime we draw the object.
	**
	** OLE2NOTE: We require the object to support IViewObject2
	**    interface. this is an extension to the IViewObject interface
	**    that was added with the OLE 2.01 release. This interface must
	**    be supported by all object handlers and DLL-based objects.
	*/
	lpContainerLine->m_lpViewObj2 = (LPVIEWOBJECT2)OleStdQueryInterface(
			(LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IViewObject2);
	if (! lpContainerLine->m_lpViewObj2) {
#if defined( _DEBUG )
		OleDbgAssertSz(
			lpContainerLine->m_lpViewObj2,"IViewObject2 NOT supported\r\n");
#endif
		goto error;
	}

	// Cache a pointer to the IPersistStorage* interface. *Required*
	//      we need this everytime we save the object.
	lpContainerLine->m_lpPersistStg = (LPPERSISTSTORAGE)OleStdQueryInterface(
			(LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IPersistStorage);
	if (! lpContainerLine->m_lpPersistStg) {
		OleDbgAssert(lpContainerLine->m_lpPersistStg);
		goto error;
	}

	// Cache a pointer to the IOleLink* interface if supported. *Optional*
	//      if supported the object is a link. we need this to manage the link
	if (lpContainerLine->m_dwLinkType != 0) {
		lpContainerLine->m_lpOleLink = (LPOLELINK)OleStdQueryInterface(
				(LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IOleLink);
		if (! lpContainerLine->m_lpOleLink) {
			OleDbgAssert(lpContainerLine->m_lpOleLink);
			goto error;
		}
	}

	/* OLE2NOTE: clear our re-entrancy guard. the object is now ready
	**    to have interface methods called.
	*/
	lpContainerLine->m_fGuardObj = FALSE;

	/* OLE2NOTE: similarly, if the OLE object being loaded is in a data
	**    transfer document, then we do NOT need to setup any advises,
	**    call SetHostNames, SetMoniker, etc.
	*/
	if (lpOleClientSite) {
		/* Setup the Advises (OLE notifications) that we are interested
		** in receiving.
		*/
		OleStdSetupAdvises(
				lpContainerLine->m_lpOleObj,
				lpContainerLine->m_dwDrawAspect,
				(LPSTR)APPNAME,
				lpOutlineDoc->m_lpszDocTitle,
				(LPADVISESINK)&lpContainerLine->m_AdviseSink,
				FALSE   /*fCreate*/
		);

		/* OLE2NOTE: if the OLE object has a moniker assigned, we need to
		**    inform the object by calling IOleObject::SetMoniker. this
		**    will force the OLE object to register in the
		**    RunningObjectTable when it enters the running state.
		*/
		if (lpContainerLine->m_fMonikerAssigned) {
			lpmkObj = ContainerLine_GetRelMoniker(
					lpContainerLine,
					GETMONIKER_ONLYIFTHERE
			);

			if (lpmkObj) {
				OLEDBG_BEGIN2("IOleObject::SetMoniker called\r\n")
				lpContainerLine->m_lpOleObj->lpVtbl->SetMoniker(
						lpContainerLine->m_lpOleObj,
						OLEWHICHMK_OBJREL,
						lpmkObj
				);
				OLEDBG_END2
				OleStdRelease((LPUNKNOWN)lpmkObj);
			}
		}

		/* get the Short form of the user type name of the object. this
		**    is used all the time when we have to build the object
		**    verb menu. we will cache this information to make it
		**    quicker to build the verb menu.
		*/
		// block is only for polestr declaration
		{
		LPOLESTR polestr;

		OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")
		lpContainerLine->m_lpOleObj->lpVtbl->GetUserType(
				lpContainerLine->m_lpOleObj,
				USERCLASSTYPE_SHORT,
				&polestr
		);

		CopyAndFreeOLESTR(polestr, &lpContainerLine->m_lpszShortType);
		}
		OLEDBG_END2

#if defined( INPLACE_CNTR )
		/* OLE2NOTE: an inside-out container should check if the object
		**    is an inside-out and prefers to be activated when visible
		**    type of object. if so, the object should be immediately
		**    activated in-place, BUT NOT UIActived.
		*/
		if (g_fInsideOutContainer &&
				lpContainerLine->m_dwDrawAspect == DVASPECT_CONTENT) {
			DWORD mstat;
			OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n")
			lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus(
					lpContainerLine->m_lpOleObj,
					DVASPECT_CONTENT,
					(DWORD FAR*)&mstat
			);
			OLEDBG_END2

			lpContainerLine->m_fInsideOutObj = (BOOL)
				   (mstat & (OLEMISC_INSIDEOUT|OLEMISC_ACTIVATEWHENVISIBLE));

			if ( lpContainerLine->m_fInsideOutObj ) {
				HWND hWndDoc = OutlineDoc_GetWindow(lpOutlineDoc);

				ContainerLine_DoVerb(
						lpContainerLine,
						OLEIVERB_INPLACEACTIVATE,
						NULL,
						FALSE,
						FALSE
				);

				/* OLE2NOTE: following this DoVerb(INPLACEACTIVATE) the
				**    object may have taken focus. but because the
				**    object is NOT UIActive it should NOT have focus.
				**    we will make sure our document has focus.
				*/
				SetFocus(hWndDoc);
			}
		}
#endif  // INPLACE_CNTR
		OLEDBG_END2

	}

	OLEDBG_END2
	return TRUE;

error:
	OLEDBG_END2
	return FALSE;
}


/* ContainerLine_CloseOleObject
** ----------------------------
**    Close the OLE object associated with the ContainerLine.
**
**    Closing the object forces the object to transition from the
**    running state to the loaded state. if the object was not running,
**    then there is no effect. it is necessary to close the OLE object
**    before releasing the pointers to the OLE object.
**
**    Returns TRUE if successfully closed,
**            FALSE if closing was aborted.
*/
BOOL ContainerLine_CloseOleObject(
		LPCONTAINERLINE         lpContainerLine,
		DWORD                   dwSaveOption
)
{
	HRESULT hrErr;
	SCODE   sc;

	if (lpContainerLine->m_fGuardObj)
		return FALSE;                // object in process of creation

	if (! lpContainerLine->m_lpOleObj)
		return TRUE;    // object is NOT loaded

	OLEDBG_BEGIN2("IOleObject::Close called\r\n")
	hrErr = lpContainerLine->m_lpOleObj->lpVtbl->Close(
			lpContainerLine->m_lpOleObj,
			(dwSaveOption == OLECLOSE_NOSAVE ?
					OLECLOSE_NOSAVE : OLECLOSE_SAVEIFDIRTY)
	);
	OLEDBG_END2

#if defined( INPLACE_CNTR )
	if (lpContainerLine->m_fIpServerRunning) {
		/* OLE2NOTE: unlock the lock held on the in-place object.
		**    it is VERY important that an in-place container
		**    that also support linking to embeddings properly manage
		**    the running of its in-place objects. in an outside-in
		**    style in-place container, when the user clicks
		**    outside of the in-place active object, the object gets
		**    UIDeactivated and the object hides its window. in order
		**    to make the object fast to reactivate, the container
		**    deliberately does not call IOleObject::Close. the object
		**    stays running in the invisible unlocked state. the idea
		**    here is if the user simply clicks outside of the object
		**    and then wants to double click again to re-activate the
		**    object, we do not want this to be slow. if we want to
		**    keep the object running, however, we MUST Lock it
		**    running. otherwise the object will be in an unstable
		**    state where if a linking client does a "silent-update"
		**    (eg. UpdateNow from the Links dialog), then the in-place
		**    server will shut down even before the object has a chance
		**    to be saved back in its container. this saving normally
		**    occurs when the in-place container closes the object. also
		**    keeping the object in the unstable, hidden, running,
		**    not-locked state can cause problems in some scenarios.
		**    ICntrOtl keeps only one object running. if the user
		**    intiates a DoVerb on another object, then that last
		**    running in-place active object is closed. a more
		**    sophistocated in-place container may keep more object running.
		**    (see CntrLine_IPSite_OnInPlaceActivate)
		*/
		lpContainerLine->m_fIpServerRunning = FALSE;

		OLEDBG_BEGIN2("OleLockRunning(FALSE,TRUE) called\r\n")
		OleLockRunning((LPUNKNOWN)lpContainerLine->m_lpOleObj, FALSE, TRUE);
		OLEDBG_END2
	}
#endif

	if (hrErr != NOERROR) {
		OleDbgOutHResult("IOleObject::Close returned", hrErr);
		sc = GetScode(hrErr);
		if (sc == RPC_E_CALL_REJECTED || sc==OLE_E_PROMPTSAVECANCELLED)
			return FALSE;   // object aborted shutdown
	}
	return TRUE;
}


/* ContainerLine_UnloadOleObject
** -----------------------------
**    Close the OLE object associated with the ContainerLine and
**    release all pointer held to the object.
**
**    Closing the object forces the object to transition from the
**    running state to the loaded state. if the object was not running,
**    then there is no effect. it is necessary to close the OLE object
**    before releasing the pointers to the OLE object. releasing all
**    pointers to the object allows the object to transition from
**    loaded to unloaded (or passive).
*/
void ContainerLine_UnloadOleObject(
		LPCONTAINERLINE         lpContainerLine,
		DWORD                   dwSaveOption
)
{
	if (lpContainerLine->m_lpOleObj) {

		OLEDBG_BEGIN2("IOleObject::Close called\r\n")
		lpContainerLine->m_lpOleObj->lpVtbl->Close(
				lpContainerLine->m_lpOleObj, dwSaveOption);
		OLEDBG_END2

		/* OLE2NOTE: we will take our IOleClientSite* pointer away from
		**    the object before we release all pointers to the object.
		**    in the scenario where the object is implemented as an
		**    in-proc server (DLL object), then, if there are link
		**    connections to the DLL object, it is possible that the
		**    object will not be destroyed when we release our pointers
		**    to the object. the existance of the remote link
		**    connections will hold the object alive. later when these
		**    strong connections are released, then the object may
		**    attempt to call IOleClientSite::Save if we had not taken
		**    away the client site pointer.
		*/
		OLEDBG_BEGIN2("IOleObject::SetClientSite(NULL) called\r\n")
		lpContainerLine->m_lpOleObj->lpVtbl->SetClientSite(
				lpContainerLine->m_lpOleObj, NULL);
		OLEDBG_END2

		OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpOleObj);
		lpContainerLine->m_lpOleObj = NULL;

		if (lpContainerLine->m_lpViewObj2) {
			OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpViewObj2);
			lpContainerLine->m_lpViewObj2 = NULL;
		}
		if (lpContainerLine->m_lpPersistStg) {
			OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpPersistStg);
			lpContainerLine->m_lpPersistStg = NULL;
		}

		if (lpContainerLine->m_lpOleLink) {
			OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpOleLink);
			lpContainerLine->m_lpOleLink = NULL;
		}
	}

	if (lpContainerLine->m_lpszShortType) {
		OleStdFreeString(lpContainerLine->m_lpszShortType, NULL);
		lpContainerLine->m_lpszShortType = NULL;
	}
}


/* ContainerLine_Delete
** --------------------
**    Delete the ContainerLine.
**
**    NOTE: we can NOT directly destroy the memory for the
**    ContainerLine; the ContainerLine maintains a reference count. a
**    non-zero reference count indicates that the object is still
**    in-use. The OleObject keeps a reference-counted pointer to the
**    ClientLine object. we must take the actions necessary so that the
**    ContainerLine object receives Releases for outstanding
**    references. when the reference count of the ContainerLine reaches
**    zero, then the memory for the object will actually be destroyed
**    (ContainerLine_Destroy called).
**
*/
void ContainerLine_Delete(LPCONTAINERLINE lpContainerLine)
{
	OLEDBG_BEGIN2("ContainerLine_Delete\r\n")

#if defined( INPLACE_CNTR )
	if (lpContainerLine == lpContainerLine->m_lpDoc->m_lpLastIpActiveLine)
		lpContainerLine->m_lpDoc->m_lpLastIpActiveLine = NULL;
	if (lpContainerLine == lpContainerLine->m_lpDoc->m_lpLastUIActiveLine)
		lpContainerLine->m_lpDoc->m_lpLastUIActiveLine = NULL;
#endif

	/* OLE2NOTE: in order to have a stable line object during the
	**    process of deleting, we intially AddRef the line ref cnt and
	**    later Release it. This initial AddRef is artificial; it is
	**    simply done to guarantee that our object does not destroy
	**    itself until the END of this routine.
	*/
	ContainerLine_AddRef(lpContainerLine);

	// Unload the loaded OLE object
	if (lpContainerLine->m_lpOleObj)
		ContainerLine_UnloadOleObject(lpContainerLine, OLECLOSE_NOSAVE);

	/* OLE2NOTE: we can NOT directly free the memory for the ContainerLine
	**    data structure until everyone holding on to a pointer to our
	**    ClientSite interface and IAdviseSink interface has released
	**    their pointers. There is one refcnt on the ContainerLine object
	**    which is held by the container itself. we will release this
	**    refcnt here.
	*/
	ContainerLine_Release(lpContainerLine);

	/* OLE2NOTE: this call forces all external connections to our
	**    ContainerLine to close down and therefore guarantees that
	**    we receive all releases associated with those external
	**    connections. Strictly this call should NOT be necessary, but
	**    it is defensive coding to make this call.
	*/
	OLEDBG_BEGIN2("CoDisconnectObject(lpContainerLine) called\r\n")
	CoDisconnectObject((LPUNKNOWN)&lpContainerLine->m_Unknown, 0);
	OLEDBG_END2

#if defined( _DEBUG )
	/* at this point the object all references from the OLE object to
	**    our ContainerLine object should have been released. there
	**    should only be 1 remaining reference that will be released below.
	*/
	if (lpContainerLine->m_cRef != 1) {
		OleDbgOutRefCnt(
			"WARNING: ContainerLine_Delete: cRef != 1\r\n",
			lpContainerLine,
			lpContainerLine->m_cRef
		);
	}
#endif

	ContainerLine_Release(lpContainerLine); // release artificial AddRef above
	OLEDBG_END2
}


/* ContainerLine_Destroy
** ---------------------
**    Destroy (Free) the memory used by a ContainerLine structure.
**    This function is called when the ref count of the ContainerLine goes
**    to zero. the ref cnt goes to zero after ContainerLine_Delete forces
**    the OleObject to unload and release its pointers to the
**    ContainerLine IOleClientSite and IAdviseSink interfaces.
*/

void ContainerLine_Destroy(LPCONTAINERLINE lpContainerLine)
{
	LPUNKNOWN lpTmpObj;

	OLEDBG_BEGIN2("ContainerLine_Destroy\r\n")

	// Release the storage opened for the OLE object
	if (lpContainerLine->m_lpStg) {
		lpTmpObj = (LPUNKNOWN)lpContainerLine->m_lpStg;
		lpContainerLine->m_lpStg = NULL;

		OleStdRelease(lpTmpObj);
	}

	if (lpContainerLine->m_lpszShortType) {
		OleStdFreeString(lpContainerLine->m_lpszShortType, NULL);
		lpContainerLine->m_lpszShortType = NULL;
	}

	Delete(lpContainerLine);        // Free the memory for the structure itself
	OLEDBG_END2
}


/* ContainerLine_CopyToDoc
 * -----------------------
 *
 *      Copy a ContainerLine to another Document (usually ClipboardDoc)
 */
BOOL ContainerLine_CopyToDoc(
		LPCONTAINERLINE         lpSrcLine,
		LPOUTLINEDOC            lpDestDoc,
		int                     nIndex
)
{
	LPCONTAINERLINE lpDestLine = NULL;
	LPLINELIST  lpDestLL = &lpDestDoc->m_LineList;
	HDC         hDC;
	HRESULT     hrErr;
	BOOL        fStatus;
	LPSTORAGE   lpDestDocStg = ((LPOLEDOC)lpDestDoc)->m_lpStg;
	LPSTORAGE   lpDestObjStg = NULL;

	lpDestLine = (LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE));
	if (lpDestLine == NULL) {
		OleDbgAssertSz(lpDestLine!=NULL, "Error allocating ContainerLine");
		return FALSE;
	}

	hDC = LineList_GetDC(lpDestLL);
	ContainerLine_Init(lpDestLine, ((LPLINE)lpSrcLine)->m_nTabLevel, hDC);
	LineList_ReleaseDC(lpDestLL, hDC);

	/* OLE2NOTE: In order to have a stable ContainerLine object we must
	**    AddRef the object's refcnt. this will be later released when
	**    the ContainerLine is deleted.
	*/
	ContainerLine_AddRef(lpDestLine);

	lpDestLine->m_lpDoc = (LPCONTAINERDOC)lpDestDoc;

	// Copy data of the original source ContainerLine.
	((LPLINE)lpDestLine)->m_nWidthInHimetric =
			((LPLINE)lpSrcLine)->m_nWidthInHimetric;
	((LPLINE)lpDestLine)->m_nHeightInHimetric =
			((LPLINE)lpSrcLine)->m_nHeightInHimetric;
	lpDestLine->m_fMonikerAssigned = lpSrcLine->m_fMonikerAssigned;
	lpDestLine->m_dwDrawAspect = lpSrcLine->m_dwDrawAspect;
	lpDestLine->m_sizeInHimetric = lpSrcLine->m_sizeInHimetric;
	lpDestLine->m_dwLinkType = lpSrcLine->m_dwLinkType;


	/* We must create a new sub-storage for the embedded object within
	**    the destination document's storage. We will first attempt to
	**    use the same storage name as the source line. if this name is
	**    in use, then we will allocate a new name. in this way we try
	**    to keep the name associated with the OLE object unchanged
	**    through a Cut/Paste operation.
	*/
	lpDestObjStg = OleStdCreateChildStorage(
			lpDestDocStg,
			lpSrcLine->m_szStgName
	);
	if (lpDestObjStg) {
		lstrcpy(lpDestLine->m_szStgName, lpSrcLine->m_szStgName);
	} else {
		/* the original name was in use, make up a new name. */
		ContainerDoc_GetNextStgName(
				(LPCONTAINERDOC)lpDestDoc,
				lpDestLine->m_szStgName,
				sizeof(lpDestLine->m_szStgName)
		);
		lpDestObjStg = OleStdCreateChildStorage(
				lpDestDocStg,
				lpDestLine->m_szStgName
		);
	}
	if (lpDestObjStg == NULL) {
		OleDbgAssertSz(lpDestObjStg != NULL, "Error creating child stg");
		goto error;
	}

	// Copy over storage of the embedded object itself

	if (! lpSrcLine->m_lpOleObj) {

		/*****************************************************************
		** CASE 1: object is NOT loaded.
		**    because the object is not loaded, we can simply copy the
		**    object's current storage to the new storage.
		*****************************************************************/

		/* if current object storage is not already open, then open it */
		if (! lpSrcLine->m_lpStg) {
			LPSTORAGE lpSrcDocStg = ((LPOLEDOC)lpSrcLine->m_lpDoc)->m_lpStg;

			if (! lpSrcDocStg) goto error;

			// open object storage.
			lpSrcLine->m_lpStg = OleStdOpenChildStorage(
					lpSrcDocStg,
					lpSrcLine->m_szStgName,
					STGM_READWRITE
			);
			if (lpSrcLine->m_lpStg == NULL) {
#if defined( _DEBUG )
				OleDbgAssertSz(
						lpSrcLine->m_lpStg != NULL,
						"Error opening child stg"
				);
#endif
				goto error;
			}
		}

		hrErr = lpSrcLine->m_lpStg->lpVtbl->CopyTo(
				lpSrcLine->m_lpStg,
				0,
				NULL,
				NULL,
				lpDestObjStg
		);
		if (hrErr != NOERROR) {
			OleDbgOutHResult("WARNING: lpSrcObjStg->CopyTo returned", hrErr);
			goto error;
		}

		fStatus = OleStdCommitStorage(lpDestObjStg);

	} else {

		/*****************************************************************
		** CASE 2: object IS loaded.
		**    we must tell the object to save into the new storage.
		*****************************************************************/

		SCODE sc = S_OK;
		LPPERSISTSTORAGE lpPersistStg = lpSrcLine->m_lpPersistStg;
		OleDbgAssert(lpPersistStg);

		OLEDBG_BEGIN2("OleSave called\r\n")
		hrErr = OleSave(lpPersistStg, lpDestObjStg, FALSE /*fSameAsLoad*/);
		OLEDBG_END2

		if (hrErr != NOERROR) {
			OleDbgOutHResult("WARNING: OleSave returned", hrErr);
			sc = GetScode(hrErr);
		}

		// OLE2NOTE: even if OleSave fails, SaveCompleted must be called.
		OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n")
		hrErr=lpPersistStg->lpVtbl->SaveCompleted(lpPersistStg,NULL);
		OLEDBG_END2

		if (hrErr != NOERROR) {
			OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr);
			if (sc == S_OK)
				sc = GetScode(hrErr);
		}

		if (sc != S_OK)
			goto error;

	}

	OutlineDoc_AddLine(lpDestDoc, (LPLINE)lpDestLine, nIndex);
	OleStdVerifyRelease(
			(LPUNKNOWN)lpDestObjStg,
			"Copied object stg not released"
	);

	return TRUE;

error:

	// Delete any partially created storage.
	if (lpDestObjStg) {

		OleStdVerifyRelease(
				(LPUNKNOWN)lpDestObjStg,
				"Copied object stg not released"
		);

		{
		CREATEOLESTR(polestr, lpDestLine->m_szStgName)

		lpDestDocStg->lpVtbl->DestroyElement(
				lpDestDocStg,
				lpDestLine->m_szStgName
		);

		FREEOLESTR(polestr);
		}

		lpDestLine->m_szStgName[0] = '\0';
	}

	// destroy partially created ContainerLine
	if (lpDestLine)
		ContainerLine_Delete(lpDestLine);
	return FALSE;
}


/* ContainerLine_UpdateExtent
** --------------------------
**   Update the size of the ContainerLine because the extents of the
**    object may have changed.
**
**    NOTE: because we are using a Windows OwnerDraw ListBox, we must
**    constrain the maximum possible height of a line. the ListBox has
**    a limitation (unfortunately) that no line can become larger than
**    255 pixels. thus we force the object to scale maintaining its
**    aspect ratio if this maximum line height limit is reached. the
**    actual maximum size for an object at 100% Zoom is
**    255
**
**    RETURNS TRUE -- if the extents of the object changed
**            FALSE -- if the extents did NOT change
*/
BOOL ContainerLine_UpdateExtent(
		LPCONTAINERLINE     lpContainerLine,
		LPSIZEL             lpsizelHim
)
{
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
	LPLINELIST lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
	LPLINE lpLine = (LPLINE)lpContainerLine;
	int nIndex = LineList_GetLineIndex(lpLL, lpLine);
	UINT nOrgWidthInHimetric = lpLine->m_nWidthInHimetric;
	UINT nOrgHeightInHimetric = lpLine->m_nHeightInHimetric;
	BOOL fWidthChanged = FALSE;
	BOOL fHeightChanged = FALSE;
	SIZEL sizelHim;
	HRESULT hrErr;

	if (!lpContainerLine || !lpContainerLine->m_lpOleObj)
		return FALSE;

	if (lpContainerLine->m_fGuardObj)
		return FALSE;                // object in process of creation

	OLEDBG_BEGIN3("ContainerLine_UpdateExtent\r\n");

	lpContainerLine->m_fDoGetExtent = FALSE;

	if (! lpsizelHim) {
		/* OLE2NOTE: We want to call IViewObject2::GetExtent instead of
		**    IOleObject::GetExtent. IViewObject2::GetExtent method was
		**    added in OLE 2.01 release. It always retrieves the
		**    extents of the object corresponding to that which will be
		**    drawn by calling IViewObject::Draw. Normally, this is
		**    determined by the data stored in the data cache. This
		**    call will never result in a remoted (LRPC) call.
		*/
		OLEDBG_BEGIN2("IViewObject2::GetExtent called\r\n")
		hrErr = lpContainerLine->m_lpViewObj2->lpVtbl->GetExtent(
				lpContainerLine->m_lpViewObj2,
				lpContainerLine->m_dwDrawAspect,
				-1,     /*lindex*/
				NULL,   /*ptd*/
				(LPSIZEL)&sizelHim
		);
		OLEDBG_END2
		if (hrErr != NOERROR)
			sizelHim.cx = sizelHim.cy = 0;

		lpsizelHim = (LPSIZEL)&sizelHim;
	}

	if (lpsizelHim->cx == lpContainerLine->m_sizeInHimetric.cx &&
		lpsizelHim->cy == lpContainerLine->m_sizeInHimetric.cy) {
		goto noupdate;
	}

	if (lpsizelHim->cx > 0 || lpsizelHim->cy > 0) {
		lpContainerLine->m_sizeInHimetric = *lpsizelHim;
	} else {
		/* object does not have any extents; let's use our container
		**    chosen arbitrary size for OLE objects.
		*/
		lpContainerLine->m_sizeInHimetric.cx = (long)DEFOBJWIDTH;
		lpContainerLine->m_sizeInHimetric.cy = (long)DEFOBJHEIGHT;
	}

	ContainerLine_SetLineHeightFromObjectExtent(
			lpContainerLine,
			(LPSIZEL)&lpContainerLine->m_sizeInHimetric);

	// if height of object changed, then reset the height of line in LineList
	if (nOrgHeightInHimetric != lpLine->m_nHeightInHimetric) {
		LineList_SetLineHeight(lpLL, nIndex, lpLine->m_nHeightInHimetric);
		fHeightChanged = TRUE;
	}

	fWidthChanged = LineList_RecalcMaxLineWidthInHimetric(
			lpLL,
			nOrgWidthInHimetric
	);
	fWidthChanged |= (nOrgWidthInHimetric != lpLine->m_nWidthInHimetric);

	if (fHeightChanged || fWidthChanged) {
		OutlineDoc_ForceRedraw(lpOutlineDoc, TRUE);

		// mark ContainerDoc as now dirty
		OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, TRUE);
	}

	OLEDBG_END3
	return TRUE;

noupdate:
	OLEDBG_END3
	return FALSE;   // No UPDATE necessary
}


/* ContainerLine_DoVerb
** --------------------
**    Activate the OLE object and perform a specific verb.
*/
BOOL ContainerLine_DoVerb(
		LPCONTAINERLINE lpContainerLine,
		LONG            iVerb,
		LPMSG           lpMsg,
		BOOL            fMessage,
		BOOL            fAction
)
{
	HRESULT hrErr;
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
	RECT rcPosRect;
	OLEDBG_BEGIN3("ContainerLine_DoVerb\r\n")

	if (lpContainerLine->m_fGuardObj) {
		// object in process of creation--Fail the DoVerb call
		hrErr = ResultFromScode(E_FAIL);
		goto error;
	}

	/* if object is not already loaded, then load it now. objects are
	**    loaded lazily in this manner.
	*/
	if (! lpContainerLine->m_lpOleObj)
		ContainerLine_LoadOleObject(lpContainerLine);

	if (! lpContainerLine->m_lpOleObj) {
#if defined( _DEBUG )
		OleDbgAssertSz(
				lpContainerLine->m_lpOleObj != NULL,
				"OLE object not loaded"
		);
#endif
		goto error;
	}

ExecuteDoVerb:

	ContainerLine_GetPosRect(lpContainerLine, (LPRECT)&rcPosRect);

	// run the object
	hrErr = ContainerLine_RunOleObject(lpContainerLine);
	if (hrErr != NOERROR)
		goto error;

	/* Tell object server to perform a "verb". */
	OLEDBG_BEGIN2("IOleObject::DoVerb called\r\n")
	hrErr = lpContainerLine->m_lpOleObj->lpVtbl->DoVerb (
			lpContainerLine->m_lpOleObj,
			iVerb,
			lpMsg,
			(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
			-1,
			OutlineDoc_GetWindow(lpOutlineDoc),
			(LPCRECT)&rcPosRect
	);
	OLEDBG_END2

	/* OLE2NOTE: IOleObject::DoVerb may return a success code
	**    OLE_S_INVALIDVERB. this SCODE should NOT be considered an
	**    error; thus it is important to use the "FAILED" macro to
	**    check for an error SCODE.
	*/
	if (FAILED(GetScode(hrErr))) {
		OleDbgOutHResult("WARNING: lpOleObj->DoVerb returned", hrErr);
		goto error;
	}

#if defined( INPLACE_CNTR )
	/* OLE2NOTE: we want to keep only 1 inplace server active at any
	**    given time. so when we start to do a DoVerb on another line,
	**    then we want to shut down the previously activated server. in
	**    this way we keep at most one inplace server active at a time.
	**    because it is possible that the object we want to do DoVerb
	**    on is handled by the same EXE as that of the previously
	**    activated server, then we do not want the EXE to be shut down
	**    only to be launched again. in order to avoid this we will do
	**    the DoVerb BEFORE trying to shutdown the previous object.
	*/
	if (!g_fInsideOutContainer) {
		ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded(
				lpContainerLine->m_lpDoc, lpContainerLine);
	}
#endif  // INPLACE_CNTR

	OLEDBG_END3
	return TRUE;

error:

	if (lpContainerLine->m_dwLinkType != 0)
		lpContainerLine->m_fLinkUnavailable = TRUE;

#if defined( INPLACE_CNTR )
	/* OLE2NOTE: we want to keep only 1 inplace server active at any
	**    given time. so when we start to do a DoVerb on another line,
	**    then we want to shut down the previously activated server. in
	**    this way we keep at most one inplace server active at a time.
	**    even though the DoVerb failed, we will still shutdown the
	**    previous server. it is possible that we ran out of memory and
	**    that the DoVerb will succeed next time after shutting down
	**    the pervious server.
	*/
	if (!g_fInsideOutContainer) {
		ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded(
				lpContainerLine->m_lpDoc, lpContainerLine);
	}
#endif  // INPLACE_CNTR

	/* OLE2NOTE: if an error occurs we must give the appropriate error
	**    message box. there are many potential errors that can occur.
	**    the OLE2.0 user model has specific guidelines as to the
	**    dialogs that should be displayed given the various potential
	**    errors (eg. server not registered, unavailable link source.
	**    the OLE2UI library includes support for most of the
	**    recommended message dialogs. (see OleUIPrompUser function)
	*/
	if (fMessage) {
		BOOL fReDoVerb = ContainerLine_ProcessOleRunError(
				lpContainerLine,
				hrErr,
				fAction,
				(lpMsg==NULL && iVerb>=0)   /* fMenuInvoked */
		);
		if (fReDoVerb) {
			goto ExecuteDoVerb;
		}
	}

	OLEDBG_END3
	return FALSE;
}



/* ContainerLine_ProcessOleRunError
 * --------------------------------
 *
 *  Handle the various errors possible when attempting OleRun of an object.
 *  Popup up appropriate message according to the error and/or take action
 *  specified button pressed by the user.
 *
 *  OLE2NOTE: The OLE 2.0 User Interface Guidelines specify the messages
 *            that should be given for the following situations:
 *                  1. Link Source Unavailable...goto Links dialog
 *                  2. Server Not Registered...goto Convert dialog
 *                  3. Link Type Changed
 *                  4. Server Not Found
 *
 *  Returns:    TRUE -- repeat IOleObject::DoVerb call.
 *              FALSE -- do NOT repeat IOleObject::DoVerb call.
 *
 *  Comments:
 *      (see LinkTypeChanged case)
 */
BOOL ContainerLine_ProcessOleRunError(
		LPCONTAINERLINE         lpContainerLine,
		HRESULT                 hrErr,
		BOOL                    fAction,
		BOOL                    fMenuInvoked
)
{
	LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
	HWND            hwndParent = OutlineDoc_GetWindow(lpOutlineDoc);
	SCODE           sc = GetScode(hrErr);
	BOOL            fReDoVerb = FALSE;

	OleDbgOutHResult("ProcessError", hrErr);
	if ((sc >= MK_E_FIRST) && (sc <= MK_E_LAST))
		goto LinkSourceUnavailable;
	if (sc == OLE_E_CANT_BINDTOSOURCE)
		goto LinkSourceUnavailable;
	if (sc == STG_E_PATHNOTFOUND)
		goto LinkSourceUnavailable;
	if (sc == REGDB_E_CLASSNOTREG)
		goto ServerNotReg;
	if (sc == OLE_E_STATIC)
		goto ServerNotReg;  // user dblclk'ed a static object w/ no svr reg'd
	if (sc == OLE_E_CLASSDIFF)
		goto LinkTypeChanged;
	if (sc == CO_E_APPDIDNTREG)
		goto ServerNotFound;
	if (sc == CO_E_APPNOTFOUND)
		goto ServerNotFound;
	if (sc == E_OUTOFMEMORY)
		goto OutOfMemory;

	if (ContainerLine_IsOleLink(lpContainerLine))
		goto LinkSourceUnavailable;
	else
		goto ServerNotFound;


/*************************************************************************
** Error handling routines                                              **
*************************************************************************/
LinkSourceUnavailable:
	if (ID_PU_LINKS == OleUIPromptUser(
				(WORD)IDD_LINKSOURCEUNAVAILABLE,
				hwndParent,
				(LPSTR)APPNAME)) {
		if (fAction) {
			ContainerDoc_EditLinksCommand(lpContainerLine->m_lpDoc);
		}
	}
	return fReDoVerb;

ServerNotReg:
	{
	LPSTR lpszUserType = NULL;
	CLIPFORMAT  cfFormat;	    // not used
	LPOLESTR polestr;

	hrErr = ReadFmtUserTypeStg(
			lpContainerLine->m_lpStg, &cfFormat, &polestr);

	CopyAndFreeOLESTR(polestr, &lpszUserType);

	if (ID_PU_CONVERT == OleUIPromptUser(
			(WORD)IDD_SERVERNOTREG,
			hwndParent,
			(LPSTR)APPNAME,
			(hrErr == NOERROR) ? lpszUserType : (LPSTR)"Unknown Object")) {
		if (fAction) {
			ContainerDoc_ConvertCommand(
					lpContainerLine->m_lpDoc,
					TRUE        // fMustActivate
			);
		}
	}

	if (lpszUserType)
		OleStdFreeString(lpszUserType, NULL);

	return fReDoVerb;
	}


LinkTypeChanged:
	{
	/* OLE2NOTE: If IOleObject::DoVerb is executed on a Link object and it
	**    returns OLE_E_CLASSDIFF because the link source is no longer
	**    the expected class, then if the verb is a semantically
	**    defined verb (eg. OLEIVERB_PRIMARY, OLEIVERB_SHOW,
	**    OLEIVERB_OPEN, etc.), then the link should be re-created with
	**    the new link source and the same verb executed on the new
	**    link. there is no need to give a message to the user. if the
	**    user had selected a verb from the object's verb menu
	**    (fMenuInvoked==TRUE), then we can not be certain of the
	**    semantics of the verb and whether the new link can still
	**    support the verb. in this case the user is given a prompt
	**    telling him to "choose a different command offered by the new
	**    type".
	*/

	LPSTR       lpszUserType = NULL;

	if (fMenuInvoked) {
		LPOLESTR polestr;

		hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserType(
				lpContainerLine->m_lpOleObj,USERCLASSTYPE_FULL, polestr);

		CopyAndFreeOLESTR(polestr, &lpszUserType);

		OleUIPromptUser(
				(WORD)IDD_LINKTYPECHANGED,
				hwndParent,
				(LPSTR)APPNAME,
				(hrErr == NOERROR) ? lpszUserType : (LPSTR)"Unknown Object"
		);
	} else {
		fReDoVerb = TRUE;
	}
	ContainerLine_ReCreateLinkBecauseClassDiff(lpContainerLine);

	if (lpszUserType)
		OleStdFreeString(lpszUserType, NULL);

	return fReDoVerb;
	}

ServerNotFound:

	OleUIPromptUser(
			(WORD)IDD_SERVERNOTFOUND,
			hwndParent,
			(LPSTR)APPNAME);
	return fReDoVerb;

OutOfMemory:

	OleUIPromptUser(
			(WORD)IDD_OUTOFMEMORY,
			hwndParent,
			(LPSTR)APPNAME);
	return fReDoVerb;
}


/* ContainerLine_ReCreateLinkBecauseClassDiff
** ------------------------------------------
**    Re-create the link. The existing link was created when
**    the moniker binds to a link source bound of a different class
**    than the same moniker currently binds to. the link may be a
**    special link object specifically used with the old link
**    source class. thus the link object needs to be re-created to
**    give the new link source the opportunity to create its own
**    special link object. (see description "Custom Link Source")
*/
HRESULT ContainerLine_ReCreateLinkBecauseClassDiff(
		LPCONTAINERLINE lpContainerLine
)
{
	LPOLELINK   lpOleLink = lpContainerLine->m_lpOleLink;
	HGLOBAL     hMetaPict = NULL;
	LPMONIKER   lpmkLinkSrc = NULL;
	SCODE       sc = E_FAIL;
	HRESULT     hrErr;

	if (lpOleLink &&
		lpOleLink->lpVtbl->GetSourceMoniker(
				lpOleLink, (LPMONIKER FAR*)&lpmkLinkSrc) == NOERROR) {

		BOOL            fDisplayAsIcon =
							(lpContainerLine->m_dwDrawAspect==DVASPECT_ICON);
		STGMEDIUM       medium;
		LPDATAOBJECT    lpDataObj = NULL;
		DWORD           dwOleRenderOpt;
		FORMATETC       renderFmtEtc;
		LPFORMATETC     lpRenderFmtEtc = NULL;

		// get the current icon if object is displayed as icon
		if (fDisplayAsIcon &&
			(lpDataObj = (LPDATAOBJECT)OleStdQueryInterface( (LPUNKNOWN)
					lpContainerLine->m_lpOleObj,&IID_IDataObject)) != NULL ) {
			hMetaPict = OleStdGetData(
					lpDataObj, CF_METAFILEPICT, NULL, DVASPECT_ICON, &medium);
			OleStdRelease((LPUNKNOWN)lpDataObj);
		}

		if (fDisplayAsIcon && hMetaPict) {
			// a special icon should be used. first we create the object
			// OLERENDER_NONE. then we stuff the special icon into the cache.

			dwOleRenderOpt = OLERENDER_NONE;

		} else if (fDisplayAsIcon && hMetaPict == NULL) {
			// the object's default icon should be used

			dwOleRenderOpt = OLERENDER_DRAW;
			lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc;
			SETFORMATETC(renderFmtEtc,0,DVASPECT_ICON,NULL,TYMED_NULL,-1);

		} else {
			// create standard DVASPECT_CONTENT/OLERENDER_DRAW object
			dwOleRenderOpt = OLERENDER_DRAW;
		}

		// unload original link object
		ContainerLine_UnloadOleObject(lpContainerLine, OLECLOSE_SAVEIFDIRTY);

		// delete entire contents of the current object's storage
		OleStdDestroyAllElements(lpContainerLine->m_lpStg);

		OLEDBG_BEGIN2("OleCreateLink called\r\n")
		hrErr = OleCreateLink (
				lpmkLinkSrc,
				&IID_IOleObject,
				dwOleRenderOpt,
				lpRenderFmtEtc,
				(LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite,
				lpContainerLine->m_lpStg,
				(LPVOID FAR*)&lpContainerLine->m_lpOleObj
		);
		OLEDBG_END2

		if (hrErr == NOERROR) {
			if (! ContainerLine_SetupOleObject(
					lpContainerLine, fDisplayAsIcon, hMetaPict) ) {

				// ERROR: setup of the new link failed.
				// revert the storage to restore the original link.
				ContainerLine_UnloadOleObject(
						lpContainerLine, OLECLOSE_NOSAVE);
				lpContainerLine->m_lpStg->lpVtbl->Revert(
						lpContainerLine->m_lpStg);
				sc = E_FAIL;
			} else {
				sc = S_OK;  // IT WORKED!

			}
		}
		else {
			sc = GetScode(hrErr);
			OleDbgOutHResult("OleCreateLink returned", hrErr);
			// ERROR: Re-creating the link failed.
			// revert the storage to restore the original link.
			lpContainerLine->m_lpStg->lpVtbl->Revert(
					lpContainerLine->m_lpStg);
		}
	}

	if (hMetaPict)
		OleUIMetafilePictIconFree(hMetaPict); // clean up metafile
	return ResultFromScode(sc);
}

/* ContainerLine_GetOleObject
** --------------------------
**    return pointer to desired interface of embedded/linked object.
**
**    NOTE: this function causes an AddRef to the object. when the caller is
**          finished with the object, it must call Release.
**          this function does not AddRef the ContainerLine object.
*/
LPUNKNOWN ContainerLine_GetOleObject(
		LPCONTAINERLINE         lpContainerLine,
		REFIID                  riid
)
{
	/* if object is not already loaded, then load it now. objects are
	**    loaded lazily in this manner.
	*/
	if (! lpContainerLine->m_lpOleObj)
		ContainerLine_LoadOleObject(lpContainerLine);

	if (lpContainerLine->m_lpOleObj)
		return OleStdQueryInterface(
				(LPUNKNOWN)lpContainerLine->m_lpOleObj,
				riid
		);
	else
		return NULL;
}



/* ContainerLine_RunOleObject
** --------------------------
**    Load and run the object. Upon running and if size of object has changed,
**    use SetExtent to change to new size.
**
*/
HRESULT ContainerLine_RunOleObject(LPCONTAINERLINE lpContainerLine)
{
	LPLINE lpLine = (LPLINE)lpContainerLine;
	SIZEL   sizelNew;
	HRESULT hrErr;
	HCURSOR  hPrevCursor;

	if (! lpContainerLine)
		return NOERROR;

	if (lpContainerLine->m_fGuardObj) {
		// object in process of creation--Fail to Run the object
		return ResultFromScode(E_FAIL);
	}

	if (lpContainerLine->m_lpOleObj &&
		OleIsRunning(lpContainerLine->m_lpOleObj))
		return NOERROR;     // object already running

	// this may take a while, put up hourglass cursor
	hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
	OLEDBG_BEGIN3("ContainerLine_RunOleObject\r\n")

	if (! lpContainerLine->m_lpOleObj) {
		if (! ContainerLine_LoadOleObject(lpContainerLine))
			return ResultFromScode(E_OUTOFMEMORY); // Error: couldn't load obj
	}

	OLEDBG_BEGIN2("OleRun called\r\n")
	hrErr = OleRun((LPUNKNOWN)lpContainerLine->m_lpOleObj);
	OLEDBG_END2

	if (hrErr != NOERROR) {
		SetCursor(hPrevCursor);     // restore original cursor

		OleDbgOutHResult("OleRun returned", hrErr);
		OLEDBG_END3
		return hrErr;
	}

	if (lpContainerLine->m_fDoSetExtent) {
		/* OLE2NOTE: the OLE object was resized when it was not running
		**    and the object did not have the OLEMISC_RECOMPOSEONRESIZE
		**    bit set. if it had, the object would have been run
		**    immediately when it was resized. this flag indicates that
		**    the object does something other than simple scaling when
		**    it is resized. because the object is being run now, we
		**    will call IOleObject::SetExtent.
		*/
		lpContainerLine->m_fDoSetExtent = FALSE;

		// the size stored in our Line includes the border around the object.
		// we must subtract the border to get the size of the object itself.
		sizelNew.cx = lpLine->m_nWidthInHimetric;
		sizelNew.cy = lpLine->m_nHeightInHimetric;

		if ((sizelNew.cx != lpContainerLine->m_sizeInHimetric.cx) ||
			(sizelNew.cy != lpContainerLine->m_sizeInHimetric.cy)) {

			OLEDBG_BEGIN2("IOleObject::SetExtent called\r\n")
			lpContainerLine->m_lpOleObj->lpVtbl->SetExtent(
					lpContainerLine->m_lpOleObj,
					lpContainerLine->m_dwDrawAspect,
					(LPSIZEL)&sizelNew
			);
			OLEDBG_END2
		}
	}

	SetCursor(hPrevCursor);     // restore original cursor

	OLEDBG_END3
	return NOERROR;

}


/* ContainerLine_IsOleLink
** -----------------------
**
**    return TRUE if the ContainerLine has an OleLink.
**           FALSE if the ContainerLine has an embedding
*/
BOOL ContainerLine_IsOleLink(LPCONTAINERLINE lpContainerLine)
{
	if (!lpContainerLine)
		return FALSE;

	return (lpContainerLine->m_dwLinkType != 0);
}


/*  ContainerLine_Draw
**  ------------------
**
**      Draw a ContainerLine object on a DC.
**
**  Parameters:
**      hDC     - DC to which the line will be drawn
**      lpRect  - the object rect in logical coordinates
**      lpRectWBounds - bounding rect of the metafile underneath hDC
**                      (NULL if hDC is not a metafile DC)
**      fHighlight    - TRUE if line has selection highlight
*/
void ContainerLine_Draw(
		LPCONTAINERLINE         lpContainerLine,
		HDC                     hDC,
		LPRECT                  lpRect,
		LPRECT                  lpRectWBounds,
		BOOL                    fHighlight
)
{
	LPLINE  lpLine = (LPLINE) lpContainerLine;
	HRESULT hrErr = NOERROR;
	RECTL   rclHim;
	RECTL   rclHimWBounds;
	RECT    rcHim;

	if (lpContainerLine->m_fGuardObj) {
		// object in process of creation--do NOT try to draw
		return;
	}

	/* if object is not already loaded, then load it now. objects are
	**    loaded lazily in this manner.
	*/
	if (! lpContainerLine->m_lpViewObj2) {
		if (! ContainerLine_LoadOleObject(lpContainerLine))
			return;     // Error: could not load object
	}

	if (lpRectWBounds) {
		rclHimWBounds.left      = (long) lpRectWBounds->left;
		rclHimWBounds.bottom    = (long) lpRectWBounds->bottom;
		rclHimWBounds.top       = (long) lpRectWBounds->top;
		rclHimWBounds.right     = (long) lpRectWBounds->right;
	}

	/* construct bounds rectangle for the object.
	**  offset origin for object to correct tab indentation
	*/
	rclHim.left     = (long) lpRect->left;
	rclHim.bottom   = (long) lpRect->bottom;
	rclHim.top      = (long) lpRect->top;
	rclHim.right    = (long) lpRect->right;

	rclHim.left += (long) ((LPLINE)lpContainerLine)->m_nTabWidthInHimetric;
	rclHim.right += (long) ((LPLINE)lpContainerLine)->m_nTabWidthInHimetric;

#if defined( INPLACE_CNTR )
	/* OLE2NOTE: if the OLE object currently has a visible in-place
	**    window, then we do NOT want to draw on top of its window.
	**    this could interfere with the object's display.
	*/
	if ( !lpContainerLine->m_fIpVisible )
#endif
	{
	hrErr = lpContainerLine->m_lpViewObj2->lpVtbl->Draw(
			lpContainerLine->m_lpViewObj2,
			lpContainerLine->m_dwDrawAspect,
			-1,
			NULL,
			NULL,
			NULL,
			hDC,
			(LPRECTL)&rclHim,
			(lpRectWBounds ? (LPRECTL)&rclHimWBounds : NULL),
			NULL,
			0
	);
	if (hrErr != NOERROR)
		OleDbgOutHResult("IViewObject::Draw returned", hrErr);

	if (lpContainerLine->m_fObjWinOpen)
		{
		rcHim.left      = (int) rclHim.left;
		rcHim.top       = (int) rclHim.top;
		rcHim.right     = (int) rclHim.right;
		rcHim.bottom    = (int) rclHim.bottom;

		/* OLE2NOTE: if the object servers window is Open (ie. not active
		**    in-place) then we must shade the object in our document to
		**    indicate to the user that the object is open elsewhere.
		*/
		OleUIDrawShading((LPRECT)&rcHim, hDC, OLEUI_SHADE_FULLRECT, 0);
		}
	}

	/* if the object associated with the ContainerLine is an automatic
	**    link then try to connect it with its LinkSource if the
	**    LinkSource is already running. we do not want to force the
	**    LinkSource to run.
	**
	**    OLE2NOTE: a sophistocated container will want to continually
	**    attempt to connect its automatic links. OLE does NOT
	**    automatically connect links when link sources become
	**    available. some containers will want to attempt to connect
	**    its links as part of idle time processing. another strategy
	**    is to attempt to connect an automatic link every time it is
	**    drawn on the screen. (this is the strategy used by this
	**    CntrOutl sample application.)
	*/
	if (lpContainerLine->m_dwLinkType == OLEUPDATE_ALWAYS)
		ContainerLine_BindLinkIfLinkSrcIsRunning(lpContainerLine);

	return;
}


void ContainerLine_DrawSelHilight(
		LPCONTAINERLINE lpContainerLine,
		HDC             hDC,            // MM_TEXT mode
		LPRECT          lprcPix,        // listbox rect
		UINT            itemAction,
		UINT            itemState
)
{
	LPLINE  lpLine = (LPLINE)lpContainerLine;
	RECT    rcObj;
	DWORD   dwFlags = OLEUI_HANDLES_INSIDE | OLEUI_HANDLES_USEINVERSE;
	int     nHandleSize;
	LPCONTAINERDOC lpContainerDoc;

	if (!lpContainerLine || !hDC || !lprcPix)
		return;

	lpContainerDoc = lpContainerLine->m_lpDoc;

	// Get size of OLE object
	ContainerLine_GetOleObjectRectInPixels(lpContainerLine, (LPRECT)&rcObj);

	nHandleSize = GetProfileInt("windows", "oleinplaceborderwidth",
			DEFAULT_HATCHBORDER_WIDTH) + 1;

	OleUIDrawHandles((LPRECT)&rcObj, hDC, dwFlags, nHandleSize, TRUE);
}

/* InvertDiffRect
** --------------
**
**    Paint the surrounding of the Obj rect black but within lprcPix
**      (similar to the lprcPix minus lprcObj)
*/
static void InvertDiffRect(LPRECT lprcPix, LPRECT lprcObj, HDC hDC)
{
	RECT rcBlack;

	// draw black in all space outside of object's rectangle
	rcBlack.top = lprcPix->top;
	rcBlack.bottom = lprcPix->bottom;

	rcBlack.left = lprcPix->left + 1;
	rcBlack.right = lprcObj->left - 1;
	InvertRect(hDC, (LPRECT)&rcBlack);

	rcBlack.left = lprcObj->right + 1;
	rcBlack.right = lprcPix->right - 1;
	InvertRect(hDC, (LPRECT)&rcBlack);

	rcBlack.top = lprcPix->top;
	rcBlack.bottom = lprcPix->top + 1;
	rcBlack.left = lprcObj->left - 1;
	rcBlack.right = lprcObj->right + 1;
	InvertRect(hDC, (LPRECT)&rcBlack);

	rcBlack.top = lprcPix->bottom;
	rcBlack.bottom = lprcPix->bottom - 1;
	rcBlack.left = lprcObj->left - 1;
	rcBlack.right = lprcObj->right + 1;
	InvertRect(hDC, (LPRECT)&rcBlack);
}


/* Edit the ContainerLine line object.
**      returns TRUE if line was changed
**              FALSE if the line was NOT changed
*/
BOOL ContainerLine_Edit(LPCONTAINERLINE lpContainerLine, HWND hWndDoc,HDC hDC)
{
	ContainerLine_DoVerb(lpContainerLine, OLEIVERB_PRIMARY, NULL, TRUE, TRUE);

	/* assume object was NOT changed, if it was obj will send Changed
	**    or Saved notification.
	*/
	return FALSE;
}



/* ContainerLine_SetHeightInHimetric
** ---------------------------------
**
** Set the height of a ContainerLine object. The widht will be changed
** to keep the aspect ratio
*/
void ContainerLine_SetHeightInHimetric(LPCONTAINERLINE lpContainerLine, int nHeight)
{
	LPLINE  lpLine = (LPLINE)lpContainerLine;
	SIZEL   sizelOleObject;
	HRESULT hrErr;

	if (!lpContainerLine)
		return;

	if (lpContainerLine->m_fGuardObj) {
		// object in process of creation--Fail to set the Height
		return;
	}

	if (nHeight != -1) {
		BOOL    fMustClose = FALSE;
		BOOL    fMustRun   = FALSE;

		/* if object is not already loaded, then load it now. objects are
		**    loaded lazily in this manner.
		*/
		if (! lpContainerLine->m_lpOleObj)
			ContainerLine_LoadOleObject(lpContainerLine);

		// the height argument specifies the desired height for the Line.
		sizelOleObject.cy = nHeight;

		// we will calculate the corresponding width for the object by
		// maintaining the current aspect ratio of the object.
		sizelOleObject.cx = (int)(sizelOleObject.cy *
				lpContainerLine->m_sizeInHimetric.cx /
				lpContainerLine->m_sizeInHimetric.cy);

		/* OLE2NOTE: if the OLE object is already running then we can
		**    immediately call SetExtent. But, if the object is NOT
		**    currently running then we will check if the object
		**    indicates that it is normally recomposes itself on
		**    resizing. ie. that the object does not simply scale its
		**    display when it it resized. if so then we will force the
		**    object to run so that we can call IOleObject::SetExtent.
		**    SetExtent does not have any effect if the object is only
		**    loaded. if the object does NOT indicate that it
		**    recomposes on resize (OLEMISC_RECOMPOSEONRESIZE) then we
		**    will wait till the next time that the object is run to
		**    call SetExtent. we will store a flag in the ContainerLine
		**    to indicate that a SetExtent is necessary. It is
		**    necessary to persist this flag.
		*/
		if (! OleIsRunning(lpContainerLine->m_lpOleObj)) {
			DWORD dwStatus;

			OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n")
			hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus(
					lpContainerLine->m_lpOleObj,
					lpContainerLine->m_dwDrawAspect,
					(LPDWORD)&dwStatus
			);
			OLEDBG_END2
			if (hrErr == NOERROR && (dwStatus & OLEMISC_RECOMPOSEONRESIZE)) {
				// force the object to run
				ContainerLine_RunOleObject(lpContainerLine);
				fMustClose = TRUE;
			} else {
				/*  the OLE object is NOT running and does NOT
				**    recompose on resize. simply scale the object now
				**    and do the SetExtent the next time the object is
				**    run. we set the Line to the new size even though
				**    the object's extents have not been changed.
				**    this has the result of scaling the object's
				**    display to the new size.
				*/
				lpContainerLine->m_fDoSetExtent = TRUE;
				ContainerLine_SetLineHeightFromObjectExtent(
						lpContainerLine, (LPSIZEL)&sizelOleObject);
				return;
			}
		}

		OLEDBG_BEGIN2("IOleObject::SetExtent called\r\n")
		hrErr = lpContainerLine->m_lpOleObj->lpVtbl->SetExtent(
				lpContainerLine->m_lpOleObj,
				lpContainerLine->m_dwDrawAspect,
				(LPSIZEL)&sizelOleObject);
		OLEDBG_END2

		if (hrErr != NOERROR) {
			/* OLE Object refuses to take on the new extents. Set the
			**    Line to the new size even though the object refused
			**    the new extents. this has the result of scaling the
			**    object's display to the new size.
			**
			**    if the object HAD accepted the new extents, then it
			**    will send out an OnViewChange/OnDataChange
			**    notification. this results in our container receiving
			**    an OnViewChange notification; the line height will be
			**    reset when this notification is received.
			*/
			ContainerLine_SetLineHeightFromObjectExtent(
					lpContainerLine, (LPSIZEL)&sizelOleObject);
		}

		if (fMustClose)
			ContainerLine_CloseOleObject(
					lpContainerLine, OLECLOSE_SAVEIFDIRTY);
	}
	else {
		/* Set the line to default height given the natural (unscaled)
		**    extents of the OLE object.
		*/
		ContainerLine_SetLineHeightFromObjectExtent(
				lpContainerLine,(LPSIZEL)&lpContainerLine->m_sizeInHimetric);
	}

}


/*  ContainerLine_SetLineHeightFromObjectExtent
 *
 *  Purpose:
 *      Calculate the corresponding line height from the OleObject size
 *      Scale the line height to fit the limit if necessary
 *
 *  Parameters:
 *      lpsizelOleObject        pointer to size of OLE Object
 *
 *  Returns:
 *      nil
 */
void ContainerLine_SetLineHeightFromObjectExtent(
		LPCONTAINERLINE         lpContainerLine,
		LPSIZEL                 lpsizelOleObject
)
{
	LPLINE lpLine = (LPLINE)lpContainerLine;

	UINT uMaxObjectHeight = XformHeightInPixelsToHimetric(NULL,
			LISTBOX_HEIGHT_LIMIT);

	if (!lpContainerLine || !lpsizelOleObject)
		return;

	if (lpContainerLine->m_fGuardObj) {
		// object in process of creation--Fail to set the Height
		return;
	}

	lpLine->m_nWidthInHimetric = (int)lpsizelOleObject->cx;
	lpLine->m_nHeightInHimetric = (int)lpsizelOleObject->cy;

	// Rescale the object if height is greater than the limit
	if (lpLine->m_nHeightInHimetric > (UINT)uMaxObjectHeight) {

		lpLine->m_nWidthInHimetric = (UINT)
				((long)lpLine->m_nWidthInHimetric *
				(long)uMaxObjectHeight /
				(long)lpLine->m_nHeightInHimetric);

		lpLine->m_nHeightInHimetric = uMaxObjectHeight;
	}

}


/* ContainerLine_SaveToStg
** -----------------------
**    Save a given ContainerLine and associated OLE object to an IStorage*.
*/
BOOL ContainerLine_SaveToStm(
		LPCONTAINERLINE         lpContainerLine,
		LPSTREAM                lpLLStm
)
{
	CONTAINERLINERECORD objLineRecord;
	ULONG nWritten;
	HRESULT hrErr;

	lstrcpy(objLineRecord.m_szStgName, lpContainerLine->m_szStgName);
	objLineRecord.m_fMonikerAssigned = lpContainerLine->m_fMonikerAssigned;
	objLineRecord.m_dwDrawAspect = lpContainerLine->m_dwDrawAspect;
	objLineRecord.m_sizeInHimetric = lpContainerLine->m_sizeInHimetric;
	objLineRecord.m_dwLinkType = lpContainerLine->m_dwLinkType;
	objLineRecord.m_fDoSetExtent = lpContainerLine->m_fDoSetExtent;

	/* write line record */
	hrErr = lpLLStm->lpVtbl->Write(
			lpLLStm,
			(LPVOID)&objLineRecord,
			sizeof(CONTAINERLINERECORD),
			&nWritten
	);

	if (hrErr != NOERROR) {
		OleDbgAssertSz(hrErr == NOERROR,"Could not write to LineList stream");
		return FALSE;
	}

	return TRUE;
}


/* ContainerLine_SaveOleObjectToStg
** --------------------------------
**    Save the OLE object associated with the ContainerLine to an IStorage*.
*/
BOOL ContainerLine_SaveOleObjectToStg(
		LPCONTAINERLINE         lpContainerLine,
		LPSTORAGE               lpSrcStg,
		LPSTORAGE               lpDestStg,
		BOOL                    fRemember
)
{
	HRESULT         hrErr;
	SCODE           sc = S_OK;
	BOOL            fStatus;
	BOOL            fSameAsLoad = (lpSrcStg==lpDestStg ? TRUE : FALSE);
	LPSTORAGE       lpObjDestStg;

	if (lpContainerLine->m_fGuardObj) {
		// object in process of creation--Fail to save
		return FALSE;
	}

	if (! lpContainerLine->m_lpOleObj) {

		/*****************************************************************
		** CASE 1: object is NOT loaded.
		*****************************************************************/

		if (fSameAsLoad) {
			/*************************************************************
			** CASE 1A: we are saving to the current storage. because
			**    the object is not loaded, it is up-to-date
			**    (ie. nothing to do).
			*************************************************************/

			;

		} else {
			/*************************************************************
			** CASE 1B: we are saving to a new storage. because
			**    the object is not loaded, we can simply copy the
			**    object's current storage to the new storage.
			*************************************************************/

			/* if current object storage is not already open, then open it */
			if (! lpContainerLine->m_lpStg) {
				lpContainerLine->m_lpStg = OleStdOpenChildStorage(
						lpSrcStg,
						lpContainerLine->m_szStgName,
						STGM_READWRITE
					);
				if (lpContainerLine->m_lpStg == NULL) {
#if defined( _DEBUG )
					OleDbgAssertSz(
							lpContainerLine->m_lpStg != NULL,
							"Error opening child stg"
					);
#endif
					return FALSE;
				}
			}

			/* Create a child storage inside the destination storage. */
			lpObjDestStg = OleStdCreateChildStorage(
					lpDestStg,
					lpContainerLine->m_szStgName
			);

			if (lpObjDestStg == NULL) {
#if defined( _DEBUG )
				OleDbgAssertSz(
						lpObjDestStg != NULL,
						"Could not create obj storage!"
				);
#endif
				return FALSE;
			}

			hrErr = lpContainerLine->m_lpStg->lpVtbl->CopyTo(
					lpContainerLine->m_lpStg,
					0,
					NULL,
					NULL,
					lpObjDestStg
			);
			// REVIEW: should we handle error here?
			fStatus = OleStdCommitStorage(lpObjDestStg);

			/* if we are supposed to remember this storage as the new
			**    storage for the object, then release the old one and
			**    save the new one. else, throw away the new one.
			*/
			if (fRemember) {
				OleStdVerifyRelease(
						(LPUNKNOWN)lpContainerLine->m_lpStg,
						"Original object stg not released"
				);
				lpContainerLine->m_lpStg = lpObjDestStg;
			} else {
				OleStdVerifyRelease(
						(LPUNKNOWN)lpObjDestStg,
						"Copied object stg not released"
				);
			}
		}

	} else {

		/*****************************************************************
		** CASE 2: object IS loaded.
		*****************************************************************/

		if (fSameAsLoad) {
			/*************************************************************
			** CASE 2A: we are saving to the current storage. if the object
			**    is not dirty, then the current storage is up-to-date
			**    (ie. nothing to do).
			*************************************************************/

			LPPERSISTSTORAGE lpPersistStg = lpContainerLine->m_lpPersistStg;
			OleDbgAssert(lpPersistStg);

			hrErr = lpPersistStg->lpVtbl->IsDirty(lpPersistStg);

			/* OLE2NOTE: we will only accept an explicit "no i
			**    am NOT dirty statement" (ie. S_FALSE) as an
			**    indication that the object is clean. eg. if
			**    the object returns E_NOTIMPL we must
			**    interpret it as the object IS dirty.
			*/
			if (GetScode(hrErr) != S_FALSE) {

				/* OLE object IS dirty */

				OLEDBG_BEGIN2("OleSave called\r\n")
				hrErr = OleSave(
						lpPersistStg, lpContainerLine->m_lpStg, fSameAsLoad);
				OLEDBG_END2

				if (hrErr != NOERROR) {
					OleDbgOutHResult("WARNING: OleSave returned", hrErr);
					sc = GetScode(hrErr);
				}

				// OLE2NOTE: if OleSave fails, SaveCompleted must be called.
				OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n")
				hrErr=lpPersistStg->lpVtbl->SaveCompleted(lpPersistStg,NULL);
				OLEDBG_END2

				if (hrErr != NOERROR) {
					OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr);
					if (sc == S_OK)
						sc = GetScode(hrErr);
				}

				if (sc != S_OK)
					return FALSE;
			}

		} else {
			/*************************************************************
			** CASE 2B: we are saving to a new storage. we must
			**    tell the object to save into the new storage.
			*************************************************************/

			LPPERSISTSTORAGE lpPersistStg = lpContainerLine->m_lpPersistStg;

			if (! lpPersistStg) return FALSE;

			/* Create a child storage inside the destination storage. */
			lpObjDestStg = OleStdCreateChildStorage(
					lpDestStg,
					lpContainerLine->m_szStgName
			);

			if (lpObjDestStg == NULL) {
#if defined( _DEBUG )
				OleDbgAssertSz(
						lpObjDestStg != NULL,
						"Could not create object storage!"
				);
#endif
				return FALSE;
			}

			OLEDBG_BEGIN2("OleSave called\r\n")
			hrErr = OleSave(lpPersistStg, lpObjDestStg, fSameAsLoad);
			OLEDBG_END2

			// OLE2NOTE: even if OleSave fails, must still call SaveCompleted
			if (hrErr != NOERROR) {
				OleDbgOutHResult("WARNING: OleSave returned", hrErr);
				sc = GetScode(hrErr);
			}

			/* OLE2NOTE: a root level container should immediately
			**    call IPersistStorage::SaveCompleted after calling
			**    OleSave. a nested level container should not call
			**    SaveCompleted now, but must wait until SaveCompleted
			**    is call on it by its container. since our container
			**    is not a container/server, then we always call
			**    SaveComplete here.
			**
			**    if this is a SaveAs operation, then we need to pass
			**    the lpStg back in SaveCompleted to inform the object
			**    of its new storage that it may hold on to. if this is
			**    a Save or a SaveCopyAs operation, then we simply pass
			**    NULL in SaveCompleted; the object can continue to hold
			**    its current storage. if an error occurs during the
			**    OleSave call we must still call SaveCompleted but we
			**    must pass NULL.
			*/
			OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n")
			hrErr = lpPersistStg->lpVtbl->SaveCompleted(
					lpPersistStg,
					((FAILED(sc) || !fRemember) ? NULL : lpObjDestStg)
			);
			OLEDBG_END2

			if (hrErr != NOERROR) {
				OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr);
				if (sc == S_OK)
					sc = GetScode(hrErr);
			}

			if (sc != S_OK) {
				OleStdVerifyRelease(
						(LPUNKNOWN)lpObjDestStg,
						"Copied object stg not released"
				);
				return FALSE;
			}

			/* if we are supposed to remember this storage as the new
			**    storage for the object, then release the old one and
			**    save the new one. else, throw away the new one.
			*/
			if (fRemember) {
				OleStdVerifyRelease(
						(LPUNKNOWN)lpContainerLine->m_lpStg,
						"Original object stg not released"
				);
				lpContainerLine->m_lpStg = lpObjDestStg;
			} else {
				OleStdVerifyRelease(
						(LPUNKNOWN)lpObjDestStg,
						"Copied object stg not released"
				);
			}
		}
	}

	/* OLE2NOTE: after saving an OLE object it is possible that it sent
	**    an OnViewChange notification because it had been modified. in
	**    this situation it is possible that the extents of the object
	**    have changed. if so we want to relayout the space for the
	**    object immediately so that the extent information saved with
	**    the ContainerLine match the data saved with the OLE object
	**    itself.
	*/
	if (lpContainerLine->m_fDoGetExtent) {
		BOOL fSizeChanged = ContainerLine_UpdateExtent(lpContainerLine, NULL);
#if defined( INPLACE_CNTR )
		/* if the extents of this ContainerLine have changed, then we
		**    need to reset the fDoGetExtent flag to TRUE so that later
		**    when ContainerDoc_UpdateExtentOfAllOleObjects is called
		**    (when the WM_U_UPDATEOBJECTEXTENT message is processed),
		**    it is recognized that the extents of this line have
		**    changed. if any line changes size, then any in-place
		**    active object below this line must be told to update the
		**    position of their windows (via SetObjectRects -- see
		**    ContainerDoc_UpdateInPlaceObjectRects function).
		*/
		lpContainerLine->m_fDoGetExtent = fSizeChanged;
#endif
	}

	return TRUE;
}


/* ContainerLine_LoadFromStg
** -------------------------
**    Create a ContainerLine object and initialize it with data that
**    was previously writen to an IStorage*. this function does not
**    immediately OleLoad the associated OLE object, only the data of
**    the ContainerLine object itself is loaded from the IStorage*.
*/
LPLINE ContainerLine_LoadFromStg(
		LPSTORAGE               lpSrcStg,
		LPSTREAM                lpLLStm,
		LPOUTLINEDOC            lpDestDoc
)
{
	HDC         hDC;
	LPLINELIST  lpDestLL = &lpDestDoc->m_LineList;
	ULONG nRead;
	HRESULT hrErr;
	LPCONTAINERLINE lpContainerLine;
	CONTAINERLINERECORD objLineRecord;

	lpContainerLine=(LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE));
	if (lpContainerLine == NULL) {
		OleDbgAssertSz(lpContainerLine!=NULL,"Error allocating ContainerLine");
		return NULL;
	}

	hDC = LineList_GetDC(lpDestLL);
	ContainerLine_Init(lpContainerLine, 0, hDC);
	LineList_ReleaseDC(lpDestLL, hDC);

	/* OLE2NOTE: In order to have a stable ContainerLine object we must
	**    AddRef the object's refcnt. this will be later released when
	**    the ContainerLine is deleted.
	*/
	ContainerLine_AddRef(lpContainerLine);

	lpContainerLine->m_lpDoc = (LPCONTAINERDOC) lpDestDoc;

	/* read line record */
	hrErr = lpLLStm->lpVtbl->Read(
			lpLLStm,
			(LPVOID)&objLineRecord,
			sizeof(CONTAINERLINERECORD),
			&nRead
	);

	if (hrErr != NOERROR) {
		OleDbgAssertSz(hrErr==NOERROR, "Could not read from LineList stream");
		goto error;
	}

	lstrcpy(lpContainerLine->m_szStgName, objLineRecord.m_szStgName);
	lpContainerLine->m_fMonikerAssigned = objLineRecord.m_fMonikerAssigned;
	lpContainerLine->m_dwDrawAspect = objLineRecord.m_dwDrawAspect;
	lpContainerLine->m_sizeInHimetric = objLineRecord.m_sizeInHimetric;
	lpContainerLine->m_dwLinkType = objLineRecord.m_dwLinkType;
	lpContainerLine->m_fDoSetExtent = objLineRecord.m_fDoSetExtent;

	return (LPLINE)lpContainerLine;

error:
	// destroy partially created ContainerLine
	if (lpContainerLine)
		ContainerLine_Delete(lpContainerLine);
	return NULL;
}


/* ContainerLine_GetTextLen
 * ------------------------
 *
 * Return length of the string representation of the ContainerLine
 *  (not considering the tab level). we will use the following as the
 *  string representation of a ContainerLine:
 *      "<" + user type name of OLE object + ">"
 *  eg:
 *      <Microsoft Excel Worksheet>
 */
int ContainerLine_GetTextLen(LPCONTAINERLINE lpContainerLine)
{
	LPSTR   lpszUserType = NULL;
	HRESULT hrErr;
	int     nLen;
	BOOL    fIsLink = ContainerLine_IsOleLink(lpContainerLine);

	/* if object is not already loaded, then load it now. objects are
	**    loaded lazily in this manner.
	*/
	if (! lpContainerLine->m_lpOleObj)
		ContainerLine_LoadOleObject(lpContainerLine);

	OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")

	{
	LPOLESTR polestr;

	hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserType(
			lpContainerLine->m_lpOleObj,
			USERCLASSTYPE_FULL,
			&polestr
	);

	CopyAndFreeOLESTR(polestr, &lpszUserType);
	}

	OLEDBG_END2

	if (hrErr != NOERROR)   {
		// user type is NOT available
		nLen = sizeof(UNKNOWN_OLEOBJ_TYPE) + 2; // allow space for '<' + '>'
		nLen += lstrlen((LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT)) + 1;
	} else {
		nLen = lstrlen(lpszUserType) + 2;   // allow space for '<' + '>'
		nLen += lstrlen((LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT)) + 1;

		/* OLE2NOTE: we must free the string that was allocated by the
		**    IOleObject::GetUserType method.
		*/
		OleStdFreeString(lpszUserType, NULL);
	}

	return nLen;
}


/* ContainerLine_GetTextData
 * -------------------------
 *
 * Return the string representation of the ContainerLine
 *  (not considering the tab level). we will use the following as the
 *  string representation of a ContainerLine:
 *      "<" + user type name of OLE object + ">"
 *  eg:
 *      <Microsoft Excel Worksheet>
 */
void ContainerLine_GetTextData(LPCONTAINERLINE lpContainerLine, LPSTR lpszBuf)
{
	LPSTR   lpszUserType = NULL;
	BOOL    fIsLink = ContainerLine_IsOleLink(lpContainerLine);
	HRESULT hrErr;
	LPOLESTR polestr;

	/* if object is not already loaded, then load it now. objects are
	**    loaded lazily in this manner.
	*/
	if (! lpContainerLine->m_lpOleObj)
		ContainerLine_LoadOleObject(lpContainerLine);

	{
	LPOLESTR polestr;

	hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetUserType(
			lpContainerLine->m_lpOleObj,
			USERCLASSTYPE_FULL,
			&lpszUserType
	);

	CopyAndFree(polestr, &lpszUserType);
	}

	// Convert output to mbs
	CopyAndFree(polestr, &lpszUserType);

	if (hrErr != NOERROR)   {
		// user type is NOT available
		wsprintf(
				lpszBuf,
				"<%s %s>",
				UNKNOWN_OLEOBJ_TYPE,
				(LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT)
		);
	} else {
		wsprintf(
				lpszBuf,
				"<%s %s>",
				lpszUserType,
				(LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT)
		);

		/* OLE2NOTE: we must free the string that was allocated by the
		**    IOleObject::GetUserType method.
		*/
		OleStdFreeString(lpszUserType, NULL);
	}
}


/* ContainerLine_GetOutlineData
 * ----------------------------
 *
 * Return the CF_OUTLINE format data for the ContainerLine.
 */
BOOL ContainerLine_GetOutlineData(
		LPCONTAINERLINE         lpContainerLine,
		LPTEXTLINE              lpBuf
)
{
	LPLINE      lpLine = (LPLINE)lpContainerLine;
	LPLINELIST  lpLL = &((LPOUTLINEDOC)lpContainerLine->m_lpDoc)->m_LineList;
	HDC         hDC;
	char        szTmpBuf[MAXSTRLEN+1];
	LPTEXTLINE  lpTmpTextLine;

	// Create a TextLine with the Text representation of the ContainerLine.
	ContainerLine_GetTextData(lpContainerLine, (LPSTR)szTmpBuf);

	hDC = LineList_GetDC(lpLL);
	lpTmpTextLine = TextLine_Create(hDC, lpLine->m_nTabLevel, szTmpBuf);
	LineList_ReleaseDC(lpLL, hDC);

	TextLine_Copy(lpTmpTextLine, lpBuf);

	// Delete the temporary TextLine
	TextLine_Delete(lpTmpTextLine);
	return TRUE;
}


/* ContainerLine_GetPosRect
** -----------------------
**    Get the PosRect in client coordinates for the OLE object's window.
**
** OLE2NOTE: the PosRect must take into account the scroll postion of
**    the document window.
*/
void ContainerLine_GetPosRect(
		LPCONTAINERLINE     lpContainerLine,
		LPRECT              lprcPosRect
)
{
	ContainerLine_GetOleObjectRectInPixels(lpContainerLine,lprcPosRect);

	// shift rect for left margin
	lprcPosRect->left += lpContainerLine->m_nHorizScrollShift;
	lprcPosRect->right += lpContainerLine->m_nHorizScrollShift;
}


/* ContainerLine_GetOleObjectRectInPixels
** --------------------------------------
**    Get the extent of the OLE Object contained in the given Line in
**    client coordinates after scaling.
*/
void ContainerLine_GetOleObjectRectInPixels(LPCONTAINERLINE lpContainerLine, LPRECT lprc)
{
	LPOUTLINEDOC lpOutlineDoc;
	LPSCALEFACTOR lpscale;
	LPLINELIST lpLL;
	LPLINE lpLine;
	int nIndex;
	HDC hdcLL;

	if (!lpContainerLine || !lprc)
		return;

	lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
	lpscale = OutlineDoc_GetScaleFactor(lpOutlineDoc);
	lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
	lpLine = (LPLINE)lpContainerLine;
	nIndex = LineList_GetLineIndex(lpLL, lpLine);

	LineList_GetLineRect(lpLL, nIndex, lprc);

	hdcLL = GetDC(lpLL->m_hWndListBox);

	/* lprc is set to be size of Line Object (including the boundary) */
	lprc->left += (int)(
			(long)XformWidthInHimetricToPixels(hdcLL,
					lpLine->m_nTabWidthInHimetric +
					LOWORD(OutlineDoc_GetMargin(lpOutlineDoc))) *
			lpscale->dwSxN / lpscale->dwSxD);
	lprc->right = (int)(
			lprc->left + (long)
			XformWidthInHimetricToPixels(hdcLL, lpLine->m_nWidthInHimetric) *
			lpscale->dwSxN / lpscale->dwSxD);

	ReleaseDC(lpLL->m_hWndListBox, hdcLL);
}


/* ContainerLine_GetOleObjectSizeInHimetric
** ----------------------------------------
**    Get the size of the OLE Object contained in the given Line
*/
void ContainerLine_GetOleObjectSizeInHimetric(LPCONTAINERLINE lpContainerLine, LPSIZEL lpsizel)
{
	if (!lpContainerLine || !lpsizel)
		return;

	*lpsizel = lpContainerLine->m_sizeInHimetric;
}


/* ContainerLine_BindLinkIfLinkSrcIsRunning
** ----------------------------------------
**    Try to connect the OLE link object associated with the
**    ContainerLine with its LinkSource if the LinkSource is already
**    running and the link is an automatic link. we do not want to
**    force the LinkSource to run.
**
**    OLE2NOTE: a sophistocated container will want to continually
**    attempt to connect its automatic links. OLE does NOT
**    automatically connect links when link source become available. some
**    containers will want to attempt to connect its links as part of
**    idle time processing. another strategy is to attempt to connect
**    an automatic link every time it is drawn on the screen. (this is
**    the strategy used by this CntrOutl sample application.)
*/
void ContainerLine_BindLinkIfLinkSrcIsRunning(LPCONTAINERLINE lpContainerLine)
{
	LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
	HRESULT hrErr;
	BOOL fPrevEnable1;
	BOOL fPrevEnable2;

	// if the link source is known to be un-bindable, then don't even try
	if (lpContainerLine->m_fLinkUnavailable)
		return;

	/* OLE2NOTE: we do not want to ever give the Busy/NotResponding
	**    dialogs when we are attempting to BindIfRunning to the link
	**    source. if the link source is currently busy, this could
	**    cause the Busy dialog to come up. even if the link source is
	**    busy, we do not want put up the busy dialog. thus we will
	**    disable the dialog and later re-enable them
	*/
	OleApp_DisableBusyDialogs(lpOleApp, &fPrevEnable1, &fPrevEnable2);

	OLEDBG_BEGIN2("IOleLink::BindIfRunning called\r\n")
	hrErr = lpContainerLine->m_lpOleLink->lpVtbl->BindIfRunning(
			lpContainerLine->m_lpOleLink);
	OLEDBG_END2

	// re-enable the Busy/NotResponding dialogs
	OleApp_EnableBusyDialogs(lpOleApp, fPrevEnable1, fPrevEnable2);
}
