// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1993 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and Microsoft
// QuickHelp and/or WinHelp 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 BASED_CODE 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;

	// by default a COleServerItem only supports CF_METAFILEPICT
	m_dataSource.m_nGrowBy = 1;
	FORMATETC formatEtc;
	formatEtc.cfFormat = CF_METAFILEPICT;
	formatEtc.ptd = NULL;
	formatEtc.dwAspect = DVASPECT_CONTENT;
	formatEtc.lindex = -1;
	formatEtc.tymed = TYMED_MFPICT;
	m_dataSource.DelayRenderData(0, &formatEtc);
	m_pDocument = NULL;

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

	AfxOleLockApp();
}

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

	// release any advise holders
	_AfxRelease((LPUNKNOWN*)&m_lpOleAdviseHolder);
	_AfxRelease((LPUNKNOWN*)&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 2.0 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 = ::OleStdCreateDC(lpFormatEtc->ptd);
	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)
	{
		OLE_TRACE0("calling COleServerItem::OnDrawEx()failed\n");
		return FALSE;
	}

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

	HGLOBAL hPict;
	if ((hPict = ::GlobalAlloc(GMEM_DDESHARE, 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"
			"\t-- presentation drawing may not work!\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"
			"   - will not work with some client 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 FAR* lpLogPalette)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpLogPalette, sizeof(LOGPALETTE)));

	return FALSE;   // default does nothing
}

BOOL COleServerItem::OnInitFromData(COleDataObject* pDataObject, BOOL bCreation)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(pDataObject, sizeof(COleDataObject)));

	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_VALID(this);

	// get clipboard data for this item
	CSize sizeItem(
		lpItemRect->right - lpItemRect->left,
		lpItemRect->bottom - lpItemRect->top);
	COleDataSource *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 = _oleData.cfLinkSource;
	formatEtc.ptd = NULL;
	formatEtc.dwAspect = DVASPECT_CONTENT;
	formatEtc.lindex = -1;
	formatEtc.tymed = -1;
	if (lpDataObject->QueryGetData(&formatEtc) == NOERROR)
		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 dropEffect = pDataSource->DoDragDrop(dwEffects, lpRectStartDrag);
	pDataSource->InternalRelease();
	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(_oleData.cfEmbedSource, &stgMedium);

	// add CF_OBJECTDESCRIPTOR
	GetObjectDescriptorData(lpOffset, lpSize, &stgMedium);
	pDataSource->CacheData(_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(_oleData.cfLinkSource, &stgMedium);

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

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

	LPLOCKBYTES lpLockBytes;
	HRESULT hr = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
	if (hr != NOERROR)
		AfxThrowOleException(hr);
	ASSERT(lpLockBytes != NULL);

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

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

	TRY
	{
		OnSaveEmbedding(lpStorage);
	}
	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;

	VERIFY(lpLockBytes->Release() != 0);

	// 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) != NOERROR)
		return;
	ASSERT(lpEnumFORMATETC != NULL);

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

	// cleanup
	lpEnumFORMATETC->Release();
}

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;
	HRESULT hr = lpOleObject->GetMoniker(OLEGETMONIKER_TEMPFORUSER,
		OLEWHICHMK_OBJFULL, &lpMoniker);
	if (hr != NOERROR)
	{
		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) != NOERROR)
	{
		lpMoniker->Release();
		AfxThrowMemoryException();
	}
	ASSERT(lpStream != NULL);

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

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

	// 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;
	}
	LPSIZEL lpsizel = NULL;
	SIZEL sizel;
	if (lpSize != NULL)
	{
		((CDC*)NULL)->DPtoHIMETRIC(lpSize);
		sizel.cx = lpSize->cx;
		sizel.cy = lpSize->cy;
		lpsizel = &sizel;
	}

	++m_dwRef;  // protect against destruction during this call
	HGLOBAL hGlobal = ::OleStdGetObjectDescriptorDataFromOleObject(
		lpOleObject, NULL, DVASPECT_CONTENT, pointl, lpsizel);
	--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);
	ASSERT(AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC), FALSE));
	ASSERT(AfxIsValidAddress(phGlobal, sizeof(HGLOBAL)));

	return FALSE;   // default does nothing
}

BOOL COleServerItem::OnRenderFileData(LPFORMATETC lpFormatEtc, CFile* pFile)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(lpFormatEtc, sizeof(FORMATETC)));
	ASSERT_VALID(pFile);

	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 only supports CF_METAFILEPICT
	if (lpFormatEtc->cfFormat == CF_METAFILEPICT)
		return GetMetafileData(lpFormatEtc, lpStgMedium);

	return FALSE;   // cfFormat not supported
}

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

	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(COleServerItem, OleObject)
	return (ULONG)pThis->ExternalAddRef();
}

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

STDMETHODIMP COleServerItem::XOleObject::QueryInterface(
	REFIID iid, LPVOID far* ppvObj)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	return (HRESULT)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 ResultFromScode(E_NOTIMPL);
}

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

	*ppClientSite = NULL;
	return ResultFromScode(E_NOTIMPL);
}

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

	return ResultFromScode(E_NOTIMPL);
}

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

	return ResultFromScode(E_NOTIMPL);
}

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

	return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP COleServerItem::XOleObject::GetMoniker(
	DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER FAR* ppMoniker)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

	COleServerDoc* pDoc = pThis->GetDocument();
	ASSERT_VALID(pDoc);
	ASSERT(pDoc->IsKindOf(RUNTIME_CLASS(COleServerDoc)));
	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(OLESTDDELIM,
					(LPSTR)(LPCSTR)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
			_AfxRelease((LPUNKNOWN*)&lpMoniker1);
			_AfxRelease((LPUNKNOWN*)&lpMoniker2);
		}
		break;
	}

	return *ppMoniker == NULL ? ResultFromScode(E_FAIL) : NOERROR;
}

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

	return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP COleServerItem::XOleObject::GetClipboardData(
	DWORD dwReserved, LPDATAOBJECT FAR* ppDataObject)
{
	METHOD_PROLOGUE(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);
	}
	END_CATCH_ALL

	return ResultFromScode(sc);
}

STDMETHODIMP COleServerItem::XOleObject::DoVerb(
	LONG iVerb, LPMSG lpmsg, LPOLECLIENTSITE pActiveSite, LONG lindex,
	HWND hwndParent, LPCRECT lpPosRect)
{
	METHOD_PROLOGUE(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);
	}
	END_CATCH_ALL

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

	return ResultFromScode(sc);
}

STDMETHODIMP COleServerItem::XOleObject::EnumVerbs(
	IEnumOLEVERB FAR* FAR* ppenumOleVerb)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

	*ppenumOleVerb = NULL;

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

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

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

	return ResultFromScode(sc);
}

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

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

	return ResultFromScode(sc);
}

STDMETHODIMP COleServerItem::XOleObject::GetUserClassID(CLSID FAR* pClsid)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

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

STDMETHODIMP COleServerItem::XOleObject::GetUserType(
	DWORD dwFormOfType, LPSTR FAR* ppszUserType)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

	*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 ResultFromScode(E_FAIL);
}

STDMETHODIMP COleServerItem::XOleObject::GetExtent(
	DWORD dwDrawAspect, LPSIZEL lpsizel)
{
	METHOD_PROLOGUE(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);
	}
	END_CATCH_ALL

	return ResultFromScode(sc);
}

STDMETHODIMP COleServerItem::XOleObject::Advise(
	IAdviseSink FAR* pAdvSink, DWORD FAR* pdwConnection)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

	*pdwConnection = 0;

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

STDMETHODIMP COleServerItem::XOleObject::Unadvise(DWORD dwConnection)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

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

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

STDMETHODIMP COleServerItem::XOleObject::EnumAdvise(
	LPENUMSTATDATA FAR* ppenumAdvise)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

	*ppenumAdvise = NULL;

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

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

STDMETHODIMP COleServerItem::XOleObject::GetMiscStatus(
	DWORD dwAspect, DWORD FAR* pdwStatus)
{
	METHOD_PROLOGUE(COleServerItem, OleObject)
	ASSERT_VALID(pThis);

	*pdwStatus = 0;

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

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

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

	return ResultFromScode(sc);
}

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

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

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

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

STDMETHODIMP COleServerItem::XDataObject::GetData(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
	METHOD_PROLOGUE(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

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

STDMETHODIMP COleServerItem::XDataObject::GetDataHere(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium)
{
	METHOD_PROLOGUE(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

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

STDMETHODIMP COleServerItem::XDataObject::QueryGetData(LPFORMATETC lpFormatEtc)
{
	METHOD_PROLOGUE(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

	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 ResultFromScode(DATA_S_SAMEFORMATETC);
}

STDMETHODIMP COleServerItem::XDataObject::SetData(
	LPFORMATETC lpFormatEtc, LPSTGMEDIUM lpStgMedium, BOOL fRelease)
{
	METHOD_PROLOGUE(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

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

STDMETHODIMP COleServerItem::XDataObject::EnumFormatEtc(
	DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc)
{
	METHOD_PROLOGUE(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

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

STDMETHODIMP COleServerItem::XDataObject::DAdvise(
	FORMATETC FAR* pFormatetc, DWORD advf,
	LPADVISESINK pAdvSink, DWORD FAR* pdwConnection)
{
	METHOD_PROLOGUE(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

	*pdwConnection = 0;

	if (pThis->m_lpDataAdviseHolder == NULL &&
		CreateDataAdviseHolder(&pThis->m_lpDataAdviseHolder) != NOERROR)
	{
		return ResultFromScode(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(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

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

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

STDMETHODIMP COleServerItem::XDataObject::EnumDAdvise(
	LPENUMSTATDATA FAR* ppenumAdvise)
{
	METHOD_PROLOGUE(COleServerItem, DataObject)
	ASSERT_VALID(pThis);

	*ppenumAdvise = NULL;

	if (pThis->m_lpDataAdviseHolder == NULL)
		return ResultFromScode(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)
{
	ASSERT_VALID(this);
	COleServerItem* pItem = (COleServerItem*)
		((BYTE*)this - offsetof(COleServerItem, m_dataSource));

	return pItem->OnSetData(lpFormatEtc, lpStgMedium);
		// 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);
	AFX_DUMP1(dc, "\nm_bNeedUnlock = ", m_bNeedUnlock);
	AFX_DUMP1(dc, "\nm_bAutoDelete = ", m_bAutoDelete);
	AFX_DUMP1(dc, "\nm_strItemName = ", m_strItemName);
	AFX_DUMP1(dc, "\nm_lpOleAdviseHolder = ", m_lpOleAdviseHolder);
	AFX_DUMP1(dc, "\nm_lpDataAdviseHolder = ", m_lpDataAdviseHolder);
	AFX_DUMP1(dc, "\ncontaining data source ", &m_dataSource);
}
#endif

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