// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include <shellapi.h>

#ifdef AFX_OLE4_SEG
#pragma code_seg(AFX_OLE4_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

//////////////////////////////////////////////////////////////////////////////
// COleServerDoc implementation

COleServerDoc::COleServerDoc()
{
	m_lpClientSite = NULL;  // will be non-NULL when document is embedding
	m_bCntrVisible = FALSE;
	m_pInPlaceFrame = NULL; // will be non-NULL when in-place active
	m_pOrigParent = NULL;
	m_dwOrigStyle = 0;
	m_dwOrigStyleEx = 0;
	m_bClosing = FALSE;
	m_pEmbeddedItem = NULL; // will be non-NULL if embedded item needed
}

COleServerDoc::~COleServerDoc()
{
	DeleteContents();   // Note: will not call derived class

	if (m_pEmbeddedItem != NULL)
	{
		m_pEmbeddedItem->ExternalRelease();
		m_pEmbeddedItem = NULL;
	}

	// disconnect (remove) all items from the document
	POSITION pos = GetStartPosition();
	COleServerItem* pItem;
	while ((pItem = GetNextServerItem(pos)) != NULL)
		RemoveItem(pItem);

	// should not be in-place active when doc is destroyed!
	ASSERT(m_pInPlaceFrame == NULL);

	// Note: this must be done before the client site is released
	RELEASE(m_lpRootStg);

	// release client-site pointer
	RELEASE(m_lpClientSite);
}

void COleServerDoc::DeleteContents()
{
	COleLinkingDoc::DeleteContents();

	// protect all server items with an extra reference count
	POSITION pos = GetStartPosition();
	COleServerItem* pItem;
	while ((pItem = GetNextServerItem(pos)) != NULL)
		pItem->InternalAddRef();

	// delete any autodelete server items
	pos = GetStartPosition();
	while ((pItem = GetNextServerItem(pos)) != NULL)
	{
		if (pItem->m_bAutoDelete)
			delete pItem;
	}

	// remove extra reference added above
	pos = GetStartPosition();
	while ((pItem = GetNextServerItem(pos)) != NULL)
		pItem->InternalRelease();
}

COleServerItem* COleServerDoc::GetEmbeddedItem()
{
	// allocate embedded item if necessary
	if (m_pEmbeddedItem == NULL)
	{
		m_pEmbeddedItem = OnGetEmbeddedItem();
		m_pEmbeddedItem->ExternalAddRef();
	}

	ASSERT_VALID(m_pEmbeddedItem);
	return m_pEmbeddedItem;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc client notifications

void COleServerDoc::NotifyRename(LPCTSTR lpszNewName)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidString(lpszNewName));

	if (m_pFactory != NULL)
	{
		// update running object table with new moniker
		Revoke();
		Register(m_pFactory, lpszNewName);

		// notify all items of the new moniker
		POSITION pos = GetStartPosition();
		COleServerItem* pItem;
		while ((pItem = GetNextServerItem(pos)) != NULL)
		{
			// notify client directly
			LPMONIKER lpMoniker = pItem->GetMoniker(OLEGETMONIKER_ONLYIFTHERE);
			pItem->NotifyClient(OLE_RENAMED, (DWORD)lpMoniker);
			RELEASE(lpMoniker);
		}
	}
}

void COleServerDoc::NotifyAllItems(OLE_NOTIFICATION nCode, DWORD dwParam)
{
	ASSERT_VALID(this);

	POSITION pos = GetStartPosition();
	COleServerItem* pItem;
	while ((pItem = GetNextServerItem(pos)) != NULL)
	{
		// notify client directly
		pItem->NotifyClient(nCode, dwParam);
	}
}

// UpdateAllItems is much like UpdateAllViews, but for server items
void COleServerDoc::UpdateAllItems(COleServerItem* pSender,
	LPARAM lHint, CObject* pHint, DVASPECT nDrawAspect)
{
	ASSERT_VALID(this);

	POSITION pos = GetStartPosition();
	COleServerItem* pItem;
	while ((pItem = GetNextServerItem(pos)) != NULL)
	{
		// notify client indirectly through OnUpdate
		if (pItem != pSender)
			pItem->OnUpdate(pSender, lHint, pHint, nDrawAspect);
	}
}

void COleServerItem::OnUpdate(COleServerItem* /*pSender*/,
	LPARAM /*lHint*/, CObject* /*pHint*/, DVASPECT nDrawAspect)
{
	// the default implementation always notifies the container, regardless
	//  of the specific hint or sender.

	NotifyChanged(nDrawAspect);
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc attributes

BOOL COleServerDoc::GetZoomFactor(LPSIZE lpSizeNum, LPSIZE lpSizeDenom,
	LPCRECT lpPosRect) const
{
	ASSERT_VALID(this);
	ASSERT(lpSizeNum == NULL || AfxIsValidAddress(lpSizeNum, sizeof(SIZE)));
	ASSERT(lpSizeDenom == NULL || AfxIsValidAddress(lpSizeDenom, sizeof(SIZE)));
	ASSERT(lpPosRect == NULL ||
		AfxIsValidAddress(lpPosRect, sizeof(RECT), FALSE));

	if (!IsInPlaceActive())
	{
		if (lpSizeNum != NULL)
		{
			ASSERT(lpSizeDenom != NULL);
			lpSizeNum->cx = 1;
			lpSizeNum->cy = 1;
			*lpSizeDenom = *lpSizeNum;
		}
		return FALSE;
	}
	ASSERT_VALID(m_pInPlaceFrame);

	// the zoom factor is (Size(position-rect) / m_sizeExtent)
	CSize sizeNum;
	if (lpPosRect == NULL)
	{
		// use current position rect
		sizeNum = m_pInPlaceFrame->m_rectPos.Size();
	}
	else
	{
		// use new position rect
		sizeNum.cx = lpPosRect->right - lpPosRect->left;
		sizeNum.cy = lpPosRect->bottom - lpPosRect->top;
	}

	// m_sizeExtent is in HIMETRIC units -- need to convert to pixels.
	CSize sizeDenom(0, 0);
	COleServerItem* pItem = ((COleServerDoc*)this)->GetEmbeddedItem();
	ASSERT_VALID(pItem);
	ASSERT_KINDOF(COleServerItem, pItem);

	// get zoom denominator, which is based on the current extent
	((COleServerItem*)pItem)->OnGetExtent(DVASPECT_CONTENT, sizeDenom);
	if (sizeDenom.cx == 0 || sizeDenom.cy == 0)
	{
		// no extent from container available, so use natural extent instead
		pItem->OnGetExtent(DVASPECT_CONTENT, sizeDenom);
	}
	// convert extent to pixels first
	((CDC*)NULL)->HIMETRICtoDP(&sizeDenom);

	// might be bad to have zoom denominator of zero!
	if (sizeDenom.cy == 0 || sizeDenom.cx == 0)
	{
		TRACE0("Warning: zero 'zoom denominator', using 100%% zoom instead.\n");
		sizeDenom = sizeNum;
	}

	// store the results
	if (lpSizeNum != NULL)
	{
		ASSERT(lpSizeDenom != NULL);
		*lpSizeNum = sizeNum;
		*lpSizeDenom = sizeDenom;
	}

	// if 100% scaling, return FALSE
	return sizeNum != sizeDenom;
}

void COleServerDoc::GetItemPosition(LPRECT lpPosRect) const
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpPosRect, sizeof(RECT)));
	ASSERT(IsInPlaceActive());
	ASSERT_VALID(m_pInPlaceFrame);

	// copy the current rectangle
	*lpPosRect = m_pInPlaceFrame->m_rectPos;
}

void COleServerDoc::GetItemClipRect(LPRECT lpClipRect) const
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpClipRect, sizeof(RECT)));
	ASSERT(IsInPlaceActive());
	ASSERT_VALID(m_pInPlaceFrame);

	// copy the current rectangle
	*lpClipRect = m_pInPlaceFrame->m_rectClip;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc overrideables

COleServerItem* COleServerDoc::OnGetLinkedItem(LPCTSTR lpszItemName)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidString(lpszItemName));

	// default implementation walks list of server items looking for
	//  a case sensitive match

	POSITION pos = GetStartPosition();
	COleServerItem* pItem;
	while ((pItem = GetNextServerItem(pos)) != NULL)
	{
		// return item if name matches (case sensitive)
		if (lstrcmp(pItem->GetItemName(), lpszItemName) == 0)
			return pItem;
	}

#ifdef _DEBUG
	if (afxTraceFlags & traceOle)
	{
		TRACE0("Warning: default COleServerDoc::OnGetLinkedItem implementation\n");
		TRACE1("\tfailed to find item '%s'.\n", lpszItemName);
	}
#endif
	return NULL;        // not found (no link found)
}

void COleServerDoc::OnClose(OLECLOSE dwCloseOption)
{
	ASSERT_VALID(this);

	// do nothing if already in the process of closing the document
	if (m_bClosing)
		return;

	// OLECLOSE_PROMPTSAVE is handled like OLECLOSE_SAVEIFDIRTY if invisible
	CFrameWnd* pFrameWnd = GetFirstFrame();
	if (pFrameWnd != NULL && pFrameWnd->IsWindowVisible())
		dwCloseOption = OLECLOSE_SAVEIFDIRTY;

	// handle modified document and special dwCloseOption flags
	TRY
	{
		if (IsModified())
		{
			switch (dwCloseOption)
			{
			case OLECLOSE_PROMPTSAVE:
				if (!SaveModifiedPrompt())
					AfxThrowOleException(OLE_E_PROMPTSAVECANCELLED);
				break;

			case OLECLOSE_SAVEIFDIRTY:
				SaveEmbedding();
				break;
			}
		}
	}
	END_TRY

	// deactivate in-place session
	if (m_pInPlaceFrame != NULL)
	{
		OnDeactivate();
		ASSERT(m_pInPlaceFrame == NULL);
	}

	// close the document
	BOOL bAutoDelete = m_bAutoDelete;
	m_bAutoDelete = FALSE;
	OnCloseDocument();
	m_bAutoDelete = bAutoDelete;
}

BOOL COleServerDoc::SaveModifiedPrompt()
{
	ASSERT_VALID(this);

	if (m_lpClientSite == NULL)
		return SaveModified();

	UpdateModifiedFlag();   // check for modified client items

	if (!IsModified())
		return TRUE;        // ok to continue

	CString prompt;
	AfxFormatString1(prompt, AFX_IDP_ASK_TO_UPDATE, m_strTitle);
	switch (AfxMessageBox(prompt, MB_YESNOCANCEL, AFX_IDP_ASK_TO_UPDATE))
	{
	case IDCANCEL:
		return FALSE;       // don't continue

	case IDYES:
		if (!OnUpdateDocument())
		{
			TRACE0("Warning: OnUpdateDocument failed to update.\n");
			// keep going, close will flush it
		}
		break;
	}
	return TRUE;    // keep going
}

BOOL COleServerDoc::CanCloseFrame(CFrameWnd* pFrame)
{
	m_bClosing = TRUE;
	if (!COleLinkingDoc::CanCloseFrame(pFrame))
	{
		m_bClosing = FALSE;
		return FALSE;
	}
	return TRUE;
}

BOOL COleServerDoc::GetFileTypeString(CString& rString)
{
	ASSERT_VALID(this);

	CDocTemplate* pTemplate = GetDocTemplate();
	if (pTemplate == NULL)
		return FALSE;

	pTemplate->GetDocString(rString, CDocTemplate::fileNewName);
	return !rString.IsEmpty();
}

void COleServerDoc::OnSetHostNames(LPCTSTR lpszHost, LPCTSTR lpszHostObj)
{
	ASSERT_VALID(this);
	ASSERT(lpszHost == NULL || AfxIsValidString(lpszHost));
	ASSERT(lpszHostObj == NULL || AfxIsValidString(lpszHostObj));
	UNUSED(lpszHost);    // unused in release builds

	// only change the title for embedded documents
	if (m_lpClientSite != NULL)
	{
		// save name of document for File.Exit update
		if (lpszHostObj == NULL)
			m_strHostObj.LoadString(AFX_IDS_UNTITLED);
		else
			m_strHostObj = lpszHostObj;

		// attempt to get the document name from the document template
		CString strFileType;
		if (!GetFileTypeString(strFileType))
			return;

		// format the string into <server-name> in <docname>
		CString strTitle;
		AfxFormatString2(strTitle, AFX_IDS_APP_TITLE_EMBEDDING,
			strFileType, m_strHostObj);

		// change title of document to that for an embedded item
		//  (this call will update all of the frames)
		SetTitle(strTitle);
	}
}

void COleServerDoc::SaveEmbedding()
{
	ASSERT_VALID(this);

	// tell the client site to save the object
	if (m_lpClientSite != NULL && !::InSendMessage())
	{
		SCODE sc = m_lpClientSite->SaveObject();
		if (sc != S_OK)
			AfxThrowOleException(sc);
	}
	ASSERT_VALID(this);
}

BOOL COleServerDoc::OnUpdateDocument()
{
	ASSERT_VALID(this);

	// don't save if already up-to-date
	if (!IsModified())
		return TRUE;

	// save a server document -> update
	TRY
	{
		SaveEmbedding();
	}
	CATCH_ALL(e)
	{
		AfxMessageBox(AFX_IDP_FAILED_TO_UPDATE);
		DELETE_EXCEPTION(e);
		return FALSE;
	}
	END_CATCH_ALL

	return TRUE;
}

LPMONIKER COleServerDoc::GetMoniker(OLEGETMONIKER nAssign)
{
	ASSERT_VALID(this);

	if (m_lpClientSite != NULL)
	{
		// get moniker from client site instead of from document
		LPMONIKER lpMoniker = NULL;
		m_lpClientSite->GetMoniker(nAssign, OLEWHICHMK_OBJFULL, &lpMoniker);
		return lpMoniker;
	}

	return COleLinkingDoc::GetMoniker(nAssign);
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc document handling

BOOL COleServerDoc::SaveModified()
{
	ASSERT_VALID(this);

	if (m_lpClientSite != NULL)
	{
		if (m_pInPlaceFrame == NULL)
		{
			UpdateModifiedFlag();   // check for modified items
			OnUpdateDocument();
		}
		return TRUE;
	}
	return COleLinkingDoc::SaveModified();
}

HMENU COleServerDoc::GetDefaultMenu()
{
	ASSERT_VALID(this);

	CDocTemplate* pTemplate = GetDocTemplate();
	if (pTemplate == NULL)
		return NULL;    // no doc template -- use default

	ASSERT_VALID(pTemplate);
	if (m_pInPlaceFrame != NULL)
		return pTemplate->m_hMenuInPlaceServer; // return special in-place menu
	else if (m_lpClientSite != NULL)
		return pTemplate->m_hMenuEmbedding; // return special embedding menu

	return NULL;    // no special mode, use default menu
}

HACCEL COleServerDoc::GetDefaultAccelerator()
{
	ASSERT_VALID(this);

	CDocTemplate* pTemplate = GetDocTemplate();
	if (pTemplate == NULL)
		return NULL;    // no doc template -- use default

	ASSERT_VALID(pTemplate);
	if (m_pInPlaceFrame != NULL)
		return pTemplate->m_hAccelInPlaceServer;    // return special in-place accel
	else if (m_lpClientSite != NULL)
		return pTemplate->m_hAccelEmbedding;// return special embedding accel

	return NULL;    // no special mode, use default menu
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc default command handling

BEGIN_MESSAGE_MAP(COleServerDoc, COleLinkingDoc)
	//{{AFX_MSG_MAP(COleServerDoc)
	ON_COMMAND(ID_FILE_UPDATE, OnFileUpdate)
	ON_COMMAND(ID_FILE_SAVE_COPY_AS, OnFileSaveCopyAs)
	ON_UPDATE_COMMAND_UI(ID_APP_EXIT, OnUpdateFileExit)
	ON_UPDATE_COMMAND_UI(ID_FILE_UPDATE, OnUpdateFileUpdate)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void COleServerDoc::OnFileUpdate()
{
	ASSERT_VALID(this);
	ASSERT(m_lpClientSite != NULL);

	UpdateModifiedFlag();
	OnUpdateDocument();
}

void COleServerDoc::OnFileSaveCopyAs()
{
	ASSERT_VALID(this);
	ASSERT(m_bRemember);
	ASSERT(m_lpClientSite != NULL);

	LPSTORAGE lpOrigStg = m_lpRootStg;
	m_lpRootStg = NULL; // ignore embedded storage for now

	TRY
	{
		// call DoSave to perform Save Copy As...
		m_bRemember = FALSE;
		DoSave(NULL, FALSE);
	}
	END_TRY

	m_lpRootStg = lpOrigStg;
	m_bRemember = TRUE;
}

void COleServerDoc::UpdateUsingHostObj(UINT nIDS, CCmdUI* pCmdUI)
{
	ASSERT_VALID(this);
	ASSERT(pCmdUI != NULL);

	if (m_lpClientSite == NULL)
		return;

	// update menu item using m_strHostObj
	CString str;
	AfxFormatString1(str, nIDS, m_strHostObj);
	if (!str.IsEmpty())
		pCmdUI->SetText(str);
}

void COleServerDoc::OnUpdateFileExit(CCmdUI* pCmdUI)
{
	ASSERT_VALID(this);
	ASSERT(pCmdUI != NULL);

	UpdateUsingHostObj(AFX_IDS_EXIT_MENU, pCmdUI);
}

void COleServerDoc::OnUpdateFileUpdate(CCmdUI* pCmdUI)
{
	ASSERT_VALID(this);
	ASSERT(pCmdUI != NULL);

	UpdateUsingHostObj(AFX_IDS_UPDATE_MENU, pCmdUI);
}

BOOL COleServerDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
	ASSERT_VALID(this);
	ASSERT(lpszPathName == NULL || AfxIsValidString(lpszPathName));

	BOOL bRemember = m_bRemember;
	if (!COleLinkingDoc::OnSaveDocument(lpszPathName))
		return FALSE;

	if (lpszPathName != NULL && bRemember)
	{
		if (AfxComparePath(GetPathName(), lpszPathName))
			NotifySaved();
	}
	return TRUE;
}

void COleServerDoc::OnCloseDocument()
{
	ASSERT_VALID(this);

	// don't allow in-place active documents to be closed without first
	//  deactivating them!
	if (m_pInPlaceFrame != NULL)
	{
		if (GetFirstViewPosition() != NULL)
			return;

		// no views but currently in-place active indicates that
		//  a WM_ENDSESSION is being processed.
		m_pInPlaceFrame = NULL;
	}

	InternalAddRef();   // keep document stable during shutdown

	// update lock count before sending notifications
	UpdateVisibleLock(FALSE, FALSE);

	// send some notifications to the container
	if (m_lpClientSite != NULL && m_bCntrVisible)
	{
		// allow the container to unshade the object appropriately
		m_lpClientSite->OnShowWindow(FALSE);
		m_bCntrVisible = FALSE;
	}

	// send close notification
	NotifyClosed();

	// finish closing the document (before m_lpClientSite->Release)
	BOOL bAutoDelete = m_bAutoDelete;
	m_bAutoDelete = FALSE;
	COleLinkingDoc::OnCloseDocument();
	ASSERT_VALID(this);

	// release client-site pointer
	RELEASE(m_lpClientSite);

	// disconnect the object
	LPUNKNOWN lpUnknown = (LPUNKNOWN)GetInterface(&IID_IUnknown);
	ASSERT(lpUnknown != NULL);
	CoDisconnectObject(lpUnknown, 0);

	// destroy the document if allowed
	InterlockedDecrement(&m_dwRef);              // remove InternalAddRef above
	if (bAutoDelete)
		delete this;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc show/hide

void COleServerDoc::OnShowDocument(BOOL bShow)
{
	ASSERT_VALID(this);

	CWinApp* pApp = AfxGetApp();
	if (bShow)
	{
		// deactivate in-place session if active
		if (m_pInPlaceFrame != NULL)
		{
			OnDeactivate();
			ASSERT(m_pInPlaceFrame == NULL);
		}

		// find the first view of this document
		CFrameWnd* pFrameWnd;
		if ((pFrameWnd = GetFirstFrame()) != NULL)
		{
			// allow container to show & scroll to the object
			if (!pFrameWnd->IsWindowVisible() && m_lpClientSite != NULL)
				m_lpClientSite->ShowObject();

			// activate frame holding view
			ASSERT_VALID(pFrameWnd);
			pFrameWnd->ActivateFrame();

			// activate application if necessary
			CFrameWnd* pAppFrame = pFrameWnd->GetParentFrame();
			if (pAppFrame != NULL)
			{
				pFrameWnd = pAppFrame;
				ASSERT_VALID(pFrameWnd);
				ASSERT_KINDOF(CFrameWnd, pFrameWnd);
				pFrameWnd->ActivateFrame();
			}
			pFrameWnd->GetLastActivePopup()->SetForegroundWindow();

			// update the menu and title as appropriate for this document
			pFrameWnd->OnUpdateFrameMenu(NULL);
			pFrameWnd->OnUpdateFrameTitle(TRUE);
		}
		else if (pApp->m_pMainWnd != NULL)
		{
			// otherwise, just show the main window (for simple servers)
			CWnd* pWnd = AfxGetMainWnd();

			// allow container to show & scroll to the object
			if (!pWnd->IsWindowVisible() && m_lpClientSite != NULL)
				m_lpClientSite->ShowObject();

			pWnd->ShowWindow(SW_SHOW);
			pWnd->SetActiveWindow();
			pWnd->SetForegroundWindow();
		}

		// for file based documents, showing the document puts user in control.
		if (!m_bEmbedded)
			AfxOleSetUserCtrl(TRUE);
	}
	else
	{
		if (m_pInPlaceFrame != NULL)
		{
			// hide has semantics of DeactivateUI if the item is active
			if (m_pInPlaceFrame->m_bUIActive)
				OnDeactivateUI(FALSE);

			// and then hide the frame itself
			if (m_pInPlaceFrame != NULL)
				m_pInPlaceFrame->ActivateFrame(SW_HIDE);
			return;
		}

		// find the first view of this document
		POSITION pos = GetFirstViewPosition();
		if (pos != NULL)
		{
			CFrameWnd* pDocFrame = GetFirstFrame();
			CFrameWnd* pActiveFrame = NULL;
			CFrameWnd* pMainFrame = NULL;
			CView* pView = GetNextView(pos);
			ASSERT_VALID(pView);

			// destroy or hide all the frames for this document
			//  (the main for the document is hidden, where the alternate
			//   frames are simply destroyed)
			do
			{
				// hide frame holding view
				CFrameWnd* pFrame = pView->GetParentFrame();
				ASSERT_VALID(pFrame);

				// determine next valid view before destroying the frame
				while ((pView = GetNextView(pos)) != NULL)
				{
					if (pView->GetParentFrame() != pFrame)
						break;
				}

				pMainFrame = pFrame->GetParentFrame();
				if (pMainFrame != NULL && pMainFrame->GetActiveFrame() == pFrame)
				{
					// don't destroy the active frame until later
					pActiveFrame = pFrame;
				}
				else
				{
					// not the active frame -- destroy/hide it now
					PreCloseFrame(pFrame);
					if (pDocFrame == pFrame)
						pFrame->ActivateFrame(SW_HIDE);
					else
						pFrame->DestroyWindow();
				}

			} while (pView != NULL);

			// hide the active frame last
			if (pActiveFrame != NULL)
			{
				PreCloseFrame(pActiveFrame);
				if (pDocFrame == pActiveFrame)
					pActiveFrame->ActivateFrame(SW_HIDE);
				else
					pActiveFrame->DestroyWindow();

				// should leave at least one frame
				ASSERT_VALID(this);
				ASSERT_VALID(GetFirstFrame());
			}
		}

		CFrameWnd* pMainFrame = (CFrameWnd*)pApp->m_pMainWnd;
		if (!AfxOleGetUserCtrl() && pMainFrame != NULL &&
			pMainFrame->IsWindowEnabled() && pMainFrame->IsFrameWnd() &&
			pMainFrame->GetActiveFrame() == pMainFrame)
		{
			// hide the entire application -- no visible documents left
			pApp->HideApplication();
		}
	}

	// send OnShowWindow notifications to the container
	if (m_lpClientSite != NULL && (bShow || m_bCntrVisible != bShow))
	{
		// allow the container to shade the object appropriately
		m_lpClientSite->OnShowWindow(bShow);
		m_bCntrVisible = bShow;
	}

	// force update visible lock
	if (bShow)
		UpdateVisibleLock(TRUE, FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc storage implementation

void COleServerDoc::OnNewEmbedding(LPSTORAGE lpStorage)
{
	ASSERT_VALID(this);
	ASSERT(lpStorage != NULL);

	// save state
	BOOL bUserCtrl = AfxOleGetUserCtrl();

	TRY
	{
		// remember new storage
		DeleteContents();
		lpStorage->AddRef();
		RELEASE(m_lpRootStg);
		m_lpRootStg = lpStorage;
		m_strPathName.Empty();
		m_bEmbedded = TRUE;

		// do document initialization by calling OnNewDocument
		if (!OnNewDocument())
			AfxThrowMemoryException();
	}
	CATCH_ALL(e)
	{
		// restore state
		AfxOleSetUserCtrl(bUserCtrl);
		THROW_LAST();
	}
	END_CATCH_ALL

	// restore state
	AfxOleSetUserCtrl(bUserCtrl);

	SetModifiedFlag();  // new storage-based documents are dirty!
	SendInitialUpdate();
}

void COleServerDoc::OnOpenEmbedding(LPSTORAGE lpStorage)
{
	ASSERT_VALID(this);
	ASSERT(lpStorage != NULL);

	// save state
	BOOL bUserCtrl = AfxOleGetUserCtrl();

	TRY
	{
		// abort changes to current document
		DeleteContents();
		lpStorage->AddRef();
		RELEASE(m_lpRootStg);
		m_lpRootStg = lpStorage;

		// open document from the sub-storage
		if (!OnOpenDocument(NULL))
			AfxThrowMemoryException();

		// now document is storage based
		m_strPathName.Empty();
		m_bEmbedded = TRUE;
	}
	CATCH_ALL(e)
	{
		// restore state
		AfxOleSetUserCtrl(bUserCtrl);
		THROW_LAST();
	}
	END_CATCH_ALL

	// restore state
	AfxOleSetUserCtrl(bUserCtrl);

	SetModifiedFlag(FALSE); // start off with unmodified
	SendInitialUpdate();
}

void COleServerDoc::OnSaveEmbedding(LPSTORAGE lpStorage)
{
	ASSERT_VALID(this);
	ASSERT(lpStorage != NULL);

	// save state
	BOOL bUserCtrl = AfxOleGetUserCtrl();

	// check for save as
	LPSTORAGE lpOrigStg = m_lpRootStg;
	if (!m_bSameAsLoad)
	{
		// File Save[Copy] As (saving to different file)
		ASSERT(lpStorage != NULL);
		m_lpRootStg = lpStorage;
	}

	TRY
	{
		// save document to the sub-storage
		if (!OnSaveDocument(NULL))
			AfxThrowMemoryException();
	}
	CATCH_ALL(e)
	{
		// restore state
		AfxOleSetUserCtrl(bUserCtrl);
		// re-attach original storage
		m_lpRootStg = lpOrigStg;
		THROW_LAST();
	}
	END_CATCH_ALL

	// restore state
	AfxOleSetUserCtrl(bUserCtrl);

	// re-attach original storage
	m_lpRootStg = lpOrigStg;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc in-place activation implementation

BOOL COleServerDoc::ActivateInPlace()
{
	USES_CONVERSION;

	ASSERT_VALID(this);
	LPCOLESTR lpszTitle = NULL;

	if (m_lpClientSite == NULL)
		return FALSE;   // no client-side (may be a link)

	// activate already in-place window if currently in-place active
	if (m_pInPlaceFrame != NULL)
	{
		if (m_pInPlaceFrame->m_bUIActive)
		{
			m_lpClientSite->ShowObject();   // object should get focus
			return TRUE;
		}
		// deactivate in-place session entirely before continuing
		OnDeactivate();
	}

	// fail if already fully open
	if (GetFirstFrame()->IsWindowVisible())
		return FALSE;

	// build object title/name (the container may use this in its caption)
	CString strFileType, strTitle;
	if (!GetFileTypeString(strFileType))
		return FALSE;
	AfxFormatString2(strTitle, AFX_IDS_OBJ_TITLE_INPLACE,
		AfxGetAppName(), strFileType);

	// attempt to get in-place client-site interface
	LPOLEINPLACESITE lpInPlaceSite =
		QUERYINTERFACE(m_lpClientSite, IOleInPlaceSite);
	if (lpInPlaceSite == NULL)
	{
		// unable to get in-place client site interface
		return FALSE;
	}

	// see if the container wants to go in-place right now
	if (lpInPlaceSite->CanInPlaceActivate() != S_OK)
		goto ReleaseAndFail;

	// start activation sequence...

	if (lpInPlaceSite->OnInPlaceActivate() != S_OK)
		goto ReleaseAndFail;

	// we'll need the parent window to create the COleIPFrameWnd
	HWND hWnd;
	VERIFY(lpInPlaceSite->GetWindow(&hWnd) == S_OK);
	CWnd* pParentWnd;
	pParentWnd = CWnd::FromHandle(hWnd);

	// create the inplace frame window
	COleIPFrameWnd* pFrameWnd;
	pFrameWnd = CreateInPlaceFrame(pParentWnd);
	if (pFrameWnd == NULL)
	{
		ASSERT(lpInPlaceSite != NULL);
		lpInPlaceSite->OnInPlaceDeactivate();
		goto ReleaseAndFail;
	}
	ASSERT(pFrameWnd->GetParent() == pParentWnd);
	m_pInPlaceFrame = pFrameWnd;

	// send activate notification
	if (lpInPlaceSite->OnUIActivate() != S_OK)
		goto DestroyFrameAndFail;

	// need to get frame & doc window interfaces as well as other info
	RECT rcPosRect, rcClipRect;
	if (lpInPlaceSite->GetWindowContext(
		&pFrameWnd->m_lpFrame, &pFrameWnd->m_lpDocFrame,
		&rcPosRect, &rcClipRect, &pFrameWnd->m_frameInfo) != S_OK)
	{
		goto DeactivateUIAndFail;
	}
	ASSERT(pFrameWnd->m_lpFrame != NULL);

	// setup the shared menu
	if (!pFrameWnd->BuildSharedMenu())
		goto DeactivateUIAndFail;

	// allow server to install frame controls in container
	VERIFY(pFrameWnd->m_lpFrame->GetWindow(&hWnd) == S_OK);
	pFrameWnd->m_pMainFrame = new COleCntrFrameWnd(pFrameWnd);
	pFrameWnd->m_pMainFrame->Attach(hWnd);
	if (pFrameWnd->m_lpDocFrame != NULL)
	{
		VERIFY(pFrameWnd->m_lpDocFrame->GetWindow(&hWnd) == S_OK);
		pFrameWnd->m_pDocFrame = new COleCntrFrameWnd(pFrameWnd);
		pFrameWnd->m_pDocFrame->Attach(hWnd);
	}
	// update zoom factor information before creating control bars
	pFrameWnd->m_rectPos.CopyRect(&rcPosRect);
	pFrameWnd->m_rectClip.CopyRect(&rcClipRect);
	if (!pFrameWnd->OnCreateControlBars(pFrameWnd->m_pMainFrame,
		pFrameWnd->m_pDocFrame))
	{
		goto DeactivateUIAndFail;
	}

	// resize the window to match the object
	OnSetItemRects(&rcPosRect, &rcClipRect);

	// set the active object
	ASSERT(pFrameWnd->m_lpFrame != NULL);
	LPOLEINPLACEACTIVEOBJECT lpActiveObject;
	lpActiveObject = (LPOLEINPLACEACTIVEOBJECT)
		GetInterface(&IID_IOleInPlaceActiveObject);
	lpszTitle = T2COLE(strTitle);
	pFrameWnd->m_lpFrame->SetActiveObject(lpActiveObject, lpszTitle);
	if (pFrameWnd->m_lpDocFrame != NULL)
		pFrameWnd->m_lpDocFrame->SetActiveObject(lpActiveObject, lpszTitle);

	// add frame & document level frame controls
	ASSERT(m_pInPlaceFrame->m_lpFrame != NULL);
	OnShowControlBars(m_pInPlaceFrame->m_pMainFrame, TRUE);
	if (m_pInPlaceFrame->m_lpDocFrame != NULL)
		OnShowControlBars(m_pInPlaceFrame->m_pDocFrame, TRUE);

	// show any hidden modeless dialogs as well...
	m_pInPlaceFrame->ShowOwnedWindows(TRUE);

	// attempt toolbar negotiation
	OnResizeBorder(NULL, pFrameWnd->m_lpFrame, TRUE);
	if (pFrameWnd->m_lpDocFrame != NULL)
		OnResizeBorder(NULL, pFrameWnd->m_lpDocFrame, FALSE);

	// install the menu (also installs a hook which forwards messages from
	//  the menu to the inplace frame window)
	pFrameWnd->m_lpFrame->SetMenu(pFrameWnd->m_hSharedMenu,
		pFrameWnd->m_hOleMenu, pFrameWnd->m_hWnd);

	// make sure object is scrolled into view
	m_lpClientSite->ShowObject();   // object should get focus

	// finally -- show the inplace frame window and set focus
	pFrameWnd->ShowWindow(SW_SHOW);
	pFrameWnd->SetFocus();
	pFrameWnd->UpdateWindow();
	UpdateVisibleLock(TRUE, FALSE);

	// allow the main window to be set
	OnFrameWindowActivate(TRUE);
	pFrameWnd->m_bUIActive = TRUE;

	// cleanup and return
	lpInPlaceSite->Release();
	return TRUE;

DeactivateUIAndFail:
	ASSERT(lpInPlaceSite != NULL);
	lpInPlaceSite->OnUIDeactivate(FALSE);

DestroyFrameAndFail:
	if (m_pInPlaceFrame != NULL)
	{
		ASSERT(pFrameWnd != NULL);
		DestroyInPlaceFrame(pFrameWnd);
		m_pInPlaceFrame = NULL;

		// also need to send OnInPlaceDeactivate notification
		ASSERT(lpInPlaceSite != NULL);
		lpInPlaceSite->OnInPlaceDeactivate();
	}

ReleaseAndFail:
	ASSERT(lpInPlaceSite != NULL);
	lpInPlaceSite->Release();
	return FALSE;
}

COleIPFrameWnd* COleServerDoc::CreateInPlaceFrame(CWnd* pParentWnd)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pParentWnd);

	// get runtime class from the doc template
	CDocTemplate* pTemplate = GetDocTemplate();
	ASSERT_VALID(pTemplate);

	// use existing view if possible
	CWnd* pViewParent = NULL;
	CView* pView = NULL;
	CFrameWnd* pFrame = GetFirstFrame();
	if (pFrame != NULL)
	{
		pView = (CView*)pFrame->GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);
		if (pView != NULL)
		{
			ASSERT_KINDOF(CView, pView);
			pViewParent = pView->GetParent();
			m_dwOrigStyle = pView->GetStyle();
			m_dwOrigStyleEx = pView->GetExStyle();
		}
	}

	// create the frame from the template
	COleIPFrameWnd* pFrameWnd = (COleIPFrameWnd*)
		pTemplate->CreateOleFrame(pParentWnd, this, pView == NULL);
	if (pFrameWnd == NULL)
		return NULL;

	// connect the view to the frame window, if necessary
	if (pView != NULL)
	{
		ConnectView(pFrameWnd, pView);
		pView->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
	}

	// remember original parent window for deactivate
	m_pOrigParent = pViewParent;

	// send OnInitialUpdate if new view was created
	if (pView == NULL)
		pTemplate->InitialUpdateFrame(pFrameWnd, this, FALSE);

	// verify the type
	ASSERT_VALID(pFrameWnd);
	ASSERT_KINDOF(COleIPFrameWnd, pFrameWnd);
	return pFrameWnd;
}

void COleServerDoc::DestroyInPlaceFrame(COleIPFrameWnd* pFrameWnd)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pFrameWnd);

	// connect view to original, if existing view was used
	if (m_pOrigParent != NULL)
	{
		CView* pView = (CView*)pFrameWnd->GetDescendantWindow(
			AFX_IDW_PANE_FIRST, TRUE);
		ASSERT_VALID(pView);

		// leaving the focus on an MDI child or one of its child windows
		// causes Windows to get confused when the child window is
		// destroyed, not to mention the fact that the focus will be
		// out of sync with activation.
		if (::GetFocus() == pView->m_hWnd)
		{
			// move focus to somewhere safe
			HWND hWnd = ::GetParent(pFrameWnd->m_hWnd);
			if (hWnd != NULL)
				::SetFocus(hWnd);

			// check again
			if (::GetFocus() == pView->m_hWnd)
				SetFocus(NULL); // last ditch effort
		}

		ConnectView(m_pOrigParent, pView);
		m_pOrigParent = NULL;

		// remove any scrollbars added because of in-place activation
		if ((m_dwOrigStyle & (WS_HSCROLL|WS_VSCROLL)) == 0 &&
			(pView->GetStyle() & (WS_HSCROLL|WS_VSCROLL)) != 0)
		{
			::SetScrollRange(pView->m_hWnd, SB_HORZ, 0, 0, TRUE);
			::SetScrollRange(pView->m_hWnd, SB_VERT, 0, 0, TRUE);
		}

		// restore old 3D style
		pView->ModifyStyleEx(0, m_dwOrigStyleEx & WS_EX_CLIENTEDGE,
			SWP_DRAWFRAME);

		// force recalc layout on splitter window
		CSplitterWnd* pSplitter = CView::GetParentSplitter(pView, TRUE);
		if (pSplitter != NULL)
			pSplitter->RecalcLayout();
	}

	// no active view or document during destroy
	pFrameWnd->SetActiveView(NULL);

	// destroy in-place frame window
	pFrameWnd->DestroyWindow();
}

void COleServerDoc::ConnectView(CWnd* pParentWnd, CView* pView)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pParentWnd);
	ASSERT_VALID(pView);

	// move the view to the new parent
	pView->SetParent(pParentWnd);

	// Note: The currently active view on the original frame window is
	//  kept active, because some controls, especially Windows controls,
	//  continue to send notification messages to the original parent
	//  window of the control.  So, the original frame window is kept
	//  alive with the original active view pointer intact, such that
	//  these notification messages do not get lost.

	// set the active view of the new frame to newly moved view
	CFrameWnd* pFrameWnd = pParentWnd->IsFrameWnd() ?
		(CFrameWnd*)pParentWnd : pParentWnd->GetParentFrame();
	pFrameWnd->SetActiveView(pView, FALSE);
	pFrameWnd->RecalcLayout();
}

void COleServerDoc::OnFrameWindowActivate(BOOL bActivate)
{
	ASSERT_VALID(this);

	CFrameWnd* pFrameWnd = m_pInPlaceFrame;
	ASSERT_VALID(pFrameWnd);

	CWinThread* pThread = AfxGetThread();
	if (bActivate)
	{
		// activating -- so set the main window
		pThread->m_pActiveWnd = pFrameWnd;

		// send activation notification messages
		pFrameWnd->SendMessage(WM_ACTIVATEAPP, (WPARAM)TRUE);
		pFrameWnd->SendMessage(WM_ACTIVATE, WA_ACTIVE);
	}
	else if (pFrameWnd == pThread->m_pActiveWnd)
	{
		// send deactivation notification messages
		pFrameWnd->SendMessage(WM_ACTIVATEAPP, (WPARAM)FALSE);
		pFrameWnd->SendMessage(WM_ACTIVATE, WA_INACTIVE);

		// simulate deactivation notification messages
		CView* pActiveView = pFrameWnd->GetActiveView();
		if (pActiveView != NULL)
			pActiveView->OnActivateView(FALSE, pActiveView, pActiveView);

		// deactivating and was previously active -- reset the main window
		pThread->m_pActiveWnd = NULL;
	}
}

void COleServerDoc::OnDocWindowActivate(BOOL bActivate)
{
	ASSERT_VALID(this);

	CWinThread* pThread = AfxGetApp();
	COleIPFrameWnd* pFrameWnd = m_pInPlaceFrame;

	if (bActivate)
	{
		// attach frame windows back in the permanent map
		pFrameWnd->m_pMainFrame->Attach(pFrameWnd->m_pMainFrame->Detach());
		if (pFrameWnd->m_pDocFrame != NULL)
			pFrameWnd->m_pDocFrame->Attach(pFrameWnd->m_pDocFrame->Detach());

		// set active main window
		pThread->m_pActiveWnd = pFrameWnd;

		// show frame level controls
		OnShowControlBars(pFrameWnd->m_pMainFrame, TRUE);
		pFrameWnd->ShowOwnedWindows(TRUE);

		// attempt toolbar negotiation
		OnResizeBorder(NULL, pFrameWnd->m_lpFrame, TRUE);

		// install the menu (also installs a hook which forwards messages from
		//  the menu to the inplace frame window)
		pFrameWnd->m_lpFrame->SetMenu(
			pFrameWnd->m_hSharedMenu, pFrameWnd->m_hOleMenu,
			pFrameWnd->m_hWnd);

		// set focus to the frame (it will probably set focus to the view)
		//  (by simulating normal application activate messages)
		pFrameWnd->SendMessage(WM_ACTIVATE, WA_ACTIVE);
	}
	else
	{
		// clear active window for app if this object is active
		if (pThread->m_pActiveWnd == pFrameWnd)
			pThread->m_pActiveWnd = NULL;

		// hide frame level controls -- this does not destroy them
		pFrameWnd->ShowOwnedWindows(FALSE);
		OnShowControlBars(pFrameWnd->m_pMainFrame, FALSE);

		// attempt toolbar negotiation
		OnResizeBorder(NULL, pFrameWnd->m_lpFrame, TRUE);

		// simulate deactivation notification messages
		CView* pActiveView = pFrameWnd->GetActiveView();
		if (pActiveView != NULL)
			pActiveView->OnActivateView(FALSE, pActiveView, pActiveView);
		pFrameWnd->SendMessage(WM_ACTIVATE, WA_INACTIVE);

		// set the m_hWnd members, but remove them from the maps
		pFrameWnd->m_pMainFrame->m_hWnd = pFrameWnd->m_pMainFrame->Detach();
		if (pFrameWnd->m_pDocFrame != NULL)
			pFrameWnd->m_pDocFrame->m_hWnd = pFrameWnd->m_pDocFrame->Detach();
	}
}

void COleServerDoc::OnShowControlBars(CFrameWnd* pFrameWnd, BOOL bShow)
{
	ASSERT_VALID(this);
	ASSERT(pFrameWnd == m_pInPlaceFrame->m_pMainFrame ||
		pFrameWnd == m_pInPlaceFrame->m_pDocFrame);
	ASSERT_VALID(pFrameWnd);

	// show/hide all control bars
	POSITION pos = pFrameWnd->m_listControlBars.GetHeadPosition();
	while (pos != NULL)
	{
		// show/hide the next control bar
		CControlBar* pBar =
			(CControlBar*)pFrameWnd->m_listControlBars.GetNext(pos);
		ASSERT_VALID(pBar);
		if (bShow)
		{
			if (pBar->m_nStateFlags & CControlBar::tempHide)
			{
				pBar->m_nStateFlags &= ~CControlBar::tempHide;
				pFrameWnd->ShowControlBar(pBar, bShow, TRUE);
			}
		}
		else
		{
			if (pBar->IsVisible() && !pBar->IsFloating())
			{
				pBar->m_nStateFlags |= CControlBar::tempHide;
				pFrameWnd->ShowControlBar(pBar, bShow, TRUE);
			}
		}
	}
}

void COleServerDoc::OnResizeBorder(
	LPCRECT lpRectBorder, LPOLEINPLACEUIWINDOW lpUIWindow, BOOL bFrame)
{
	ASSERT_VALID(this);
	ASSERT(lpRectBorder == NULL ||
		AfxIsValidAddress(lpRectBorder, sizeof(RECT), FALSE));
	ASSERT(lpUIWindow != NULL);

	// use IOleInPlaceUIWindow::GetBorder if no border given
	CRect rectBorder;
	if (lpRectBorder != NULL)
	{
		// use border space passed in
		rectBorder.CopyRect(lpRectBorder);
	}
	else
	{
		// GetBorderSpace may fail (WinWord6, for example)
		if (lpUIWindow->GetBorder(&rectBorder) != S_OK)
		{
			// in that case, always call SetBorderSpace(NULL), but
			// ignore the return value.
			lpUIWindow->SetBorderSpace(NULL);
			return;
		}
	}

	// get CWnd* for the OLE window
	CFrameWnd* pFrameWnd = bFrame ?
		m_pInPlaceFrame->m_pMainFrame : m_pInPlaceFrame->m_pDocFrame;

	// if this is the active document clear temphide bits from bars
	if (AfxGetThread()->m_pActiveWnd == m_pInPlaceFrame)
		OnShowControlBars(pFrameWnd, TRUE);

	// see how much space we need by calling reposition bars
	CRect rectNeeded = rectBorder;
	pFrameWnd->RepositionBars(0, 0xFFFF, 0, CWnd::reposQuery, &rectNeeded,
		&rectBorder);

	// request the border space from the container
	CRect rectRequest(
		rectNeeded.left - rectBorder.left,
		rectNeeded.top - rectBorder.top,
		rectBorder.right - rectNeeded.right,
		rectBorder.bottom - rectNeeded.bottom);
	CRect rectTemp;
	rectTemp = rectRequest;

	// if no border space, just call SetBorderSpace
	if ((!rectRequest.IsRectNull() ||
		 !pFrameWnd->m_listControlBars.IsEmpty()) &&
		lpUIWindow->RequestBorderSpace(&rectTemp) == S_OK)
	{
		// set the border space -- now this object owns it
		VERIFY(lpUIWindow->SetBorderSpace(&rectRequest) == S_OK);

		// move the bars into position after committing the space
		pFrameWnd->RepositionBars(0, 0xFFFF, 0, CWnd::reposDefault, NULL,
			&rectBorder);

		// redraw all control bars
		POSITION pos = pFrameWnd->m_listControlBars.GetHeadPosition();
		while (pos != NULL)
		{
			CControlBar* pBar =
				(CControlBar*)pFrameWnd->m_listControlBars.GetNext(pos);
			ASSERT_VALID(pBar);
			if (!pBar->IsFloating())
				pBar->UpdateWindow();
		}
	}
	else
	{
		// hide any toolbars (since we couldn't get border space for any)
		OnShowControlBars(pFrameWnd, FALSE);

		// make sure border space is cleared
		CRect rect(0,0,0,0);
		lpUIWindow->SetBorderSpace(&rect);
	}
}

void COleServerDoc::OnDeactivate()
{
	ASSERT_VALID(this);

	ASSERT(m_pInPlaceFrame != NULL);

	// do UI deactivate first -- this hides everything
	if (m_pInPlaceFrame->m_bUIActive)
	{
		OnDeactivateUI(FALSE);

		// some containers call OnDeactivate during OnDeactivateUI
		if (m_pInPlaceFrame == NULL)
			return;
	}
	ASSERT(m_pInPlaceFrame != NULL);
	ASSERT(!m_pInPlaceFrame->m_bUIActive);

	// now safe to destroy the shared menu
	m_pInPlaceFrame->DestroySharedMenu();

	// no longer need doc & frame window interfaces
	RELEASE(m_pInPlaceFrame->m_lpFrame);
	RELEASE(m_pInPlaceFrame->m_lpDocFrame);
	DestroyInPlaceFrame(m_pInPlaceFrame);
	m_pInPlaceFrame = NULL;
		// destructor for COleIPFrameWnd or derivative should cleanup any
		//   toolbars etc. created during in-place activation

	// last of all, call IOleClientSite::InPlaceDeactivate
	ASSERT(m_lpClientSite != NULL);
	LPOLEINPLACESITE lpInPlaceSite =
		QUERYINTERFACE(m_lpClientSite, IOleInPlaceSite);
	if (lpInPlaceSite != NULL)
	{
		lpInPlaceSite->OnInPlaceDeactivate();
		lpInPlaceSite->Release();
	}
}

void COleServerDoc::OnDeactivateUI(BOOL bUndoable)
{
	ASSERT_VALID(this);
	COleIPFrameWnd* pFrameWnd = m_pInPlaceFrame;

	if (pFrameWnd == NULL || !pFrameWnd->m_bUIActive)
		return;

	// reset active object pointers
	ASSERT(pFrameWnd->m_lpFrame != NULL);
	pFrameWnd->m_lpFrame->SetActiveObject(NULL, NULL);
	if (pFrameWnd->m_lpDocFrame != NULL)
		pFrameWnd->m_lpDocFrame->SetActiveObject(NULL, NULL);

	// remove frame & document level frame controls
	ASSERT(pFrameWnd->m_lpFrame != NULL);
	OnShowControlBars(pFrameWnd->m_pMainFrame, FALSE);
	if (pFrameWnd->m_lpDocFrame != NULL)
		OnShowControlBars(pFrameWnd->m_pDocFrame, FALSE);

	// hide the frame and any popups owned by the frame
	pFrameWnd->ShowOwnedWindows(FALSE);
	pFrameWnd->ShowWindow(SW_HIDE);
	pFrameWnd->m_nShowDelay = SW_HIDE;
	pFrameWnd->m_pMainFrame->m_nShowDelay = SW_HIDE;

	// set the m_hWnd members, but remove them from the maps
	pFrameWnd->m_pMainFrame->m_hWnd = pFrameWnd->m_pMainFrame->Detach();
	if (pFrameWnd->m_pDocFrame != NULL)
		pFrameWnd->m_pDocFrame->m_hWnd = pFrameWnd->m_pDocFrame->Detach();

	// no longer UI active...
	pFrameWnd->m_bUIActive = FALSE;
	CWinThread* pThread = AfxGetApp();
	if (pThread->m_pActiveWnd == pFrameWnd)
		pThread->m_pActiveWnd = NULL;

	// call IOleClientSite::OnUIDeactivate
	ASSERT(m_lpClientSite != NULL);
	LPOLEINPLACESITE lpInPlaceSite =
		QUERYINTERFACE(m_lpClientSite, IOleInPlaceSite);
	if (lpInPlaceSite != NULL)
	{
		lpInPlaceSite->OnUIDeactivate(bUndoable);
		lpInPlaceSite->Release();
	}
}

void COleServerDoc::OnSetItemRects(LPCRECT lpPosRect, LPCRECT lpClipRect)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpPosRect, sizeof(RECT), FALSE));
	ASSERT(AfxIsValidAddress(lpClipRect, sizeof(RECT), FALSE));

	if (m_pInPlaceFrame == NULL)
		return;
	ASSERT_VALID(m_pInPlaceFrame);

	// tell the frame to position itself such that the view is at the given
	//  rectangle (relative to the frame's parent)
	m_pInPlaceFrame->RepositionFrame(lpPosRect, lpClipRect);
}

BOOL COleServerDoc::OnReactivateAndUndo()
{
	// default implementation doesn't support undo

	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc special APIs for in-place editing

void COleServerDoc::RequestPositionChange(LPCRECT lpPosRect)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpPosRect, sizeof(RECT), FALSE));

	// get IOleInPlaceSite interface
	ASSERT(m_lpClientSite != NULL);
	LPOLEINPLACESITE lpInPlaceSite =
		QUERYINTERFACE(m_lpClientSite, IOleInPlaceSite);

	if (lpInPlaceSite != NULL)
	{
		// call IOleInPlaceSite::OnPosRectChange
		lpInPlaceSite->OnPosRectChange(lpPosRect);
		lpInPlaceSite->Release();
	}
}

BOOL COleServerDoc::ScrollContainerBy(CSize sizeScroll)
{
	ASSERT_VALID(this);

	// get IOleInPlaceSite interface
	ASSERT(m_lpClientSite != NULL);
	LPOLEINPLACESITE lpInPlaceSite =
		QUERYINTERFACE(m_lpClientSite, IOleInPlaceSite);
	if (lpInPlaceSite == NULL)
		return FALSE;

	// call IOleInPlaceSite::Scroll
	BOOL bResult = lpInPlaceSite->Scroll(sizeScroll) == S_OK;
	lpInPlaceSite->Release();
	return bResult;
}

BOOL COleServerDoc::DeactivateAndUndo()
{
	ASSERT_VALID(this);

	// get IOleInPlaceSite interface
	ASSERT(m_lpClientSite != NULL);
	LPOLEINPLACESITE lpInPlaceSite =
		QUERYINTERFACE(m_lpClientSite, IOleInPlaceSite);
	if (lpInPlaceSite == NULL)
		return FALSE;

	// call IOleInPlaceSite::DeactivateAndUndo
	BOOL bResult = lpInPlaceSite->DeactivateAndUndo() == S_OK;
	lpInPlaceSite->Release();
	return bResult;
}

BOOL COleServerDoc::DiscardUndoState()
{
	ASSERT_VALID(this);

	// get IOleInPlaceSite interface
	ASSERT(m_lpClientSite != NULL);
	LPOLEINPLACESITE lpInPlaceSite =
		QUERYINTERFACE(m_lpClientSite, IOleInPlaceSite);
	if (lpInPlaceSite == NULL)
		return FALSE;

	// call IOleInPlaceSite::DiscardUndoState
	BOOL bResult = lpInPlaceSite->DiscardUndoState() == S_OK;
	lpInPlaceSite->Release();
	return bResult;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc OLE interface implementation

BEGIN_INTERFACE_MAP(COleServerDoc, COleLinkingDoc)
	INTERFACE_PART(COleServerDoc, IID_IPersistStorage, PersistStorage)
	INTERFACE_PART(COleServerDoc, IID_IOleObject, OleObject)
	INTERFACE_PART(COleServerDoc, IID_IDataObject, DataObject)
	INTERFACE_PART(COleServerDoc, IID_IOleWindow, OleInPlaceObject)
	INTERFACE_PART(COleServerDoc, IID_IOleInPlaceObject, OleInPlaceObject)
	INTERFACE_PART(COleServerDoc, IID_IOleInPlaceActiveObject, OleInPlaceActiveObject)
END_INTERFACE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc::CPersistStorage

STDMETHODIMP_(ULONG) COleServerDoc::XPersistStorage::AddRef()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, PersistStorage)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleServerDoc::XPersistStorage::Release()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, PersistStorage)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleServerDoc::XPersistStorage::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, PersistStorage)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleServerDoc::XPersistStorage::GetClassID(LPCLSID lpClassID)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, PersistStorage)

	LPPERSISTFILE lpPersistFile = (LPPERSISTFILE)
		pThis->GetInterface(&IID_IPersistFile);
	return lpPersistFile->GetClassID(lpClassID);
}

STDMETHODIMP COleServerDoc::XPersistStorage::IsDirty()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, PersistStorage)

	LPPERSISTFILE lpPersistFile = (LPPERSISTFILE)
		pThis->GetInterface(&IID_IPersistFile);
	return lpPersistFile->IsDirty();
}

STDMETHODIMP COleServerDoc::XPersistStorage::InitNew(LPSTORAGE pStg)
{
	METHOD_PROLOGUE_EX(COleServerDoc, PersistStorage)
	ASSERT_VALID(pThis);

	SCODE sc = S_OK;
	TRY
	{
		// delegate to member function in the document
		pThis->OnNewEmbedding(pStg);
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	ASSERT_VALID(pThis);
	return sc;
}

STDMETHODIMP COleServerDoc::XPersistStorage::Load(LPSTORAGE pStg)
{
	METHOD_PROLOGUE_EX(COleServerDoc, PersistStorage)
	ASSERT_VALID(pThis);

	SCODE sc = S_OK;
	pThis->BeginDeferErrors();
	TRY
	{
		// delegate to member function in the document
		pThis->OnOpenEmbedding(pStg);
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL
	sc = pThis->EndDeferErrors(sc);

	ASSERT_VALID(pThis);
	return sc;
}

STDMETHODIMP COleServerDoc::XPersistStorage::Save(
	LPSTORAGE pStgSave, BOOL fSameAsLoad)
{
	METHOD_PROLOGUE_EX(COleServerDoc, PersistStorage)
	ASSERT_VALID(pThis);

	// don't bother saving if destination is up-to-date
	if (fSameAsLoad && !pThis->IsModified())
		return S_OK;

	SCODE sc = S_OK;
	pThis->BeginDeferErrors();
	TRY
	{
		// delegate through the document
		ASSERT(pThis->m_bRemember);
		pThis->m_bRemember = FALSE;
		pThis->m_bSameAsLoad = fSameAsLoad;
		pThis->OnSaveEmbedding(pStgSave);

		// clear dirty flag since save to same storage successful
		if (fSameAsLoad)
		{
			pThis->SetModifiedFlag(FALSE);

			// notify clients that object has been saved
			pThis->NotifySaved();
		}
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL
	sc = pThis->EndDeferErrors(sc);

	// restore default state
	pThis->m_bRemember = TRUE;

	ASSERT_VALID(pThis);
	return sc;
}

STDMETHODIMP COleServerDoc::XPersistStorage::SaveCompleted(LPSTORAGE pStgSaved)
{
	METHOD_PROLOGUE_EX(COleServerDoc, PersistStorage)
	ASSERT_VALID(pThis);

	// call SaveCompleted on any embedded items
	pThis->CommitItems(pStgSaved != NULL);

	// update state to reflect new storage
	if (pStgSaved != NULL)
	{
		// attach new storage
		pStgSaved->AddRef();
		RELEASE(pThis->m_lpRootStg);
		pThis->m_lpRootStg = pStgSaved;

		// now this document is storage based
		pThis->m_strPathName.Empty();
		pThis->m_bEmbedded = TRUE;
		pThis->SetModifiedFlag(FALSE);

		// notify clients that object has been saved
		pThis->NotifySaved();
	}

	ASSERT_VALID(pThis);
	return S_OK;
}

STDMETHODIMP COleServerDoc::XPersistStorage::HandsOffStorage()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, PersistStorage)

	if (pThis->m_lpRootStg != NULL)
	{
		// first call HandsOffStorage for all the embedded client items
		POSITION pos = pThis->GetStartPosition();
		COleClientItem* pItem;
		while ((pItem = pThis->GetNextClientItem(pos)) != NULL)
		{
			ASSERT(pItem->m_lpObject != NULL);
			LPPERSISTSTORAGE lpPersistStorage =
				QUERYINTERFACE(pItem->m_lpObject, IPersistStorage);
			ASSERT(lpPersistStorage != NULL);
			lpPersistStorage->HandsOffStorage();
			lpPersistStorage->Release();
		}

		// for now, can't access the storage
		RELEASE(pThis->m_lpRootStg);
	}

	ASSERT_VALID(pThis);
	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc::XOleObject

STDMETHODIMP_(ULONG) COleServerDoc::XOleObject::AddRef()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleServerDoc::XOleObject::Release()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleServerDoc::XOleObject::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleServerDoc::XOleObject::SetClientSite(
	LPOLECLIENTSITE pClientSite)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	// maintain reference counts
	if (pClientSite != NULL)
		pClientSite->AddRef();
	RELEASE(pThis->m_lpClientSite);
	pThis->m_lpClientSite = pClientSite;

	return S_OK;
}

STDMETHODIMP COleServerDoc::XOleObject::GetClientSite(
	LPOLECLIENTSITE* ppClientSite)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	if (pThis->m_lpClientSite == NULL)
	{
		*ppClientSite = NULL;
		return E_FAIL;
	}

	*ppClientSite = pThis->m_lpClientSite;
	pThis->m_lpClientSite->AddRef();
	return S_OK;
}

STDMETHODIMP COleServerDoc::XOleObject::SetHostNames(
	LPCOLESTR lpszContainerApp, LPCOLESTR lpszContainerObj)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	USES_CONVERSION;

	TRY
	{
		pThis->OnSetHostNames(OLE2CT(lpszContainerApp),
			OLE2CT(lpszContainerObj));
	}
	END_TRY

	return S_OK;
}

STDMETHODIMP COleServerDoc::XOleObject::Close(DWORD dwSaveOption)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	pThis->InternalAddRef();    // protect this object

	SCODE sc = S_OK;
	TRY
	{
		// delegate through document for most of the work
		pThis->OnClose((OLECLOSE)dwSaveOption);
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	pThis->InternalRelease();   // may 'delete this'

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::SetMoniker(
	DWORD /*dwWhichMoniker*/, LPMONIKER /*pmk*/)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	if (pThis->m_lpClientSite == NULL)
		return E_FAIL;

	// get current full moniker from client-site
	LPMONIKER lpMoniker = NULL;
	if (pThis->m_lpClientSite->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
		OLEWHICHMK_OBJFULL, &lpMoniker) != S_OK)
	{
		// just to make sure -- always set moniker to NULL on failure
		lpMoniker = NULL;
	}

	// update all embedded items with new moniker
	POSITION pos = pThis->GetStartPosition();
	COleClientItem* pItem;
	while ((pItem = pThis->GetNextClientItem(pos)) != NULL)
	{
		if (pItem->m_bMoniker)
			pItem->m_lpObject->SetMoniker(OLEWHICHMK_CONTAINER, lpMoniker);
	}

	// send Rename advises
	pThis->NotifyAllItems(OLE_RENAMED, (DWORD)lpMoniker);
	RELEASE(lpMoniker);

	return S_OK;
}

STDMETHODIMP COleServerDoc::XOleObject::GetMoniker(
	DWORD dwAssign, DWORD /*dwWhichMoniker*/, LPMONIKER* ppMoniker)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	*ppMoniker = pThis->GetMoniker((OLEGETMONIKER)dwAssign);
	return *ppMoniker == NULL ? E_FAIL : S_OK;
}

STDMETHODIMP COleServerDoc::XOleObject::InitFromData(
	LPDATAOBJECT pDataObject, BOOL fCreation, DWORD /*dwReserved*/)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	COleServerItem* pItem;
	SCODE sc;
	TRY
	{
		// delegate through item
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);

		COleDataObject dataObject;
		dataObject.Attach(pDataObject,  FALSE);
		sc = pItem->OnInitFromData(&dataObject, fCreation) ? S_OK : S_FALSE;
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::GetClipboardData(
	DWORD /*dwReserved*/, LPDATAOBJECT* ppDataObject)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	*ppDataObject = NULL;

	COleServerItem* pItem;
	SCODE sc = S_OK;
	TRY
	{
		// delegate through item
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);

		COleDataSource* pDataSource =
			pItem->OnGetClipboardData(TRUE, NULL, NULL);
		ASSERT(pDataSource != NULL);

		*ppDataObject =
			(LPDATAOBJECT)pDataSource->GetInterface(&IID_IDataObject);
		ASSERT(*ppDataObject != NULL);
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::DoVerb(
	LONG iVerb, LPMSG /*lpmsg*/, LPOLECLIENTSITE /*pActiveSite*/, LONG /*lindex*/,
	HWND /*hwndParent*/, LPCRECT /*lpPosRect*/)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	pThis->InternalAddRef();    // protect this object

	COleServerItem* pItem;
	SCODE sc = S_OK;
	TRY
	{
		// delegate through item
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		pItem->OnDoVerb(iVerb);
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL
	pThis->InternalRelease();   // may 'delete this'

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::EnumVerbs(
	IEnumOLEVERB** ppenumOleVerb)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	*ppenumOleVerb = NULL;

	LPOLEOBJECT lpObject = (LPOLEOBJECT)pThis->GetInterface(&IID_IOleObject);
	ASSERT(lpObject != NULL);
	CLSID clsid;
	lpObject->GetUserClassID(&clsid);

	return OleRegEnumVerbs(clsid, ppenumOleVerb);
}

STDMETHODIMP COleServerDoc::XOleObject::Update()
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	COleServerItem* pItem;
	SCODE sc = S_OK;
	TRY
	{
		// delegate through item
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		pItem->OnUpdateItems();
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::IsUpToDate()
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	COleServerItem* pItem;
	SCODE sc;
	TRY
	{
		// delegate through item
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);

		sc = pItem->OnQueryUpdateItems() ? S_FALSE : S_OK;
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::GetUserClassID(CLSID* lpClassID)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	LPPERSISTFILE lpPersistFile = (LPPERSISTFILE)
		pThis->GetInterface(&IID_IPersistFile);
	return lpPersistFile->GetClassID(lpClassID);
}

STDMETHODIMP COleServerDoc::XOleObject::GetUserType(
	DWORD dwFormOfType, LPOLESTR* ppszUserType)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	*ppszUserType = NULL;

	LPOLEOBJECT lpObject = (LPOLEOBJECT)pThis->GetInterface(&IID_IOleObject);
	ASSERT(lpObject != NULL);
	CLSID clsid;
	lpObject->GetUserClassID(&clsid);

	return OleRegGetUserType(clsid, dwFormOfType, ppszUserType);
}

STDMETHODIMP COleServerDoc::XOleObject::SetExtent(
	DWORD dwDrawAspect, LPSIZEL lpsizel)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	COleServerItem* pItem;
	SCODE sc = E_FAIL;
	TRY
	{
		// convert rectangle to a CSize and call item OnSetExtent
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);

		CSize size((int)lpsizel->cx, (int)lpsizel->cy);
		if (pItem->OnSetExtent((DVASPECT)dwDrawAspect, size))
			sc = S_OK;
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::GetExtent(
	DWORD dwDrawAspect, LPSIZEL lpsizel)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	COleServerItem* pItem;
	SCODE sc = E_INVALIDARG;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);

		// call to get regular windows CSize
		CSize size;
		if (pItem->OnGetExtent((DVASPECT)dwDrawAspect, size))
		{
			if (size.cy < 0)
				size.cy = -size.cy; // extents are always positive
			lpsizel->cx = size.cx;
			lpsizel->cy = size.cy;

			sc = S_OK;
		}
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	return sc;
}

STDMETHODIMP COleServerDoc::XOleObject::Advise(
	IAdviseSink* pAdvSink, DWORD* pdwConnection)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetOleObject()->Advise(pAdvSink, pdwConnection);
}

STDMETHODIMP COleServerDoc::XOleObject::Unadvise(DWORD dwConnection)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetOleObject()->Unadvise(dwConnection);
}

STDMETHODIMP COleServerDoc::XOleObject::EnumAdvise(
	LPENUMSTATDATA* ppenumAdvise)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetOleObject()->EnumAdvise(ppenumAdvise);
}

STDMETHODIMP COleServerDoc::XOleObject::GetMiscStatus(
	DWORD dwAspect, DWORD* pdwStatus)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleObject)

	*pdwStatus = 0;

	LPOLEOBJECT lpObject = (LPOLEOBJECT)pThis->GetInterface(&IID_IOleObject);
	ASSERT(lpObject != NULL);
	CLSID clsid;
	lpObject->GetUserClassID(&clsid);

	return OleRegGetMiscStatus(clsid, dwAspect, pdwStatus);
}

STDMETHODIMP COleServerDoc::XOleObject::SetColorScheme(LPLOGPALETTE lpLogpal)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleObject)
	ASSERT_VALID(pThis);

	COleServerItem* pItem;
	SCODE sc = E_NOTIMPL;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);

		// delegate to embedded item
		if (pItem->OnSetColorScheme(lpLogpal))
			sc = S_OK;
	}
	END_TRY

	return sc;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc::XDataObject

STDMETHODIMP_(ULONG) COleServerDoc::XDataObject::AddRef()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleServerDoc::XDataObject::Release()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleServerDoc::XDataObject::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleServerDoc::XDataObject::GetData(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->GetData(lpFormatEtc, lpStgMedium);
}

STDMETHODIMP COleServerDoc::XDataObject::GetDataHere(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->GetDataHere(lpFormatEtc, lpStgMedium);
}

STDMETHODIMP COleServerDoc::XDataObject::QueryGetData(
	LPFORMATETC lpFormatEtc)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->QueryGetData(lpFormatEtc);
}

STDMETHODIMP COleServerDoc::XDataObject::GetCanonicalFormatEtc(
	LPFORMATETC /*lpFormatEtcIn*/, LPFORMATETC /*lpFormatEtcOut*/)
{
	// because we support the target-device (ptd) for server metafile format,
	//  all members of the FORMATETC are significant.

	return DATA_S_SAMEFORMATETC;
}

STDMETHODIMP COleServerDoc::XDataObject::SetData(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium, BOOL bRelease)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->SetData(lpFormatEtc, lpStgMedium, bRelease);
}

STDMETHODIMP COleServerDoc::XDataObject::EnumFormatEtc(
	DWORD dwDirection, LPENUMFORMATETC* ppenumFormatEtc)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->EnumFormatEtc(dwDirection, ppenumFormatEtc);
}

STDMETHODIMP COleServerDoc::XDataObject::DAdvise(
	FORMATETC* pFormatetc, DWORD advf,
	LPADVISESINK pAdvSink, DWORD* pdwConnection)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->DAdvise(pFormatetc, advf, pAdvSink,
		pdwConnection);
}

STDMETHODIMP COleServerDoc::XDataObject::DUnadvise(DWORD dwConnection)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->DUnadvise(dwConnection);
}

STDMETHODIMP COleServerDoc::XDataObject::EnumDAdvise(
	LPENUMSTATDATA* ppenumAdvise)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, DataObject)

	COleServerItem* pItem;
	SCODE sc = E_OUTOFMEMORY;
	TRY
	{
		pItem = pThis->GetEmbeddedItem();
		ASSERT_VALID(pItem);
		ASSERT_KINDOF(COleServerItem, pItem);
		sc = S_OK;
	}
	END_TRY

	if (sc != S_OK)
		return sc;

	return pItem->GetDataObject()->EnumDAdvise(ppenumAdvise);
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc::COleInPlaceObject

STDMETHODIMP_(ULONG) COleServerDoc::XOleInPlaceObject::AddRef()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceObject)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleServerDoc::XOleInPlaceObject::Release()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceObject)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleServerDoc::XOleInPlaceObject::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceObject)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleServerDoc::XOleInPlaceObject::GetWindow(HWND* lphwnd)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceObject)

	LPOLEINPLACEACTIVEOBJECT lpActiveObject = (LPOLEINPLACEACTIVEOBJECT)
		pThis->GetInterface(&IID_IOleInPlaceActiveObject);
	return lpActiveObject->GetWindow(lphwnd);
}

STDMETHODIMP COleServerDoc::XOleInPlaceObject::ContextSensitiveHelp(
	BOOL fEnterMode)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceObject)

	LPOLEINPLACEACTIVEOBJECT lpActiveObject = (LPOLEINPLACEACTIVEOBJECT)
		pThis->GetInterface(&IID_IOleInPlaceActiveObject);
	return lpActiveObject->ContextSensitiveHelp(fEnterMode);
}

STDMETHODIMP COleServerDoc::XOleInPlaceObject::InPlaceDeactivate()
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceObject)
	ASSERT_VALID(pThis);

	pThis->InternalAddRef();    // protect this object

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		// only call deactivate if necessary
		if (pThis->m_pInPlaceFrame != NULL)
			pThis->OnDeactivate();

		// should be completely inactive
		ASSERT(pThis->m_pInPlaceFrame == NULL);
		sc = S_OK;
	}
	END_TRY

	pThis->InternalRelease();   // may 'delete this'
	return sc;
}

STDMETHODIMP COleServerDoc::XOleInPlaceObject::UIDeactivate()
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceObject)
	ASSERT_VALID(pThis);

	pThis->InternalAddRef();    // protect this object

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		// only call OnUIDeactivate if necessary
		if (pThis->m_pInPlaceFrame != NULL &&
			pThis->m_pInPlaceFrame->m_bUIActive)
		{
			pThis->OnDeactivateUI(FALSE);   // default to not undoable
		}

		// should not be ui active
		ASSERT(pThis->m_pInPlaceFrame == NULL ||
			!pThis->m_pInPlaceFrame->m_bUIActive);
		sc = S_OK;
	}
	END_TRY

	pThis->InternalRelease();   // may 'delete this'
	return sc;
}

STDMETHODIMP COleServerDoc::XOleInPlaceObject::SetObjectRects(
	LPCRECT lpPosRect, LPCRECT lpClipRect)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceObject)
	ASSERT_VALID(pThis);

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		pThis->OnSetItemRects(lpPosRect, lpClipRect);
		sc = S_OK;
	}
	END_TRY

	return sc;
}

STDMETHODIMP COleServerDoc::XOleInPlaceObject::ReactivateAndUndo()
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceObject)
	ASSERT_VALID(pThis);

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		sc = pThis->OnReactivateAndUndo() ? S_OK : INPLACE_E_NOTUNDOABLE;
	}
	END_TRY

	return sc;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerDoc::XOleInPlaceActiveObject

STDMETHODIMP_(ULONG) COleServerDoc::XOleInPlaceActiveObject::AddRef()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceActiveObject)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleServerDoc::XOleInPlaceActiveObject::Release()
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceActiveObject)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceActiveObject)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::GetWindow(
	HWND* lphwnd)
{
	METHOD_PROLOGUE_EX_(COleServerDoc, OleInPlaceActiveObject)

	*lphwnd = pThis->m_pInPlaceFrame->GetSafeHwnd();
	return *lphwnd != NULL ? S_OK : E_FAIL;
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::ContextSensitiveHelp(
	BOOL fEnterMode)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceActiveObject)
	ASSERT_VALID(pThis);

	if (fEnterMode)
	{
		if (!pThis->m_pInPlaceFrame->m_bHelpMode)
		{
			// check if help mode probable
			if (!pThis->m_pInPlaceFrame->CanEnterHelpMode())
				return E_UNEXPECTED;

			// attempt to enter context help
			if (!pThis->m_pInPlaceFrame->PostMessage(WM_COMMAND, ID_CONTEXT_HELP))
				return E_UNEXPECTED;
		}
	}
	else
	{
		// just exit help mode
		pThis->m_pInPlaceFrame->ExitHelpMode();
	}

	return S_OK;
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::TranslateAccelerator(
	LPMSG lpmsg)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceActiveObject)
	ASSERT_VALID(pThis);

	pThis->InternalAddRef();    // protect this object

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		// get frame window for this document
		CFrameWnd* pFrameWnd = pThis->m_pInPlaceFrame;
		ASSERT_VALID(pFrameWnd);

		// attempt translate accelerator
		MSG msg = *lpmsg;
		sc = pFrameWnd->PreTranslateMessage(&msg) ? S_OK : S_FALSE;
		*lpmsg = msg;
	}
	END_TRY

	pThis->InternalRelease();   // may 'delete this'

	return sc;
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::OnFrameWindowActivate(
	BOOL fActivate)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceActiveObject)
	ASSERT_VALID(pThis);

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		pThis->OnFrameWindowActivate(fActivate);
		sc = S_OK;
	}
	END_TRY

	return sc;
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::OnDocWindowActivate(
	BOOL fActivate)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceActiveObject)
	ASSERT_VALID(pThis);

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		pThis->OnDocWindowActivate(fActivate);
		sc = S_OK;
	}
	END_TRY

	return sc;
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::ResizeBorder(
	LPCRECT lprectBorder, LPOLEINPLACEUIWINDOW lpUIWindow, BOOL fFrameWindow)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceActiveObject)
	ASSERT_VALID(pThis);

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		pThis->OnResizeBorder(lprectBorder, lpUIWindow, fFrameWindow);
		sc = S_OK;
	}
	END_TRY

	return sc;
}

STDMETHODIMP COleServerDoc::XOleInPlaceActiveObject::EnableModeless(
	BOOL fEnable)
{
	METHOD_PROLOGUE_EX(COleServerDoc, OleInPlaceActiveObject)
	ASSERT_VALID(pThis);

	SCODE sc = E_UNEXPECTED;
	TRY
	{
		if (!fEnable)
		{
			// start modal state if not in modal state
			if (!pThis->m_pInPlaceFrame->InModalState())
			{
				CWnd* pTemp = pThis->m_pInPlaceFrame->GetTopLevelParent();
				BOOL bEnabled = pTemp->IsWindowEnabled();
				pThis->m_pInPlaceFrame->BeginModalState();
				pTemp->EnableWindow(bEnabled);
			}
		}
		else
		{
			// end modal state if in modal state
			if (pThis->m_pInPlaceFrame->InModalState())
				pThis->m_pInPlaceFrame->EndModalState();
		}
		sc = S_OK;
	}
	END_TRY

	return sc;
}

//////////////////////////////////////////////////////////////////////////////
// Diagnostics

#ifdef _DEBUG
void COleServerDoc::AssertValid() const
{
	COleLinkingDoc::AssertValid();
	if (m_pInPlaceFrame != NULL)
		m_pInPlaceFrame->AssertValid();
	if (m_pOrigParent != NULL)
		m_pOrigParent->AssertValid();
}

void COleServerDoc::Dump(CDumpContext& dc) const
{
	COleLinkingDoc::Dump(dc);

	if (dc.GetDepth() != 0)
	{
		if (m_pInPlaceFrame != NULL)
			dc << "\nwith in-place frame: " << m_pInPlaceFrame;
		else
			dc << "\nwith no in-place frame\n";
		if (m_pOrigParent != NULL)
			dc << "\nwith original parent: " << m_pOrigParent;
		else
			dc << "with no original parent\n";
	}
	dc << "m_lpClientSite = " << m_lpClientSite;
	dc << "\nm_strHostObj = " << m_strHostObj;
	dc << "\nm_bCntrVisible = " << m_bCntrVisible;
	dc << "\nm_dwOrigStyle = " << m_dwOrigStyle;

	dc << "\n";
}
#endif //_DEBUG

//////////////////////////////////////////////////////////////////////////////
// Inline function declarations expanded out-of-line

#ifndef _AFX_ENABLE_INLINES

// expand inlines for OLE server APIs
static char _szAfxOleInl[] = "afxole.inl";
#undef THIS_FILE
#define THIS_FILE _szAfxOleInl
#define _AFXOLESVR_INLINE
#include "afxole.inl"

#endif //!_AFX_ENABLE_INLINES

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

IMPLEMENT_DYNAMIC(COleServerDoc, COleLinkingDoc)

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