// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 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"

#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

//////////////////////////////////////////////////////////////////////////////
// COleServerItem implementation

COleServerItem::COleServerItem(COleServerDoc* pServerDoc, BOOL bAutoDelete)
{
	if (pServerDoc != NULL)
		ASSERT_VALID(pServerDoc);

	m_dwRef = 0;    // always start in disconnected state
	m_bAutoDelete = bAutoDelete;
	m_bNeedUnlock = FALSE;

	// initially, item does not have an extent
	m_sizeExtent.cx = 0;
	m_sizeExtent.cy = 0;

	// initialize advise holders
	m_lpOleAdviseHolder = NULL;
	m_lpDataAdviseHolder = NULL;

	// add presentation formats to the data source
	m_dataSource.m_nGrowBy = 1;
	FORMATETC formatEtc;
	formatEtc.ptd = NULL;
	formatEtc.dwAspect = DVASPECT_CONTENT;
	formatEtc.lindex = -1;

	// by default, a COleServerItem supports CF_METAFILEPICT
	formatEtc.cfFormat = CF_METAFILEPICT;
	formatEtc.tymed = TYMED_MFPICT;
	m_dataSource.DelayRenderData(0, &formatEtc);

	// add item to server document
	m_pDocument = NULL;
	if (pServerDoc != NULL)
		pServerDoc->AddItem(this);
	ASSERT(m_pDocument == pServerDoc);

	AfxOleLockApp();
}

COleServerItem::~COleServerItem()
{
	m_bAutoDelete = FALSE;  // no delete during destructor

	// release any advise holders
	RELEASE(m_lpOleAdviseHolder);
	RELEASE(m_lpDataAdviseHolder);

	ExternalDisconnect();

	// disconnect from the document
	COleServerDoc* pDoc = GetDocument();
	if (pDoc != NULL)
	{
		// remove external lock from it
		if (m_bNeedUnlock)
		{
			pDoc->LockExternal(FALSE, TRUE);
			m_bNeedUnlock = FALSE;
		}

		// reset m_pEmbeddedItem if destroying embedded item
		if (pDoc->m_pEmbeddedItem == this)
			pDoc->m_pEmbeddedItem = NULL;

		// remove from list
		pDoc->RemoveItem(this);
	}

	// cleanup any references
	AfxOleUnlockApp();
}

BOOL COleServerItem::IsBlank() const
{
	// server items are blank in order to keep them from serializing when
	//  COleDocument::Serialize is called.

	return TRUE;
}

BOOL COleServerItem::IsConnected() const
{
	// if item is connected in any way, return TRUE
	if (m_dwRef != 0)
		return TRUE;

	// otherwise check if embedded item and document is connected
	if (!IsLinkedItem() && GetDocument()->m_lpClientSite != NULL)
		return TRUE;

	return FALSE;   // not connected
}

void COleServerItem::NotifyClient(OLE_NOTIFICATION nCode, DWORD dwParam)
{
	switch (nCode)
	{
	// IDataObject notifications
	case OLE_CHANGED:
		if (m_lpDataAdviseHolder != NULL)
			m_lpDataAdviseHolder->SendOnDataChange(GetDataObject(), dwParam, 0);
		break;

	// IOleObject notifications
	case OLE_SAVED:
		if (m_lpOleAdviseHolder != NULL)
			m_lpOleAdviseHolder->SendOnSave();
		break;
	case OLE_CLOSED:
		if (m_lpOleAdviseHolder != NULL)
			m_lpOleAdviseHolder->SendOnClose();
		break;
	case OLE_RENAMED:
		if (m_lpOleAdviseHolder != NULL)
		{
			// Note: the moniker should already be updated for this to work
			LPMONIKER lpMoniker = (LPMONIKER)dwParam;
			m_lpOleAdviseHolder->SendOnRename(lpMoniker);
		}
		break;

	default:
		ASSERT(FALSE);
	}
}

/////////////////////////////////////////////////////////////////////////////
// Helpers for getting commonly used interfaces through interface map

LPDATAOBJECT COleServerItem::GetDataObject()
{
	LPDATAOBJECT lpDataObject =
		(LPDATAOBJECT)GetInterface(&IID_IDataObject);
	ASSERT(lpDataObject != NULL);
	return lpDataObject;
}

LPOLEOBJECT COleServerItem::GetOleObject()
{
	LPOLEOBJECT lpOleObject =
		(LPOLEOBJECT)GetInterface(&IID_IOleObject);
	ASSERT(lpOleObject != NULL);
	return lpOleObject;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerItem overrides

BOOL COleServerItem::OnQueryUpdateItems()
{
	COleDocument* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// update all of the embedded objects
	POSITION pos = pDoc->GetStartPosition();
	COleClientItem* pItem;
	while ((pItem = pDoc->GetNextClientItem(pos)) != NULL)
	{
		// if any item is out-of-date, then this item is out-of-date
		if (pItem->m_lpObject->IsUpToDate() != NULL)
			return TRUE;    // update needed
	}
	return FALSE;   // update not needed
}

void COleServerItem::OnUpdateItems()
{
	COleDocument* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// update all of the embedded objects
	POSITION pos = pDoc->GetStartPosition();
	COleClientItem* pItem;
	while ((pItem = pDoc->GetNextClientItem(pos)) != NULL)
	{
		// update any out-of-date item
		if (pItem->m_lpObject->IsUpToDate() != NULL)
			pItem->m_lpObject->Update();
	}
}

BOOL COleServerItem::OnSetExtent(DVASPECT dwDrawAspect, const CSize& size)
{
	ASSERT_VALID(this);

	if (dwDrawAspect == DVASPECT_CONTENT)
	{
		m_sizeExtent = size;    // simply remember the extent
		return TRUE;
	}
	return FALSE;   // not implemented for that dwDrawAspect
}

BOOL COleServerItem::OnGetExtent(DVASPECT /*dwDrawAspect*/, CSize& rSize)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(&rSize, sizeof(CSize)));

	// the default implementation doesn't know what the extent is

	rSize.cx = 0;
	rSize.cy = 0;

	return FALSE;
}

void COleServerItem::OnDoVerb(LONG iVerb)
{
	switch (iVerb)
	{
	// open - maps to OnOpen
	case OLEIVERB_OPEN:
	case -OLEIVERB_OPEN-1:  // allows positive OLEIVERB_OPEN-1 in registry
		OnOpen();
		break;

	// primary, show, and unknown map to OnShow
	case OLEIVERB_PRIMARY:  // OLEIVERB_PRIMARY is 0 and "Edit" in registry
	case OLEIVERB_SHOW:
		OnShow();
		break;

	// hide maps to OnHide
	case OLEIVERB_HIDE:
	case -OLEIVERB_HIDE-1:  // allows positive OLEIVERB_HIDE-1 in registry
		OnHide();
		break;

	default:
		// negative verbs not understood should return E_NOTIMPL
		if (iVerb < 0)
			AfxThrowOleException(E_NOTIMPL);

		// positive verb not processed --
		//  according to OLE spec, primary verb should be executed
		//  instead.
		OnDoVerb(OLEIVERB_PRIMARY);

		// also, OLEOBJ_S_INVALIDVERB should be returned.
		AfxThrowOleException(OLEOBJ_S_INVALIDVERB);
	}
}

BOOL COleServerItem::OnDrawEx(CDC* pDC, DVASPECT nDrawAspect, CSize& rSize)
{
	ASSERT_VALID(pDC);
	ASSERT(AfxIsValidAddress(&rSize, sizeof(CSize)));

	if (nDrawAspect != DVASPECT_CONTENT)
		return FALSE;

	return OnDraw(pDC, rSize);
}

void COleServerItem::OnShow()
{
	ASSERT_VALID(this);

	// attempt in place activation (if not supported, fall back on "Open")
	COleServerDoc* pDoc = GetDocument();
	if (!pDoc->ActivateInPlace())
	{
		// by default OnShow() maps to OnOpen() if in-place activation
		//  not supported
		OnOpen();
	}
}

void COleServerItem::OnOpen()
{
	ASSERT_VALID(this);

	// default implementation shows the document
	COleServerDoc* pDoc = GetDocument();
	ASSERT(pDoc != NULL);
	pDoc->OnShowDocument(TRUE);
}

void COleServerItem::OnHide()
{
	ASSERT_VALID(this);

	// default implementation hides the document
	COleServerDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	pDoc->OnShowDocument(FALSE);
}

BOOL COleServerItem::GetMetafileData(LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
	ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));
	ASSERT(lpStgMedium->tymed == TYMED_NULL);   // GetDataHere not valid
	ASSERT(lpStgMedium->pUnkForRelease == NULL);

	// medium must be TYMED_MFPICT -- cannot fill in existing HGLOBAL
	if (!(lpFormatEtc->tymed & TYMED_MFPICT) || lpStgMedium->hGlobal != NULL)
		return FALSE;

	// create appropriate memory metafile DC
	CMetaFileDC dc;
	if (!dc.Create())
		return FALSE;

	// create attribute DC according to lpFormatEtc->ptd
	HDC hAttribDC = _AfxOleCreateDC(lpFormatEtc->ptd);
	if (hAttribDC == NULL)
		return FALSE;
	dc.SetAttribDC(hAttribDC);

	// Paint directly into the metafile.
	CSize size(0, 0);
	BOOL bResult = OnDrawEx(&dc, (DVASPECT)lpFormatEtc->dwAspect, size);

	// attribute DC is no longer necessary
	dc.SetAttribDC(NULL);
	::DeleteDC(hAttribDC);

	if (!bResult)
	{
#ifdef _DEBUG
		if (afxTraceFlags & traceOle)
			TRACE0("calling COleServerItem::OnDrawEx()failed.\n");
#endif
		return FALSE;
	}

	HMETAFILE hMF = dc.Close();
	if (hMF == NULL)
		return FALSE;

	HGLOBAL hPict;
	if ((hPict =
		::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, sizeof(METAFILEPICT))) == NULL)
	{
		DeleteMetaFile(hMF);
		return FALSE;
	}
	LPMETAFILEPICT lpPict;
	if ((lpPict = (LPMETAFILEPICT)::GlobalLock(hPict)) == NULL)
	{
		DeleteMetaFile(hMF);
		::GlobalFree(hPict);
		return FALSE;
	}

	// set the metafile size
	lpPict->mm = MM_ANISOTROPIC;
	lpPict->hMF = hMF;
	if (size.cx == 0 && size.cy == 0 &&
		!OnGetExtent((DVASPECT)lpFormatEtc->dwAspect, size))
	{
		TRACE0("Warning: OnGetExtent failed during OnDrawEx --\n");
		TRACE0("\tpresentation metafile may be badly formed!\n");
	}
	lpPict->xExt = size.cx;
	lpPict->yExt = size.cy;  // HIMETRIC height
	if (lpPict->yExt < 0)
	{
		TRACE0("Warning: HIMETRIC natural size is negative.\n");
		lpPict->yExt = -lpPict->yExt;   // backward compatibility fix
	}

#ifdef _DEBUG
	if (lpPict->xExt == 0 || lpPict->yExt == 0)
	{
		// usually the natural extent is set to something interesting
		TRACE0("Warning: COleServerItem has no natural size --\n");
		TRACE0("\twill not work with some apps like MS Write.\n");
	}
#endif

	// return the medium with the hGlobal to the METAFILEPICT
	::GlobalUnlock(hPict);
	lpStgMedium->hGlobal = hPict;
	lpStgMedium->tymed = TYMED_MFPICT;
	return TRUE;
}

BOOL COleServerItem::OnSetColorScheme(const LOGPALETTE* /*lpLogPalette*/)
{
	ASSERT_VALID(this);

	return FALSE;   // default does nothing
}

BOOL COleServerItem::OnInitFromData(
	COleDataObject* /*pDataObject*/, BOOL /*bCreation*/)
{
	ASSERT_VALID(this);

	AfxThrowOleException(E_NOTIMPL);
	return FALSE;
}

void COleServerItem::CopyToClipboard(BOOL bIncludeLink)
{
	ASSERT_VALID(this);

	COleDataSource* pDataSource = OnGetClipboardData(bIncludeLink, NULL, NULL);

	// put it on the clipboard
	pDataSource->SetClipboard();
}

COleDataSource* COleServerItem::OnGetClipboardData(BOOL bIncludeLink,
	LPPOINT lpOffset, LPSIZE lpSize)
{
	ASSERT_VALID(this);

	COleDataSource* pDataSource = new COleDataSource;
	TRY
	{
		GetClipboardData(pDataSource, bIncludeLink, lpOffset, lpSize);
	}
	CATCH_ALL(e)
	{
		delete pDataSource;
		THROW_LAST();
	}
	END_CATCH_ALL

	ASSERT_VALID(pDataSource);
	return pDataSource;
}

DROPEFFECT COleServerItem::DoDragDrop(LPCRECT lpItemRect, CPoint ptOffset,
	BOOL bIncludeLink, DWORD dwEffects, LPCRECT lpRectStartDrag)
{
	ASSERT(AfxIsValidAddress(lpItemRect, sizeof(RECT)));
	ASSERT_VALID(this);

	ASSERT_VALID(this);

	DROPEFFECT dropEffect = DROPEFFECT_NONE;
	COleDataSource *pDataSource = NULL;
	TRY
	{
		// get clipboard data for this item
		CSize sizeItem(
			lpItemRect->right - lpItemRect->left,
			lpItemRect->bottom - lpItemRect->top);
		pDataSource = OnGetClipboardData(bIncludeLink, &ptOffset, &sizeItem);

		// add DROPEFFECT_LINK if link source is available
		LPDATAOBJECT lpDataObject = (LPDATAOBJECT)
			pDataSource->GetInterface(&IID_IDataObject);
		ASSERT(lpDataObject != NULL);
		FORMATETC formatEtc;
		formatEtc.cfFormat = (CLIPFORMAT)_oleData.cfLinkSource;
		formatEtc.ptd = NULL;
		formatEtc.dwAspect = DVASPECT_CONTENT;
		formatEtc.lindex = -1;
		formatEtc.tymed = (DWORD) -1;
		if (lpDataObject->QueryGetData(&formatEtc) == S_OK)
			dwEffects |= DROPEFFECT_LINK;

		// calculate default sensitivity rectangle
		CRect rectDrag;
		if (lpRectStartDrag == NULL)
		{
			rectDrag.SetRect(lpItemRect->left, lpItemRect->top, lpItemRect->left,
				lpItemRect->top);
			lpRectStartDrag = &rectDrag;
		}

		// do drag drop operation
		dropEffect = pDataSource->DoDragDrop(dwEffects, lpRectStartDrag);
		pDataSource->InternalRelease();
	}
	CATCH_ALL(e)
	{
		if (pDataSource != NULL)
			pDataSource->InternalRelease();

		THROW_LAST();
	}
	END_CATCH_ALL

	return dropEffect;
}

void COleServerItem::GetClipboardData(COleDataSource* pDataSource,
	BOOL bIncludeLink, LPPOINT lpOffset, LPSIZE lpSize)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDataSource);
	ASSERT(lpOffset == NULL ||
		AfxIsValidAddress(lpOffset, sizeof(POINT), FALSE));

	// add CF_EMBEDDEDOBJECT by creating memory storage copy of the object
	STGMEDIUM stgMedium;
	GetEmbedSourceData(&stgMedium);
	pDataSource->CacheData((CLIPFORMAT)_oleData.cfEmbedSource, &stgMedium);

	// add CF_OBJECTDESCRIPTOR
	GetObjectDescriptorData(lpOffset, lpSize, &stgMedium);
	pDataSource->CacheData((CLIPFORMAT)_oleData.cfObjectDescriptor,
		&stgMedium);

	// add any presentation entries/conversion formats that the item
	//  can produce.
	AddOtherClipboardData(pDataSource);

	// add CF_LINKSOURCE if supporting links to pseudo objects
	if (bIncludeLink && GetLinkSourceData(&stgMedium))
	{
		pDataSource->CacheData((CLIPFORMAT)_oleData.cfLinkSource, &stgMedium);

		// add CF_LINKSOURCEDESCRIPTOR
		GetObjectDescriptorData(lpOffset, lpSize, &stgMedium);
		pDataSource->CacheData((CLIPFORMAT)_oleData.cfLinkSourceDescriptor,
			&stgMedium);
	}
}

void COleServerItem::GetEmbedSourceData(LPSTGMEDIUM lpStgMedium)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));

	LPLOCKBYTES lpLockBytes;
	SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
	if (sc != S_OK)
		AfxThrowOleException(sc);
	ASSERT(lpLockBytes != NULL);

	LPSTORAGE lpStorage;
	sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
		STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &lpStorage);
	if (sc != S_OK)
	{
		VERIFY(lpLockBytes->Release() == 0);
		AfxThrowOleException(sc);
	}
	ASSERT(lpStorage != NULL);

	// setup for save copy as
	COleServerDoc* pDoc = GetDocument();
	pDoc->m_bSameAsLoad = FALSE;
	pDoc->m_bRemember = FALSE;

	TRY
	{
		OnSaveEmbedding(lpStorage);
		pDoc->CommitItems(FALSE);
	}
	CATCH_ALL(e)
	{
		// release storage and lock bytes
		VERIFY(lpStorage->Release() == 0);
		VERIFY(lpLockBytes->Release() == 0);
		pDoc->m_bSameAsLoad = TRUE;
		pDoc->m_bRemember = TRUE;
		THROW_LAST();
	}
	END_CATCH_ALL

	pDoc->m_bSameAsLoad = TRUE;
	pDoc->m_bRemember = TRUE;
	lpLockBytes->Release();

	// add it to the data source
	lpStgMedium->tymed = TYMED_ISTORAGE;
	lpStgMedium->pstg = lpStorage;
	lpStgMedium->pUnkForRelease = NULL;
}

void COleServerItem::AddOtherClipboardData(COleDataSource* pDataSource)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pDataSource);

	// get IEnumFORMATETC interface for the IDataObject
	LPDATAOBJECT lpDataObject = GetDataObject();
	LPENUMFORMATETC lpEnumFORMATETC;
	if (lpDataObject->EnumFormatEtc(DATADIR_GET, &lpEnumFORMATETC) != S_OK)
		return;
	ASSERT(lpEnumFORMATETC != NULL);

	// get all formats that the object will give us
	FORMATETC formatEtc;
	while (lpEnumFORMATETC->Next(1, &formatEtc, NULL) == S_OK)
	{
		STGMEDIUM stgMedium;
		if (lpDataObject->GetData(&formatEtc, &stgMedium) != S_OK)
		{
			// data is not available
			CoTaskMemFree(formatEtc.ptd);
		}
		else if (stgMedium.pUnkForRelease != NULL)
		{
			// don't cache data with pUnkForRelease != NULL
			::ReleaseStgMedium(&stgMedium);
			CoTaskMemFree(formatEtc.ptd);
		}
		else
		{
			// cache the data (now we own the stgMedium)
			pDataSource->CacheData(0, &stgMedium, &formatEtc);
		}
	}

	// cleanup
	lpEnumFORMATETC->Release();
}

LPMONIKER COleServerItem::GetMoniker(OLEGETMONIKER nAssign)
{
	// get IOleObject interface for this item
	LPOLEOBJECT lpOleObject = GetOleObject();
	ASSERT(lpOleObject != NULL);

	// get moniker from OLE object
	LPMONIKER lpMoniker = NULL;
	lpOleObject->GetMoniker(nAssign, OLEWHICHMK_OBJFULL, &lpMoniker);
	return lpMoniker;
}

BOOL COleServerItem::GetLinkSourceData(LPSTGMEDIUM lpStgMedium)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));

	LPOLEOBJECT lpOleObject = GetOleObject();
	ASSERT(lpOleObject != NULL);

	// get moniker from ole object
	LPMONIKER lpMoniker;
	SCODE sc = lpOleObject->GetMoniker(OLEGETMONIKER_TEMPFORUSER,
		OLEWHICHMK_OBJFULL, &lpMoniker);
	if (sc != S_OK)
	{
		TRACE0("Warning: unable to get moniker for object.\n");
		return FALSE;
	}
	ASSERT(lpMoniker != NULL);

	// create a memory based stream to write the moniker to
	LPSTREAM lpStream;
	if (::CreateStreamOnHGlobal(NULL, TRUE, &lpStream) != S_OK)
	{
		lpMoniker->Release();
		AfxThrowMemoryException();
	}
	ASSERT(lpStream != NULL);

	// write the moniker to the stream, and add it to the clipboard
	sc = ::OleSaveToStream(lpMoniker, lpStream);
	lpMoniker->Release();
	if (sc != S_OK)
	{
		lpStream->Release();
		AfxThrowOleException(sc);
	}

	// write the class ID of the document to the stream as well
	COleLinkingDoc* pDoc = GetDocument();
	ASSERT(pDoc->m_pFactory != NULL);
	sc = WriteClassStm(lpStream, pDoc->m_pFactory->GetClassID());
	if (sc != S_OK)
	{
		lpStream->Release();
		AfxThrowOleException(sc);
	}

	// setup the STGMEDIUM
	lpStgMedium->tymed = TYMED_ISTREAM;
	lpStgMedium->pstm = lpStream;
	lpStgMedium->pUnkForRelease = NULL;
	return TRUE;
}

void COleServerItem::GetObjectDescriptorData(
	LPPOINT lpOffset, LPSIZE lpSize, LPSTGMEDIUM lpStgMedium)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));
	ASSERT(lpOffset == NULL ||
		AfxIsValidAddress(lpOffset, sizeof(POINT), FALSE));

	LPOLEOBJECT lpOleObject = GetOleObject();
	ASSERT(lpOleObject != NULL);

	// get the object descriptor for the IOleObject
	POINTL pointl = { 0, 0 };
	if (lpOffset != NULL)
	{
		CSize ptOffset(lpOffset->x, lpOffset->y);
		((CDC*)NULL)->DPtoHIMETRIC(&ptOffset);
		pointl.x = ptOffset.cx;
		pointl.y = ptOffset.cy;
	}
	SIZEL sizel;
	if (lpSize != NULL)
	{
		sizel.cx = lpSize->cx;
		sizel.cy = lpSize->cy;
		((CDC*)NULL)->DPtoHIMETRIC(&sizel);
	}
	else
	{
		sizel.cx = 0;
		sizel.cy = 0;
	}

	InterlockedIncrement(&m_dwRef);  // protect against destruction during this call
	HGLOBAL hGlobal = _AfxOleGetObjectDescriptorData(
		lpOleObject, NULL, DVASPECT_CONTENT, pointl, &sizel);
	InterlockedDecrement(&m_dwRef);

	if (hGlobal == NULL)
		AfxThrowMemoryException();

	// setup the STGMEDIUM
	lpStgMedium->tymed = TYMED_HGLOBAL;
	lpStgMedium->hGlobal = hGlobal;
	lpStgMedium->pUnkForRelease = NULL;
}

void COleServerItem::OnSaveEmbedding(LPSTORAGE lpStorage)
{
	ASSERT(lpStorage != NULL);

	// always (logically) a "File.Save Copy As" operation
	COleServerDoc* pDoc = GetDocument();
	LPSTORAGE lpOrigStg = pDoc->m_lpRootStg;
	pDoc->m_lpRootStg = lpStorage;

	TRY
	{
		ASSERT(pDoc->m_lpRootStg != NULL);
		pDoc->SaveToStorage(this);  // use helper to serialize to storage
	}
	CATCH_ALL(e)
	{
		// save as failed: re-attach original storage
		pDoc->m_lpRootStg = lpOrigStg;
		THROW_LAST();
	}
	END_CATCH_ALL

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

/////////////////////////////////////////////////////////////////////////////
// COleServerItem data-object callback default implementation

BOOL COleServerItem::OnRenderGlobalData(
	LPFORMATETC /*lpFormatEtc*/, HGLOBAL* /*phGlobal*/)
{
	ASSERT_VALID(this);

	return FALSE;   // default does nothing
}

BOOL COleServerItem::OnRenderFileData(
	LPFORMATETC /*lpFormatEtc*/, CFile* /*pFile*/)
{
	ASSERT_VALID(this);

	return FALSE;   // default does nothing
}

BOOL COleServerItem::OnRenderData(LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
	ASSERT(AfxIsValidAddress(lpStgMedium, sizeof(STGMEDIUM)));

	// default implementation does not support extended layout
	if (lpFormatEtc->lindex != -1)
		return FALSE;

	// default implementation supports both types of metafiles
	if (lpFormatEtc->cfFormat == CF_METAFILEPICT)
		return GetMetafileData(lpFormatEtc, lpStgMedium);

	return FALSE;   // cfFormat not supported
}

BOOL COleServerItem::OnSetData(
	LPFORMATETC /*lpFormatEtc*/, LPSTGMEDIUM /*lpStgMedium*/, BOOL /*bRelease*/)
{
	ASSERT_VALID(this);

	return FALSE;   // default does nothing
}

/////////////////////////////////////////////////////////////////////////////
// COleServerItem OLE interface implementation

BEGIN_INTERFACE_MAP(COleServerItem, CDocItem)
	INTERFACE_PART(COleServerItem, IID_IOleObject, OleObject)
	INTERFACE_PART(COleServerItem, IID_IDataObject, DataObject)
END_INTERFACE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COleServerItem::XOleObject

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

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

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

// COleServerItem has special Release semantics.  In particular, the item
//  is only deleted from memory if m_bAutoDelete is TRUE.
//  Also, it unlocks the document if the reference count reaches zero.

void COleServerItem::OnFinalRelease()
{
	ASSERT_VALID(this);

	COleServerDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pDoc->InternalAddRef(); // make document stable

	// if connected to a document -- remove external lock from it
	if (m_bNeedUnlock)
	{
		pDoc->LockExternal(FALSE, TRUE);
		m_bNeedUnlock = FALSE;
	}

	// delete this item if no longer needed
	if (m_bAutoDelete)
		delete this;

	// release artificial reference (may destroy the document)
	pDoc->InternalRelease();
}

STDMETHODIMP COleServerItem::XOleObject::SetClientSite(
	LPOLECLIENTSITE /*pClientSite*/)
{
	// linked objects do not support SetClientSite

	return E_NOTIMPL;
}

STDMETHODIMP COleServerItem::XOleObject::GetClientSite(
	LPOLECLIENTSITE* ppClientSite)
{
	// linked objects do not support GetClientSite

	*ppClientSite = NULL;
	return E_NOTIMPL;
}

STDMETHODIMP COleServerItem::XOleObject::SetHostNames(
	LPCOLESTR /*szContainerApp*/, LPCOLESTR /*szContainerObj*/)
{
	// linked objects do not support SetHostNames

	return E_NOTIMPL;
}

STDMETHODIMP COleServerItem::XOleObject::Close(DWORD /*dwSaveOption*/)
{
	// linked objects do not support close

	return E_NOTIMPL;
}

STDMETHODIMP COleServerItem::XOleObject::SetMoniker(
	DWORD /*dwWhichMoniker*/, LPMONIKER /*pmk*/)
{
	// linked objects do not support SetMoniker

	return E_NOTIMPL;
}

STDMETHODIMP COleServerItem::XOleObject::GetMoniker(
	DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMoniker)
{
	USES_CONVERSION;

	METHOD_PROLOGUE_EX(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

	COleServerDoc* pDoc = pThis->GetDocument();
	ASSERT_VALID(pDoc);
	ASSERT_KINDOF(COleServerDoc, pDoc);
	ASSERT(ppMoniker != NULL);
	*ppMoniker = NULL;

	switch (dwWhichMoniker)
	{
	case OLEWHICHMK_CONTAINER:
		// simply return the moniker of the container document
		*ppMoniker = pDoc->GetMoniker((OLEGETMONIKER)dwAssign);
		break;

	case OLEWHICHMK_OBJREL:
		{
			// no relative moniker if no item name
			if (pThis->m_strItemName.IsEmpty())
				break;

			// don't return relative moniker if no document moniker
			LPMONIKER lpMoniker = pDoc->GetMoniker((OLEGETMONIKER)dwAssign);
			if (lpMoniker == NULL)
				break;
			lpMoniker->Release();   // don't need document moniker

			// relative monikers have to handle assignment correctly
			switch (dwAssign)
			{
			case OLEGETMONIKER_TEMPFORUSER:
			case OLEGETMONIKER_ONLYIFTHERE:
			case OLEGETMONIKER_FORCEASSIGN:
				// create item moniker from name
				CreateItemMoniker(OLESTDDELIMOLE, T2COLE(pThis->m_strItemName),
					ppMoniker);
				break;

			case OLEGETMONIKER_UNASSIGN:
				ASSERT(FALSE);  // should never get UNASSIGN
				break;
			}
		}
		break;

	case OLEWHICHMK_OBJFULL:
		{
			// get each sub-moniker: item & document
			LPMONIKER lpMoniker1, lpMoniker2;
			GetMoniker(dwAssign, OLEWHICHMK_CONTAINER, &lpMoniker1);
			GetMoniker(dwAssign, OLEWHICHMK_OBJREL, &lpMoniker2);

			if (lpMoniker1 != NULL && lpMoniker2 != NULL)
			{
				// create composite from two parts
				::CreateGenericComposite(lpMoniker1, lpMoniker2, ppMoniker);
			}
			else if (lpMoniker1 != NULL)
			{
				// just use container moniker
				*ppMoniker = lpMoniker1;
				lpMoniker1 = NULL;
			}

			// release sub-monikers
			RELEASE(lpMoniker1);
			RELEASE(lpMoniker2);
		}
		break;
	}

	return *ppMoniker == NULL ? E_FAIL : S_OK;
}

STDMETHODIMP COleServerItem::XOleObject::InitFromData(
	LPDATAOBJECT /*pDataObject*/, BOOL /*fCreation*/, DWORD /*dwReserved*/)
{
	// linked objects do not support InitFromData

	return E_NOTIMPL;
}

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

	*ppDataObject = NULL;

	SCODE sc;
	TRY
	{
		COleDataSource* pDataSource = pThis->OnGetClipboardData(TRUE, NULL, NULL);
		ASSERT(pDataSource != NULL);

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

	return sc;
}

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

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

	SCODE sc;
	TRY
	{
		pThis->OnDoVerb(iVerb);
		sc = S_OK;
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

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

	return sc;
}

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

	*ppenumOleVerb = NULL;

	CLSID clsid;
	pThis->GetOleObject()->GetUserClassID(&clsid);
	return OleRegEnumVerbs(clsid, ppenumOleVerb);
}

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

	SCODE sc;
	TRY
	{
		pThis->OnUpdateItems();
		sc = S_OK;
	}
	CATCH_ALL(e)
	{
		sc = COleException::Process(e);
		DELETE_EXCEPTION(e);
	}
	END_CATCH_ALL

	return sc;
}

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

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

	return sc;
}

STDMETHODIMP COleServerItem::XOleObject::GetUserClassID(CLSID* pClsid)
{
	METHOD_PROLOGUE_EX_(COleServerItem, OleObject)

	COleServerDoc* pDoc = pThis->GetDocument();
	return pDoc->m_xPersistFile.GetClassID(pClsid);
}

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

	*ppszUserType = NULL;

	CLSID clsid;
	pThis->GetOleObject()->GetUserClassID(&clsid);
	return OleRegGetUserType(clsid, dwFormOfType, ppszUserType);
}

STDMETHODIMP COleServerItem::XOleObject::SetExtent(
	DWORD /*dwDrawAspect*/, LPSIZEL /*lpsizel*/)
{
	// linked objects do not support SetExtent

	return E_FAIL;
}

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

	SCODE sc = E_INVALIDARG;
	TRY
	{
		// call to get regular windows size
		CSize size;
		if (pThis->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 COleServerItem::XOleObject::Advise(
	IAdviseSink* pAdvSink, DWORD* pdwConnection)
{
	METHOD_PROLOGUE_EX_(COleServerItem, OleObject)

	*pdwConnection = 0;

	if (pThis->m_lpOleAdviseHolder == NULL &&
		::CreateOleAdviseHolder(&pThis->m_lpOleAdviseHolder) != S_OK)
	{
		return E_OUTOFMEMORY;
	}
	ASSERT(pThis->m_lpOleAdviseHolder != NULL);
	return pThis->m_lpOleAdviseHolder->Advise(pAdvSink, pdwConnection);
}

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

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

	ASSERT(pThis->m_lpOleAdviseHolder != NULL);
	return pThis->m_lpOleAdviseHolder->Unadvise(dwConnection);
}

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

	*ppenumAdvise = NULL;

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

	ASSERT(pThis->m_lpOleAdviseHolder != NULL);
	return pThis->m_lpOleAdviseHolder->EnumAdvise(ppenumAdvise);
}

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

	*pdwStatus = 0;

	CLSID clsid;
	pThis->GetOleObject()->GetUserClassID(&clsid);
	return OleRegGetMiscStatus(clsid, dwAspect, pdwStatus);
}

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

	SCODE sc = E_NOTIMPL;
	TRY
	{
		// delegate to embedded item
		if (pThis->OnSetColorScheme(lpLogpal))
			sc = S_OK;
	}
	END_TRY

	return sc;
}

/////////////////////////////////////////////////////////////////////////////
// COleServerItem::XDataObject

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

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

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

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

	return pThis->m_dataSource.m_xDataObject.GetData(lpFormatEtc, lpStgMedium);
}

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

	return pThis->m_dataSource.m_xDataObject.GetDataHere(
		lpFormatEtc, lpStgMedium);
}

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

	return pThis->m_dataSource.m_xDataObject.QueryGetData(lpFormatEtc);
}

STDMETHODIMP COleServerItem::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 COleServerItem::XDataObject::SetData(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium, BOOL bRelease)
{
	METHOD_PROLOGUE_EX_(COleServerItem, DataObject)

	return pThis->m_dataSource.m_xDataObject.SetData(
		lpFormatEtc, lpStgMedium, bRelease);
}

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

	return pThis->m_dataSource.m_xDataObject.EnumFormatEtc(
		dwDirection, ppenumFormatEtc);
}

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

	*pdwConnection = 0;

	// this special case is for apps like Excel which ask for DAdvise
	// on CF_METAFILEPICT, DVASPECT_ICON for insert as icon.
	FORMATETC formatEtc = *pFormatEtc;
	if (formatEtc.cfFormat == CF_METAFILEPICT &&
		formatEtc.dwAspect == DVASPECT_ICON)
	{
		formatEtc.dwAspect = DVASPECT_CONTENT;
	}

	// make sure the FORMATETC is valid
	if (!(pFormatEtc->cfFormat == 0 && pFormatEtc->ptd == NULL &&
		  pFormatEtc->dwAspect == -1 && pFormatEtc->lindex == -1 &&
		  pFormatEtc->tymed == -1) &&
		pThis->GetDataObject()->QueryGetData(&formatEtc) != S_OK)
	{
		// it is not a wildcard advise -and- the format is not acceptable
		return DATA_E_FORMATETC;
	}

	// create the advise holder, if necessary
	if (pThis->m_lpDataAdviseHolder == NULL &&
		CreateDataAdviseHolder(&pThis->m_lpDataAdviseHolder) != S_OK)
	{
		return E_OUTOFMEMORY;
	}
	ASSERT(pThis->m_lpDataAdviseHolder != NULL);
	return pThis->m_lpDataAdviseHolder->Advise(this, pFormatEtc, advf,
		pAdvSink, pdwConnection);
}

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

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

	ASSERT(pThis->m_lpDataAdviseHolder != NULL);
	return pThis->m_lpDataAdviseHolder->Unadvise(dwConnection);
}

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

	*ppenumAdvise = NULL;

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

	ASSERT(pThis->m_lpDataAdviseHolder != NULL);
	return pThis->m_lpDataAdviseHolder->EnumAdvise(ppenumAdvise);
}

//////////////////////////////////////////////////////////////////////////////
// special CItemDataSource implementation

BOOL COleServerItem::CItemDataSource::OnRenderGlobalData(
	LPFORMATETC lpFormatEtc, HGLOBAL* phGlobal)
{
	ASSERT_VALID(this);
	COleServerItem* pItem = (COleServerItem*)
		((BYTE*)this - offsetof(COleServerItem, m_dataSource));

	return pItem->OnRenderGlobalData(lpFormatEtc, phGlobal);
		// Note: COleDataSource has no implementation
}

BOOL COleServerItem::CItemDataSource::OnRenderFileData(
	LPFORMATETC lpFormatEtc, CFile* pFile)
{
	ASSERT_VALID(this);
	COleServerItem* pItem = (COleServerItem*)
		((BYTE*)this - offsetof(COleServerItem, m_dataSource));

	return pItem->OnRenderFileData(lpFormatEtc, pFile);
		// Note: COleDataSource has no implementation
}

BOOL COleServerItem::CItemDataSource::OnRenderData(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
	ASSERT_VALID(this);
	COleServerItem* pItem = (COleServerItem*)
		((BYTE*)this - offsetof(COleServerItem, m_dataSource));

	if (pItem->OnRenderData(lpFormatEtc, lpStgMedium))
		return TRUE;

	return COleDataSource::OnRenderData(lpFormatEtc, lpStgMedium);
}

BOOL COleServerItem::CItemDataSource::OnSetData(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium, BOOL bRelease)
{
	ASSERT_VALID(this);
	COleServerItem* pItem = (COleServerItem*)
		((BYTE*)this - offsetof(COleServerItem, m_dataSource));

	return pItem->OnSetData(lpFormatEtc, lpStgMedium, bRelease);
		// Note: COleDataSource has no implementation
}

//////////////////////////////////////////////////////////////////////////////
// COleServerItem Diagnostics

#ifdef _DEBUG
void COleServerItem::AssertValid() const
{
	CDocItem::AssertValid();

	// must be attached to a document
	ASSERT(m_pDocument != NULL);
	m_dataSource.AssertValid();
}

void COleServerItem::Dump(CDumpContext& dc) const
{
	CDocItem::Dump(dc);

	dc << "m_bNeedUnlock = " << m_bNeedUnlock;
	dc << "\nm_bAutoDelete = " << m_bAutoDelete;
	dc << "\nm_strItemName = " << m_strItemName;
	dc << "\nm_lpOleAdviseHolder = " << m_lpOleAdviseHolder;
	dc << "\nm_lpDataAdviseHolder = " << m_lpDataAdviseHolder;
	dc << "\nwith m_dataSource: " << &m_dataSource;
}
#endif

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