/*************************************************************************
**
**    OLE 2 Server Sample Code
**
**    oledoc.c
**
**    This file contains general OleDoc methods and related support
**    functions. OleDoc implementation is used by both the Container
**    versions and the Server (Object) versions of the Outline Sample.
**
**    This file includes general support for the following:
**    1. show/hide doc window
**    2. QueryInterface, AddRef, Release
**    3. document locking (calls CoLockObjectExternal)
**    4. document shutdown (Close, Destroy)
**    5. clipboard support
**
**    OleDoc Object
**      exposed interfaces:
**          IUnknown
**          IPersistFile
**          IOleItemContainer
**          IDataObject
**
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/


#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP             g_lpApp;

extern IUnknownVtbl             g_OleDoc_UnknownVtbl;
extern IPersistFileVtbl         g_OleDoc_PersistFileVtbl;
extern IOleItemContainerVtbl    g_OleDoc_OleItemContainerVtbl;
extern IExternalConnectionVtbl  g_OleDoc_ExternalConnectionVtbl;
extern IDataObjectVtbl          g_OleDoc_DataObjectVtbl;

#if defined( USE_DRAGDROP )
extern IDropTargetVtbl          g_OleDoc_DropTargetVtbl;
extern IDropSourceVtbl          g_OleDoc_DropSourceVtbl;
#endif  // USE_DRAGDROP

#if defined( INPLACE_CNTR )
extern BOOL g_fInsideOutContainer;
#endif


/* OleDoc_Init
 * -----------
 *
 *  Initialize the fields of a new OleDoc object. The object is initially
 *  not associated with a file or an (Untitled) document. This function sets
 *  the docInitType to DOCTYPE_UNKNOWN. After calling this function the
 *  caller should call:
 *      1.) Doc_InitNewFile to set the OleDoc to (Untitled)
 *      2.) Doc_LoadFromFile to associate the OleDoc with a file.
 *  This function creates a new window for the document.
 *
 *  NOTE: the window is initially created with a NIL size. it must be
 *        sized and positioned by the caller. also the document is initially
 *        created invisible. the caller must call OutlineDoc_ShowWindow
 *        after sizing it to make the document window visible.
 */
BOOL OleDoc_Init(LPOLEDOC lpOleDoc, BOOL fDataTransferDoc)
{
	LPOLEAPP   lpOleApp = (LPOLEAPP)g_lpApp;
	LPLINELIST lpLL     = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;

	lpOleDoc->m_cRef                        = 0;
	lpOleDoc->m_dwStrongExtConn             = 0;
#if defined( _DEBUG )
	lpOleDoc->m_cCntrLock                   = 0;
#endif
	lpOleDoc->m_lpStg                       = NULL;
	lpOleDoc->m_lpLLStm                     = NULL;
	lpOleDoc->m_lpNTStm                     = NULL;
	lpOleDoc->m_dwRegROT                    = 0;
	lpOleDoc->m_lpFileMoniker               = NULL;
	lpOleDoc->m_fLinkSourceAvail            = FALSE;
	lpOleDoc->m_lpSrcDocOfCopy              = NULL;
	lpOleDoc->m_fObjIsClosing               = FALSE;
	lpOleDoc->m_fObjIsDestroying            = FALSE;
	lpOleDoc->m_fUpdateEditMenu             = FALSE;

#if defined( USE_DRAGDROP )
	lpOleDoc->m_dwTimeEnterScrollArea       = 0L;
	lpOleDoc->m_dwNextScrollTime            = 0L;
	lpOleDoc->m_dwLastScrollDir             = SCROLLDIR_NULL;
	lpOleDoc->m_fRegDragDrop                = FALSE;
	lpOleDoc->m_fLocalDrag                  = FALSE;
	lpOleDoc->m_fCanDropCopy                = FALSE;
	lpOleDoc->m_fCanDropLink                = FALSE;
	lpOleDoc->m_fLocalDrop                  = FALSE;
	lpOleDoc->m_fDragLeave                  = FALSE;
	lpOleDoc->m_fPendingDrag                = FALSE;
#endif
#if defined( INPLACE_SVR ) || defined( INPLACE_CNTR )
	lpOleDoc->m_fCSHelpMode                 = FALSE;    // Shift-F1 context
														// sensitive help mode
#endif

	INIT_INTERFACEIMPL(
			&lpOleDoc->m_Unknown,
			&g_OleDoc_UnknownVtbl,
			lpOleDoc
	);

	INIT_INTERFACEIMPL(
			&lpOleDoc->m_PersistFile,
			&g_OleDoc_PersistFileVtbl,
			lpOleDoc
	);

	INIT_INTERFACEIMPL(
			&lpOleDoc->m_OleItemContainer,
			&g_OleDoc_OleItemContainerVtbl,
			lpOleDoc
	);

	INIT_INTERFACEIMPL(
			&lpOleDoc->m_ExternalConnection,
			&g_OleDoc_ExternalConnectionVtbl,
			lpOleDoc
	);

	INIT_INTERFACEIMPL(
			&lpOleDoc->m_DataObject,
			&g_OleDoc_DataObjectVtbl,
			lpOleDoc
	);

#if defined( USE_DRAGDROP )
	INIT_INTERFACEIMPL(
			&lpOleDoc->m_DropSource,
			&g_OleDoc_DropSourceVtbl,
			lpOleDoc
	);

	INIT_INTERFACEIMPL(
			&lpOleDoc->m_DropTarget,
			&g_OleDoc_DropTargetVtbl,
			lpOleDoc
	);
#endif  // USE_DRAGDROP

	/*
	** OLE2NOTE: each user level document addref's the app object in
	**    order to guarentee that the app does not shut down while the
	**    doc is still open.
	*/

	// OLE2NOTE: data transfer documents should not hold the app alive
	if (! fDataTransferDoc)
		OleApp_DocLockApp(lpOleApp);

#if defined( OLE_SERVER )
	/* OLE2NOTE: perform initialization specific for an OLE server */
	if (! ServerDoc_Init((LPSERVERDOC)lpOleDoc, fDataTransferDoc))
		return FALSE;
#endif
#if defined( OLE_CNTR )

	/* OLE2NOTE: perform initialization specific for an OLE container */
	if (! ContainerDoc_Init((LPCONTAINERDOC)lpOleDoc, fDataTransferDoc))
		return FALSE;
#endif

	return TRUE;
}



/* OleDoc_InitNewFile
 * ------------------
 *
 *  Initialize the document to be a new (Untitled) document.
 *  This function sets the docInitType to DOCTYPE_NEW.
 *
 *  OLE2NOTE: if this is a visible user document then generate a unique
 *  untitled name that we can use to register in the RunningObjectTable.
 *  We need a unique name so that clients can link to data in this document
 *  even when the document is in the un-saved (untitled) state. it would be
 *  ambiguous to register two documents titled "Outline1" in the ROT. we
 *  thus generate the lowest numbered document that is not already
 *  registered in the ROT.
 */
BOOL OleDoc_InitNewFile(LPOLEDOC lpOleDoc)
{
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;

	static UINT uUnique = 1;

	OleDbgAssert(lpOutlineDoc->m_docInitType == DOCTYPE_UNKNOWN);

#if defined( OLE_CNTR )
	{
		LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc;
#if defined( _DEBUG )
		OleDbgAssertSz(lpOleDoc->m_lpStg == NULL,
				"Setting to untitled with current file open"
		);
#endif

		/* Create a temp, (delete-on-release) file base storage
		**  for the untitled document.
		*/
		lpOleDoc->m_lpStg = OleStdCreateRootStorage(
				NULL,
				STGM_SHARE_EXCLUSIVE
		);
		if (! lpOleDoc->m_lpStg) return FALSE;
	}
#endif

	lpOutlineDoc->m_docInitType = DOCTYPE_NEW;

	if (! lpOutlineDoc->m_fDataTransferDoc) {
		/* OLE2NOTE: choose a unique name for a Moniker so that
		**    potential clients can link to our new, untitled document.
		**    if links are established (and currently are connected),
		**    then they will be notified that we have been renamed when
		**    this document is saved to a file.
		*/

		lpOleDoc->m_fLinkSourceAvail = TRUE;

		// REVIEW: should load UNTITLED string from string resource
		OleStdCreateTempFileMoniker(
				UNTITLED,
				(UINT FAR*)&uUnique,
				lpOutlineDoc->m_szFileName,
				&lpOleDoc->m_lpFileMoniker
		);

		OLEDBG_BEGIN3("OleStdRegisterAsRunning called\r\n")
		OleStdRegisterAsRunning(
				(LPUNKNOWN)&lpOleDoc->m_PersistFile,
				(LPMONIKER)lpOleDoc->m_lpFileMoniker,
				&lpOleDoc->m_dwRegROT
		);
		OLEDBG_END3

		lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName;
		OutlineDoc_SetTitle(lpOutlineDoc, FALSE /*fMakeUpperCase*/);
	} else {
		lstrcpy(lpOutlineDoc->m_szFileName, UNTITLED);
		lpOutlineDoc->m_lpszDocTitle = lpOutlineDoc->m_szFileName;
	}

	return TRUE;
}


/* OleDoc_ShowWindow
 * -----------------
 *
 *      Show the window of the document to the user.
 *      make sure app window is visible and bring the document to the top.
 *      if the document is a file-based document or a new untitled
 *      document, give the user the control over the life-time of the doc.
 */
void OleDoc_ShowWindow(LPOLEDOC lpOleDoc)
{
	LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
	LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
	LPLINELIST lpLL     = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
#if defined( OLE_SERVER )
	LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
#endif // OLE_SERVER

	OLEDBG_BEGIN3("OleDoc_ShowWindow\r\n")

	/* OLE2NOTE: while the document is visible, we do NOT want it to be
	**    prematurely destroyed when a linking client disconnects. thus
	**    we must inform OLE to hold an external lock on our document.
	**    this arranges that OLE holds at least 1 reference to our
	**    document that will NOT be released until we release this
	**    external lock. later, when the document window is hidden, we
	**    will release this external lock.
	*/
	if (! IsWindowVisible(lpOutlineDoc->m_hWndDoc))
		OleDoc_Lock(lpOleDoc, TRUE /* fLock */, 0 /* not applicable */);

#if defined( USE_DRAGDROP )
	/* OLE2NOTE: since our window is now being made visible, we will
	**    register our window as a potential drop target. when the
	**    window is hidden there is no reason to be registered as a
	**    drop target.
	*/
	if (! lpOleDoc->m_fRegDragDrop) {
		OLEDBG_BEGIN2("RegisterDragDrop called\r\n")
		RegisterDragDrop(
				LineList_GetWindow(lpLL),
				(LPDROPTARGET)&lpOleDoc->m_DropTarget
		);
		OLEDBG_END2
		lpOleDoc->m_fRegDragDrop = TRUE;
	}
#endif  // USE_DRAGDROP

#if defined( USE_FRAMETOOLS )
	{
		/* OLE2NOTE: we need to enable our frame level tools
		*/
		FrameTools_Enable(lpOutlineDoc->m_lpFrameTools, TRUE);
	}
#endif // USE_FRAMETOOLS

#if defined( OLE_SERVER )

	if (lpOutlineDoc->m_docInitType == DOCTYPE_EMBEDDED &&
			lpServerDoc->m_lpOleClientSite != NULL) {

		/* OLE2NOTE: we must also ask our container to show itself if
		**    it is not already visible and to scroll us into view. we
		**    must make sure to call this BEFORE showing our server's
		**    window and taking focus. we do not want our container's
		**    window to end up on top.
		*/
		OLEDBG_BEGIN2("IOleClientSite::ShowObject called\r\n");
		lpServerDoc->m_lpOleClientSite->lpVtbl->ShowObject(
				lpServerDoc->m_lpOleClientSite
		);
		OLEDBG_END2

		/* OLE2NOTE: if we are an embedded object and we are not
		**    in-place active in our containers window, we must inform our
		**    embedding container that our window is opening.
		**    the container must now hatch our object.
		*/

#if defined( INPLACE_SVR )
		if (! lpServerDoc->m_fInPlaceActive)
#endif
		{
			OLEDBG_BEGIN2("IOleClientSite::OnShowWindow(TRUE) called\r\n");
			lpServerDoc->m_lpOleClientSite->lpVtbl->OnShowWindow(
					lpServerDoc->m_lpOleClientSite,
					TRUE
			);
			OLEDBG_END2
		}

		/* OLE2NOTE: the life-time of our document is controlled by our
		**    client and NOT by the user. we are not an independent
		**    file-level object. we simply want to show our window here.
		**
		**    if we are not in-place active (ie. we are opening
		**    our own window), we must make sure our main app window is
		**    visible. we do not, however, want to give the user
		**    control of the App window; we do not want OleApp_ShowWindow
		**    to call OleApp_Lock on behalf of the user.
		*/
		if (! IsWindowVisible(lpOutlineApp->m_hWndApp) ||
				IsIconic(lpOutlineApp->m_hWndApp)) {
#if defined( INPLACE_SVR )
			if (! ((LPSERVERDOC)lpOleDoc)->m_fInPlaceActive)
#endif
				OleApp_ShowWindow(lpOleApp, FALSE /* fGiveUserCtrl */);
			SetFocus(lpOutlineDoc->m_hWndDoc);
		}

	} else
#endif  // OLE_SERVER

	{    // DOCTYPE_NEW || DOCTYPE_FROMFILE

		// we must make sure our app window is visible
		OleApp_ShowWindow(lpOleApp, TRUE /* fGiveUserCtrl */);
	}

	// make document window visible and make sure it is not minimized
	ShowWindow(lpOutlineDoc->m_hWndDoc, SW_SHOWNORMAL);
	SetFocus(lpOutlineDoc->m_hWndDoc);

	OLEDBG_END3
}


/* OleDoc_HideWindow
 * -----------------
 *
 *      Hide the window of the document from the user.
 *      take away the control of the document by the user.
 */
void OleDoc_HideWindow(LPOLEDOC lpOleDoc, BOOL fShutdown)
{
	LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
	LPLINELIST lpLL     = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;

	if (! IsWindowVisible(lpOutlineDoc->m_hWndDoc))
		return;     // already visible

	OLEDBG_BEGIN3("OleDoc_HideWindow\r\n")

#if defined( USE_DRAGDROP )
	// The document's window is being hidden, revoke it as a DropTarget
	if (lpOleDoc->m_fRegDragDrop) {
		OLEDBG_BEGIN2("RevokeDragDrop called\r\n");
		RevokeDragDrop(LineList_GetWindow(lpLL));
		OLEDBG_END2

		lpOleDoc->m_fRegDragDrop = FALSE ;
	}
#endif  // USE_DRAGDROP

	/* OLE2NOTE: the document is now being hidden, so we must release
	**    the external lock made when the document was made visible.
	**    if this is a shutdown situation (fShutdown==TRUE), then OLE
	**    is instructed to release our document. if this is that last
	**    external lock on our document, thus enabling our document to
	**    complete its shutdown operation. If This is not a shutdown
	**    situation (eg. in-place server hiding its window when
	**    UIDeactivating or IOleObject::DoVerb(OLEVERB_HIDE) is called),
	**    then OLE is told to NOT immediately release the document.
	**    this leaves the document in an unstable state where the next
	**    Lock/Unlock sequence will shut the document down (eg. a
	**    linking client connecting and disconnecting).
	*/
	if (IsWindowVisible(lpOutlineDoc->m_hWndDoc))
		OleDoc_Lock(lpOleDoc, FALSE /* fLock */, fShutdown);

	ShowWindow(((LPOUTLINEDOC)lpOleDoc)->m_hWndDoc, SW_HIDE);

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

		/* OLE2NOTE: if we are an embedded object and we are not
		**    in-place active, we must inform our
		**    embedding container that our window is hiding (closing
		**    from the user's perspective). the container must now
		**    un-hatch our object.
		*/
		if (lpServerDoc->m_lpOleClientSite != NULL
#if defined( INPLACE_SVR )
			&& !lpServerDoc->m_fInPlaceVisible
#endif
		) {
			OLEDBG_BEGIN2("IOleClientSite::OnShowWindow(FALSE) called\r\n");
			lpServerDoc->m_lpOleClientSite->lpVtbl->OnShowWindow(
					lpServerDoc->m_lpOleClientSite,
					FALSE
			);
			OLEDBG_END2
		}
	}
#endif

	/* OLE2NOTE: if there are no more documents visible to the user.
	**    and the app itself is not under user control, then
	**    it has no reason to stay visible. we thus should hide the
	**    app. we can not directly destroy the app, because it may be
	**    validly being used programatically by another client
	**    application and should remain running. it should simply be
	**    hidded from the user.
	*/
	OleApp_HideIfNoReasonToStayVisible(lpOleApp);
	OLEDBG_END3
}


/* OleDoc_Lock
** -----------
**    Lock/Unlock the Doc object. if the last lock is unlocked and
**    fLastUnlockReleases == TRUE, then the Doc object will shut down
**    (ie. it will recieve its final release and its refcnt will go to 0).
*/
HRESULT OleDoc_Lock(LPOLEDOC lpOleDoc, BOOL fLock, BOOL fLastUnlockReleases)
{
	HRESULT hrErr;

#if defined( _DEBUG )
	if (fLock) {
		OLEDBG_BEGIN2("CoLockObjectExternal(lpDoc,TRUE) called\r\n")
	} else {
		if (fLastUnlockReleases)
			OLEDBG_BEGIN2("CoLockObjectExternal(lpDoc,FALSE,TRUE) called\r\n")
		else
			OLEDBG_BEGIN2("CoLockObjectExternal(lpDoc,FALSE,FALSE) called\r\n")
	}
#endif  // _DEBUG

	hrErr = CoLockObjectExternal(
			(LPUNKNOWN)&lpOleDoc->m_Unknown, fLock, fLastUnlockReleases);

	OLEDBG_END2
	return hrErr;
}


/* OleDoc_AddRef
** -------------
**
**  increment the ref count of the document object.
**
**    Returns the new ref count on the object
*/
ULONG OleDoc_AddRef(LPOLEDOC lpOleDoc)
{
	++lpOleDoc->m_cRef;

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


/* OleDoc_Release
** --------------
**
**  decrement the ref count of the document object.
**    if the ref count goes to 0, then the document is destroyed.
**
**    Returns the remaining ref count on the object
*/
ULONG OleDoc_Release (LPOLEDOC lpOleDoc)
{
	ULONG cRef;
	LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
	LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;

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

	cRef = --lpOleDoc->m_cRef;

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

	OleDbgOutRefCnt4(
			"OleDoc_Release: cRef--\r\n", lpOleDoc, cRef);
#endif
	if (cRef == 0)
		OutlineDoc_Destroy((LPOUTLINEDOC)lpOleDoc);

	return cRef;
}


/* OleDoc_QueryInterface
** ---------------------
**
** Retrieve a pointer to an interface on the document object.
**
**    OLE2NOTE: this function will AddRef the ref cnt of the object.
**
**    Returns S_OK if interface is successfully retrieved.
**            E_NOINTERFACE if the interface is not supported
*/
HRESULT OleDoc_QueryInterface(
		LPOLEDOC          lpOleDoc,
		REFIID            riid,
		LPVOID FAR*       lplpvObj
)
{
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;
	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("OleDoc_QueryInterface: IUnknown* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_Unknown;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
	else if(lpOutlineDoc->m_fDataTransferDoc
			&& IsEqualIID(riid, &IID_IDataObject)) {
		OleDbgOut4("OleDoc_QueryInterface: IDataObject* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_DataObject;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}

	/* OLE2NOTE: if this document is a DataTransferDocument used to
	**    support a clipboard or drag/drop operation, then it should
	**    only expose IUnknown, IDataObject, and IDropSource
	**    interfaces. if the document is a normal user document, then
	**    we will also continue to consider our other interfaces.
	*/
	if (lpOutlineDoc->m_fDataTransferDoc)
		goto done;

	if(IsEqualIID(riid,&IID_IPersist) || IsEqualIID(riid,&IID_IPersistFile)) {
		OleDbgOut4("OleDoc_QueryInterface: IPersistFile* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_PersistFile;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
	else if(IsEqualIID(riid, &IID_IOleItemContainer) ||
			IsEqualIID(riid, &IID_IOleContainer) ||
			IsEqualIID(riid, &IID_IParseDisplayName) ) {
		OleDbgOut4("OleDoc_QueryInterface: IOleItemContainer* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_OleItemContainer;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
	else if(IsEqualIID(riid, &IID_IExternalConnection)) {
		OleDbgOut4("OleDoc_QueryInterface: IExternalConnection* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_ExternalConnection;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}

#if defined( USE_DRAGDROP )
	else if(IsEqualIID(riid, &IID_IDropTarget)) {
		OleDbgOut4("OleDoc_QueryInterface: IDropTarget* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_DropTarget;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
	else if(IsEqualIID(riid, &IID_IDropSource)) {
		OleDbgOut4("OleDoc_QueryInterface: IDropSource* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_DropSource;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
#endif

#if defined( OLE_CNTR )
	else if (IsEqualIID(riid, &IID_IOleUILinkContainer)) {
		OleDbgOut4("OleDoc_QueryInterface: IOleUILinkContainer* RETURNED\r\n");

		*lplpvObj=(LPVOID)&((LPCONTAINERDOC)lpOleDoc)->m_OleUILinkContainer;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
#endif

#if defined( OLE_SERVER )

	/* OLE2NOTE: if OLE server version, than also offer the server
	**    specific interfaces: IOleObject and IPersistStorage.
	*/
	else if (IsEqualIID(riid, &IID_IOleObject)) {
		OleDbgOut4("OleDoc_QueryInterface: IOleObject* RETURNED\r\n");

		*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_OleObject;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
	else if(IsEqualIID(riid, &IID_IPersistStorage)) {
		OleDbgOut4("OleDoc_QueryInterface: IPersistStorage* RETURNED\r\n");

		*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_PersistStorage;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
	else if(IsEqualIID(riid, &IID_IDataObject)) {
		OleDbgOut4("OleDoc_QueryInterface: IDataObject* RETURNED\r\n");

		*lplpvObj = (LPVOID) &lpOleDoc->m_DataObject;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}

#if defined( SVR_TREATAS )
	else if(IsEqualIID(riid, &IID_IStdMarshalInfo)) {
		OleDbgOut4("OleDoc_QueryInterface: IStdMarshalInfo* RETURNED\r\n");

		*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_StdMarshalInfo;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
#endif  // SVR_TREATAS

#if defined( INPLACE_SVR )
	else if (IsEqualIID(riid, &IID_IOleWindow) ||
			 IsEqualIID(riid, &IID_IOleInPlaceObject)) {
		OleDbgOut4("OleDoc_QueryInterface: IOleInPlaceObject* RETURNED\r\n");

		*lplpvObj = (LPVOID) &((LPSERVERDOC)lpOleDoc)->m_OleInPlaceObject;
		OleDoc_AddRef(lpOleDoc);
		sc = S_OK;
	}
#endif // INPLACE_SVR
#endif // OLE_SERVER

done:
	OleDbgQueryInterfaceMethod(*lplpvObj);

	return ResultFromScode(sc);
}


/* OleDoc_Close
 * ------------
 *
 *  Close the document.
 *      This functions performs the actions that are in common to all
 *      document types which derive from OleDoc (eg. ContainerDoc and
 *      ServerDoc) which are required to close a document.
 *
 *  Returns:
 *      FALSE -- user canceled the closing of the doc.
 *      TRUE -- the doc was successfully closed
 */

BOOL OleDoc_Close(LPOLEDOC lpOleDoc, DWORD dwSaveOption)
{
	LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
	LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
	LPOLEDOC lpClipboardDoc;
	LPLINELIST lpLL     = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList;
	BOOL fAbortIfSaveCanceled = (dwSaveOption == OLECLOSE_PROMPTSAVE);

	if (! lpOleDoc)
		return TRUE;    // active doc's are already destroyed

	if (lpOleDoc->m_fObjIsClosing)
		return TRUE;    // Closing is already in progress

	OLEDBG_BEGIN3("OleDoc_Close\r\n")

	if (! OutlineDoc_CheckSaveChanges((LPOUTLINEDOC)lpOleDoc,&dwSaveOption)
			&& fAbortIfSaveCanceled) {
		OLEDBG_END3
		return FALSE;           // cancel closing the doc
	}

	lpOleDoc->m_fObjIsClosing = TRUE;   // guard against recursive call

	/* OLE2NOTE: in order to have a stable app and doc during the
	**    process of closing, we intially AddRef the App and Doc ref
	**    cnts and later Release them. These initial AddRefs are
	**    artificial; they simply guarantee that these objects do not
	**    get destroyed until the end of this routine.
	*/
	OleApp_AddRef(lpOleApp);
	OleDoc_AddRef(lpOleDoc);

#if defined( OLE_CNTR )
	{
		LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc;

		/* OLE2NOTE: force all OLE objects to close. this forces all
		**    OLE object to transition from running to loaded. we can
		**    NOT exit if any embeddings are still running.
		**    if an object can't be closed and this close operation was
		**    started by the user, then we will abort closing our document.
		*/
		if (! ContainerDoc_CloseAllOleObjects(lpContainerDoc, OLECLOSE_NOSAVE)
				&& fAbortIfSaveCanceled) {
			OleDoc_Release(lpOleDoc);       // release artificial AddRef above
			OleApp_Release(lpOleApp);       // release artificial AddRef above
			lpOleDoc->m_fObjIsClosing = FALSE; // clear recursion guard

			OLEDBG_END3
			return FALSE;   // Closing is aborted
		}
	}
#endif

#if defined( INPLACE_SVR )
	/* OLE2NOTE: if the server is currently in-place active we must
	**    deactivate it now before closing
	*/
	ServerDoc_DoInPlaceDeactivate((LPSERVERDOC)lpOleDoc);
#endif

	/* OLE2NOTE: if this document is the source of data for the
	**    clipboard, then flush the clipboard. it is important to flush
	**    the clipboard BEFORE calling sending any notifications to
	**    clients (eg. IOleClientSite::OnShowWindow(FALSE)) which could
	**    give them a chance to run and try to get our clipboard data
	**    object that we want to destroy. (eg. our app tries to
	**    update the paste button of the toolbar when
	**    WM_ACTIVATEAPP is received.)
	*/
	lpClipboardDoc = (LPOLEDOC)lpOutlineApp->m_lpClipboardDoc;
	if (lpClipboardDoc &&
		lpClipboardDoc->m_lpSrcDocOfCopy == lpOleDoc) {
		OleApp_FlushClipboard(lpOleApp);
	}

	/* OLE2NOTE: Revoke the object from the Running Object Table. it is
	**    best if the object is revoke prior to calling
	**    COLockObjectExternal(FALSE,TRUE) which is called when the
	**    document window is hidden from the user.
	*/
	OLEDBG_BEGIN3("OleStdRevokeAsRunning called\r\n")
	OleStdRevokeAsRunning(&lpOleDoc->m_dwRegROT);
	OLEDBG_END3

	/* OLE2NOTE: if the user is in control of the document, the user
	**    accounts for one refcnt on the document. Closing the
	**    document is achieved by releasing the object on behalf of
	**    the user. if the document is not referenced by any other
	**    clients, then the document will also be destroyed. if it
	**    is referenced by other clients, then it will remain until
	**    they release it. it is important to hide the window and call
	**    IOleClientSite::OnShowWindow(FALSE) BEFORE sending OnClose
	**    notification.
	*/
	OleDoc_HideWindow(lpOleDoc, TRUE);

#if defined( OLE_SERVER )
	{
		LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
		LPSERVERNAMETABLE lpServerNameTable =
			(LPSERVERNAMETABLE)((LPOUTLINEDOC)lpOleDoc)->m_lpNameTable;

		/* OLE2NOTE: force all pseudo objects to close. this informs all
		**    linking clients of pseudo objects to release their PseudoObj.
		*/
		ServerNameTable_CloseAllPseudoObjs(lpServerNameTable);

		/* OLE2NOTE: send last OnDataChange notification to clients
		**    that have registered for data notifications when object
		**    stops running (ADVF_DATAONSTOP), if the data in our
		**    object has ever changed. it is best to only send this
		**    notification if necessary.
		*/
		if (lpServerDoc->m_lpDataAdviseHldr) {
			if (lpServerDoc->m_fSendDataOnStop) {
				ServerDoc_SendAdvise(
						(LPSERVERDOC)lpOleDoc,
						OLE_ONDATACHANGE,
						NULL,   /* lpmkDoc -- not relevant here */
						ADVF_DATAONSTOP
				);
			}
			/* OLE2NOTE: we just sent the last data notification that we
			**    need to send; release our DataAdviseHolder. we SHOULD be
			**    the only one using it.
			*/

			OleStdVerifyRelease(
					(LPUNKNOWN)lpServerDoc->m_lpDataAdviseHldr,
					"DataAdviseHldr not released properly"
			);
			lpServerDoc->m_lpDataAdviseHldr = NULL;
		}

		// OLE2NOTE: inform all of our linking clients that we are closing.


		if (lpServerDoc->m_lpOleAdviseHldr) {
			ServerDoc_SendAdvise(
					(LPSERVERDOC)lpOleDoc,
					OLE_ONCLOSE,
					NULL,   /* lpmkDoc -- not relevant here */
					0       /* advf -- not relevant here */
			);

			/* OLE2NOTE: OnClose is the last notification that we need to
			**    send; release our OleAdviseHolder. we SHOULD be the only
			**    one using it. this will make our destructor realize that
			**    OnClose notification has already been sent.
			*/
			OleStdVerifyRelease(
					(LPUNKNOWN)lpServerDoc->m_lpOleAdviseHldr,
					"OleAdviseHldr not released properly"
			);
			lpServerDoc->m_lpOleAdviseHldr = NULL;
		}

		/* release our Container's ClientSite. */
		if(lpServerDoc->m_lpOleClientSite) {
			OleStdRelease((LPUNKNOWN)lpServerDoc->m_lpOleClientSite);
			lpServerDoc->m_lpOleClientSite = NULL;
		}
	}
#endif

	if (lpOleDoc->m_lpLLStm) {
		/* release our LineList stream. */
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm);
		lpOleDoc->m_lpLLStm = NULL;
	}

	if (lpOleDoc->m_lpNTStm) {
		/* release our NameTable stream. */
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpNTStm);
		lpOleDoc->m_lpNTStm = NULL;
	}

	if (lpOleDoc->m_lpStg) {
		/* release our doc storage. */
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpStg);
		lpOleDoc->m_lpStg = NULL;
	}

	if (lpOleDoc->m_lpFileMoniker) {
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpFileMoniker);
		lpOleDoc->m_lpFileMoniker = NULL;
	}

	/* OLE2NOTE: this call forces all external connections to our
	**    object to close down and therefore guarantees that we receive
	**    all releases associated with those external connections.
	*/
	OLEDBG_BEGIN2("CoDisconnectObject(lpDoc) called\r\n")
	CoDisconnectObject((LPUNKNOWN)&lpOleDoc->m_Unknown, 0);
	OLEDBG_END2

	OleDoc_Release(lpOleDoc);       // release artificial AddRef above
	OleApp_Release(lpOleApp);       // release artificial AddRef above

	OLEDBG_END3
	return TRUE;
}


/* OleDoc_Destroy
 * --------------
 *
 *  Free all OLE related resources that had been allocated for a document.
 */
void OleDoc_Destroy(LPOLEDOC lpOleDoc)
{
	LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
	LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpOleDoc;

	if (lpOleDoc->m_fObjIsDestroying)
		return;     // Doc destruction is already in progress

	lpOleDoc->m_fObjIsDestroying = TRUE;    // guard against recursive call

#if defined( OLE_SERVER )

	/* OLE2NOTE: it is ALWAYS necessary to make sure that the work we
	**    do in our OleDoc_Close function is performed before we
	**    destroy our document object. this includes revoking from the
	**    Running Object Table (ROT), sending OnClose notification,
	**    revoking from Drag/Drop, closing all pseudo objects, etc.
	**    There are some tricky scenarios involving linking and
	**    when IOleObject::Close is called versus when we get our
	**    final release causing us to call our OleDoc_Destroy
	**    (destructor) function.
	**
	**    SCENARIO 1 -- closing from server (File.Exit or File.Close)
	**                  OleDoc_Close function is called directly by the
	**                  server in response to the menu command
	**                  (WM_COMMAND processing).
	**
	**    SCENARIO 2 -- closed by embedding container
	**                  our embedding container calls IOleObject::Close
	**                  directly.
	**
	**    SCENARIO 3 -- silent-update final release
	**                  THIS IS THE TRICKY ONE!!!
	**                  in the case that our object is launched because
	**                  a linking client calls IOleObject::Update on
	**                  its link, then our object will be run
	**                  invisibly, typically GetData will be called,
	**                  and then the connection from the linking client
	**                  will be released. the release of this last
	**                  linking connection should cause our object to
	**                  shut down.
	**                  there are 2 strategies to deal with this scenario:
	**
	**                  STRATEGY 1 -- implement IExternalConnection.
	**                  IExternalConnection::AddConnection will be
	**                  called (by the StubManager) every time that an
	**                  external (linking) connection is created or
	**                  CoLockObjectExternal is called. the object
	**                  should maintain a count of strong connections
	**                  (m_dwStrongExtConn). IExternalConnection::
	**                  ReleaseConnection will be called when these
	**                  connections are released. when the
	**                  m_dwStrongExtConn transistions to 0, the object
	**                  should call its IOleObject::Close function.
	**                  this assumes that CoLockObjectExternal is used
	**                  to manage locks by the object itself (eg. when
	**                  the object is visible to the user--fUserCtrl,
	**                  and when PseudoObjects are created, etc.)
	**                  this is the strategy implemented by SVROUTL.
	**
	**                  STRATEGY 2 -- guard both the destructor
	**                  function and the Close function. if the
	**                  destructor is called directly without Close
	**                  first being called, then call Close before
	**                  proceeding with the destruction code.
	**                  previously SVROUTL was organized in this
	**                  manner. that old code is conditionaly compiled
	**                  away with "#ifdef OBSOLETE" below. this
	**                  method has the disadvantage that external
	**                  remoting is no longer possible by the time the
	**                  Close is called making it impossible for
	**                  the object to ask its container to save the
	**                  object if the object is dirty. this can result
	**                  in data loss. thus STRATEGY 1 is safer.
	**                  consider the scenario where an in-place
	**                  container UIDeactivates an object but does NOT
	**                  keep the object locked running (this is
	**                  required--see CntrLine_IPSite_OnInPlaceActivate
	**                  in cntrline.c), then, if a linking client binds
	**                  and unbinds from the object, the object will be
	**                  destroyed and will NOT have an opportunity to
	**                  be saved. by implementing IExternalConnection,
	**                  a server can insulate itself from a poorly
	**                  written container.
	*/
#if defined( _DEBUG )
	OleDbgAssertSz(
			(lpOutlineDoc->m_fDataTransferDoc || lpOleDoc->m_fObjIsClosing),
			"Destroy called without Close being called\r\n"
	);
#endif  // _DEBUG
#if defined( OBSOLETE )
	/* OLE2NOTE: if the document destructor is called directly because
	**    the object's refcnt went to 0 (ie. without OleDoc_Close first
	**    being called), then we need to make sure that the document is
	**    properly closed before destroying the object. this scenario
	**    could arise during a silent-update of a link. calling
	**    OleDoc_Close here guarantees that the clipboard will be
	**    properly flushed, the doc's moniker will be properly revoked,
	**    the document will be saved if necessary, etc.
	*/
	if (!lpOutlineDoc->m_fDataTransferDoc && !lpOleDoc->m_fObjIsClosing)
		OleDoc_Close(lpOleDoc, OLECLOSE_NOSAVE);
#endif

	{
		LPSERVERDOC lpServerDoc = (LPSERVERDOC)lpOleDoc;
		/* OLE2NOTE: perform processing specific for an OLE server */

#if defined( SVR_TREATAS )
		if (lpServerDoc->m_lpszTreatAsType) {
			OleStdFreeString(lpServerDoc->m_lpszTreatAsType, NULL);
			lpServerDoc->m_lpszTreatAsType = NULL;
		}
#endif  // SVR_TREATAS

#if defined( INPLACE_SVR )
		if (IsWindow(lpServerDoc->m_hWndHatch))
			DestroyWindow(lpServerDoc->m_hWndHatch);
#endif  // INPLACE_SVR
	}
#endif  // OLE_SERVER

	if (lpOleDoc->m_lpLLStm) {
		/* release our LineList stream. */
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpLLStm);
		lpOleDoc->m_lpLLStm = NULL;
	}

	if (lpOleDoc->m_lpNTStm) {
		/* release our NameTable stream. */
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpNTStm);
		lpOleDoc->m_lpNTStm = NULL;
	}

	if (lpOleDoc->m_lpStg) {
		/* release our doc storage. */
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpStg);
		lpOleDoc->m_lpStg = NULL;
	}

	if (lpOleDoc->m_lpFileMoniker) {
		OleStdRelease((LPUNKNOWN)lpOleDoc->m_lpFileMoniker);
		lpOleDoc->m_lpFileMoniker = NULL;
	}

	/*****************************************************************
	** OLE2NOTE: each document addref's the app object in order to  **
	**    guarentee that the app does not shut down while the doc   **
	**    is still open. since this doc is now destroyed, we will   **
	**    release this refcnt now. if there are now more open       **
	**    documents AND the app is not under the control of the     **
	**    user (ie. launched by OLE) then the app will revoke its   **
	**    ClassFactory. if there are no more references to the      **
	**    ClassFactory after it is revoked, then the app will shut  **
	**    down. this whole procedure is triggered by calling        **
	**    OutlineApp_DocUnlockApp.                                  **
	*****************************************************************/

	OutlineApp_DocUnlockApp(lpOutlineApp, lpOutlineDoc);
}


/* OleDoc_SetUpdateEditMenuFlag
 * ----------------------------
 *
 *  Purpose:
 *      Set/clear the UpdateEditMenuFlag in OleDoc.
 *
 *  Parameters:
 *      fUpdate     new value of the flag
 *
 *  Returns:
 */
void OleDoc_SetUpdateEditMenuFlag(LPOLEDOC lpOleDoc, BOOL fUpdate)
{
	if (!lpOleDoc)
		return;

	lpOleDoc->m_fUpdateEditMenu = fUpdate;
}


/* OleDoc_GetUpdateEditMenuFlag
 * ----------------------------
 *
 *  Purpose:
 *      Get the value of the UpdateEditMenuFlag in OleDoc
 *
 *  Parameters:
 *
 *  Returns:
 *      value of the flag
 */
BOOL OleDoc_GetUpdateEditMenuFlag(LPOLEDOC lpOleDoc)
{
	if (!lpOleDoc)
		return FALSE;

	return lpOleDoc->m_fUpdateEditMenu;
}



/*************************************************************************
** OleDoc::IUnknown interface implementation
*************************************************************************/

STDMETHODIMP OleDoc_Unk_QueryInterface(
		LPUNKNOWN           lpThis,
		REFIID              riid,
		LPVOID FAR*         lplpvObj
)
{
	LPOLEDOC lpOleDoc = ((struct CDocUnknownImpl FAR*)lpThis)->lpOleDoc;

	return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);
}


STDMETHODIMP_(ULONG) OleDoc_Unk_AddRef(LPUNKNOWN lpThis)
{
	LPOLEDOC lpOleDoc = ((struct CDocUnknownImpl FAR*)lpThis)->lpOleDoc;

	OleDbgAddRefMethod(lpThis, "IUnknown");

	return OleDoc_AddRef(lpOleDoc);
}


STDMETHODIMP_(ULONG) OleDoc_Unk_Release (LPUNKNOWN lpThis)
{
	LPOLEDOC lpOleDoc = ((struct CDocUnknownImpl FAR*)lpThis)->lpOleDoc;

	OleDbgReleaseMethod(lpThis, "IUnknown");

	return OleDoc_Release(lpOleDoc);
}
