// 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 _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// COleDocObjectItem

IMPLEMENT_DYNAMIC(COleDocObjectItem, COleClientItem)

BEGIN_INTERFACE_MAP(COleDocObjectItem, COleClientItem)
	INTERFACE_PART(COleDocObjectItem, IID_IOleDocumentSite, OleDocumentSite)
END_INTERFACE_MAP()


COleDocObjectItem::COleDocObjectItem(COleDocument* pContainerDoc)
	: COleClientItem(pContainerDoc)
{
	m_pHelpPopupMenu = NULL;
	m_pActiveView = NULL;
	m_pIPrint = NULL;
	m_bInHelpMenu = FALSE;
}

COleDocObjectItem::~COleDocObjectItem()
{
	if (m_pHelpPopupMenu != NULL)
		m_pHelpPopupMenu->RemoveMenu(0, MF_BYPOSITION);
	delete m_pHelpPopupMenu;
}


/////////////////////////////////////////////////////////////////////////////
// IOleDocumentSite interface

STDMETHODIMP_(ULONG) COleDocObjectItem::XOleDocumentSite::AddRef()
{
	METHOD_PROLOGUE_EX(COleDocObjectItem, OleDocumentSite)
	return pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleDocObjectItem::XOleDocumentSite::Release()
{
	METHOD_PROLOGUE_EX(COleDocObjectItem, OleDocumentSite)
	return pThis->ExternalRelease();
}

STDMETHODIMP COleDocObjectItem::XOleDocumentSite::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX(COleDocObjectItem, OleDocumentSite)
	return pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleDocObjectItem::XOleDocumentSite::ActivateMe(
	LPOLEDOCUMENTVIEW pViewToActivate)
{
	METHOD_PROLOGUE_EX(COleDocObjectItem, OleDocumentSite)

	LPOLEDOCUMENT lpDocument;
	LPOLECLIENTSITE lpClientSite = pThis->GetClientSite();
	LPOLEINPLACESITE lpInPlaceSite =
		(LPOLEINPLACESITE) pThis->GetInterface(&IID_IOleInPlaceSite);

	if (lpClientSite == NULL || lpInPlaceSite == NULL)
		return E_FAIL;

	// if we've gotten a NULL view, we're to create one ourselves

	if (pViewToActivate == NULL)
	{
		// if we already have a view, we can simply activate it

		if (pThis->m_pActiveView != NULL && pThis->m_pView != NULL)
		{
			pThis->ActivateAndShow();
			return NOERROR;
		}

		ASSERT(pThis->m_lpObject != NULL);
		if (pThis->m_lpObject == NULL)
			return E_FAIL;

		lpDocument = QUERYINTERFACE(pThis->m_lpObject, IOleDocument);
		if (lpDocument == NULL)
			return E_FAIL;

		if (FAILED(
			lpDocument->CreateView(lpInPlaceSite, NULL, 0, &pViewToActivate)))
		{
			lpDocument->Release();
		return E_OUTOFMEMORY;
		}

		// we're done with the document pointer
		lpDocument->Release();
	}
	else if (pThis->m_pActiveView != NULL && pThis->m_pActiveView == pViewToActivate)
	{
		// we already own this view, so no need to addref
		// simply make it visible and resize it

		pThis->ActivateAndShow();
		return NOERROR;
	}
	else
	{
		// set the in-place site
		pViewToActivate->SetInPlaceSite(lpInPlaceSite);
		pViewToActivate->AddRef();
	}

	// it must've created
	ASSERT(pThis->m_pView != NULL);

	// if we had an old one, release it
	if (pThis->m_pActiveView != NULL)
	{
		pThis->m_pActiveView->Show(FALSE);
		pThis->m_pActiveView->UIActivate(FALSE);
		pThis->m_pActiveView->Release();

		if (pThis->m_pIPrint != (IPrint*) -1 && pThis->m_pIPrint != NULL)
			pThis->m_pIPrint->Release();
		pThis->m_pIPrint = NULL;
	}

	// remember it for later
	pThis->m_pActiveView = pViewToActivate;

	// activate and position it
	pThis->ActivateAndShow();

	return NOERROR;
}

/////////////////////////////////////////////////////////////////////////////
// IOleDocumentSite implementation helper

void COleDocObjectItem::ActivateAndShow()
{
	// set up toolbars and menus for the object
	m_pActiveView->UIActivate(TRUE);

	// set the window size, avoiding new toolbars
	RECT rc;
	m_pView->GetClientRect(&rc);
	m_pActiveView->SetRect(&rc);

	// make everything visible
	m_pActiveView->Show(TRUE);

	return;
}

void COleDocObjectItem::OnGetItemPosition(CRect& rPosition)
{
	ASSERT_VALID(this);
	ASSERT(AfxIsValidAddress(&rPosition, sizeof(RECT)));

	// doc objects [almost] always in the exact rect of the view
	ASSERT_VALID(m_pView);
	m_pView->GetClientRect(&rPosition);
}

LPOLEDOCUMENTVIEW COleDocObjectItem::GetActiveView() const
{
	return m_pActiveView;
}

void COleDocObjectItem::Release(OLECLOSE dwCloseOption)
{
	RELEASE(m_pActiveView);
	if (m_pIPrint != (IPrint*) -1)
		RELEASE(m_pIPrint);
	COleClientItem::Release(dwCloseOption);
}

HRESULT COleDocObjectItem::ExecCommand(DWORD nCmdID,
	DWORD nCmdExecOpt /* = OLECMDEXECOPT_DONTPROMPTUSER */,
	const GUID* pguidCmdGroup /* = NULL */)
{
	LPOLECOMMANDTARGET lpCt = QUERYINTERFACE(m_lpObject, IOleCommandTarget);
	HRESULT hr = E_NOTIMPL;

	if (lpCt != NULL)
		hr = lpCt->Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, NULL, NULL);

	RELEASE(lpCt);
	return hr;
}

BOOL COleDocObjectItem::SupportsIPrint()
{
	// did someone already ask? -1 means we know it doesn't,
	// non-NULL means we know it does (and we point at it)
	// NULL means we don't know

	if (m_pIPrint == NULL)
	{
		// QI for it
		m_pIPrint = QUERYINTERFACE(m_lpObject, IPrint);

		if (m_pIPrint == NULL)
		{
			// if the server isn't running, we'll need to
			// start it in order to print

			if (FAILED(::OleRun(m_lpObject)))
				m_pIPrint = (IPrint*) -1;
			else
				m_pIPrint = QUERYINTERFACE(m_lpObject, IPrint);
		}
	}

	return (m_pIPrint != NULL && m_pIPrint != (IPrint*) -1);
}

BOOL COleDocObjectItem::GetPageCount(LPLONG pnFirstPage, LPLONG pcPages)
{
	if (!SupportsIPrint())
		return FALSE;

	//WINBUG: The proxy in DOCOBJ.DLL is broken; it doesn't allow
	// NULL parameters to IPrint::GetPageInfo(), even though the spec
	// says it should.

	LONG lPages;
	LONG lFirstPage;

	HRESULT hr = m_pIPrint->GetPageInfo(&lFirstPage, &lPages);

	if (pnFirstPage != NULL)
		*pnFirstPage = lFirstPage;
	if (pcPages != NULL)
		*pcPages = lPages;

	if (SUCCEEDED(hr))
		return TRUE;
	else
		return FALSE;
}


CMenu* COleDocObjectItem::GetHelpMenu(UINT& nPosition)
{
	CFrameWnd* pFrame = m_pView->GetTopLevelFrame();
	CMenu* pMenuFrame = CMenu::FromHandle(pFrame->m_hMenuDefault);

	if (pMenuFrame != NULL)
		nPosition = pMenuFrame->GetMenuItemCount() -1;

	return pMenuFrame;
}

void COleDocObjectItem::OnInsertMenus(CMenu* pMenuShared,
	LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pMenuShared);
	ASSERT(AfxIsValidAddress(lpMenuWidths, sizeof(OLEMENUGROUPWIDTHS)));

	// initialize the group widths array
	lpMenuWidths->width[0] = 1;
	lpMenuWidths->width[2] = 0;
	lpMenuWidths->width[4] = 0;

	// get menu from document template
	CDocTemplate* pTemplate = GetDocument()->GetDocTemplate();
	HMENU hMenuOLE = pTemplate->m_hMenuInPlace;

	// only copy the popups if there is a menu loaded
	if (hMenuOLE == NULL)
		return;

	UINT nItem;
	CMenu* pMenuFrame = GetHelpMenu(nItem);

	if (pMenuFrame != NULL)
	{
		CString strHelpMenuName;
		int nSeparator = pMenuFrame->GetMenuString(nItem,
				strHelpMenuName, MF_BYPOSITION);
		if (nSeparator == 0)
		{
			TRACE0("Error: COleDocObjectItem::OnInsertMenus() found no help menu!\n");
			return;
		}

		CString strTearOffName;
		strTearOffName.Format(_T("%s %s"), AfxGetAppName(), strHelpMenuName);
		strTearOffName.Remove('&');

		// get the normal frame menu
		int nCount = pMenuFrame->GetMenuItemCount();
		HMENU hMenu = GetSubMenu(pMenuFrame->m_hMenu, nCount-1);

		// clean up old menu and allocate a new one
		if (m_pHelpPopupMenu == NULL)
		{
			m_pHelpPopupMenu = new CMenu;

			// create new sub-popup menu and add container's Help tearoff
			// then add help menu from main window
			m_pHelpPopupMenu->CreateMenu();
			m_pHelpPopupMenu->InsertMenu((UINT) -1, MF_BYPOSITION | MF_POPUP,
				(UINT) hMenu, strTearOffName);
		}

		pMenuShared->InsertMenu(1, MF_BYPOSITION | MF_POPUP,
			(UINT) m_pHelpPopupMenu->m_hMenu, strHelpMenuName);

		// tell the object we added our Help menu
		lpMenuWidths->width[5] = 1;
	}

	// insert our menu items and adjust group widths array
	AfxMergeMenus(pMenuShared->GetSafeHmenu(), hMenuOLE,
		&lpMenuWidths->width[0], 0);
}

void COleDocObjectItem::OnRemoveMenus(CMenu *pMenuShared)
{
	int cItemsShared = pMenuShared->GetMenuItemCount();
	if (cItemsShared != 0)
	{
		CMenu *pMenuHelp = pMenuShared->GetSubMenu(cItemsShared - 1);

		int cItemsHelp = pMenuHelp->GetMenuItemCount();
		int nItem;
		for (nItem = cItemsHelp-1; nItem > 0; nItem--)
			pMenuHelp->DeleteMenu(nItem, MF_BYPOSITION);

		pMenuShared->RemoveMenu(cItemsShared - 1, MF_BYPOSITION);
	}

	COleClientItem::OnRemoveMenus(pMenuShared);
}

BOOL COleDocObjectItem::OnPreparePrinting(CView* pCaller,
	CPrintInfo* pInfo, BOOL bPrintAll)
{
	LONG lDocObjectPages = 0;

	CDocument* pDoc = pCaller->GetDocument();
	COleDocument* pOleDoc = DYNAMIC_DOWNCAST(COleDocument, pDoc);
	if (pOleDoc == NULL)
		return FALSE;

	POSITION pos = pOleDoc->GetStartPosition();
	while (pos != NULL)
	{
		COleClientItem* pItem = pOleDoc->GetNextClientItem(pos);
		COleDocObjectItem* pDocItem =
			DYNAMIC_DOWNCAST(COleDocObjectItem, pItem);
		if (pDocItem == NULL)
			continue;

		// if this isn't the view, continue
		if (!bPrintAll)
		{
			if (pItem->m_pView == NULL ||
				 pItem->m_pView->m_hWnd != pCaller->m_hWnd)
				continue;
		}

		if (pDocItem->SupportsIPrint())
		{
			LONG lThisObjectPages;
			if (pDocItem->GetPageCount(NULL, &lThisObjectPages))
				lDocObjectPages += lThisObjectPages;
			pInfo->m_bDocObject = TRUE;
		}
		else
			lDocObjectPages++;

		if (!bPrintAll)
			break;
	}

	if (lDocObjectPages > 0)
	{
		UINT nMaxPage = pInfo->GetMaxPage();

		// set the page count; increment it if previously set
		if (nMaxPage == 0xFFFF)
			pInfo->SetMaxPage(lDocObjectPages);
		else
			pInfo->SetMaxPage(nMaxPage + lDocObjectPages);
		pInfo->m_bDocObject = TRUE;
	}

	if (pInfo->m_bDocObject)
	{
		// we can't show the "selection" button for DocObjects
		pInfo->m_pPD->m_pd.Flags |= PD_NOSELECTION;

		// if it's a doc object, and we're printing all, then we
		// shouldn't show the selection
		if (bPrintAll)
			pInfo->m_pPD->m_pd.Flags |= PD_NOPAGENUMS;
	}

	return TRUE;
}

void COleDocObjectItem::OnPrint(CView* pCaller, CPrintInfo* pInfo,
	BOOL bPrintAll)
{
	// Note that this function ignores pInfo->m_nCurPage, and will always
	// print the whole range of pages in the CPrintInfo object. That's
	// because DocObjects don't support any mechanism to _continue_
	// printing to an existing print job.

	CDocument* pDoc = pCaller->GetDocument();
	COleDocument* pOleDoc = DYNAMIC_DOWNCAST(COleDocument, pDoc);
	if (pOleDoc == NULL)
		return;

	POSITION pos = pOleDoc->GetStartPosition();
	while (pos != NULL)
	{
		COleClientItem* pItem = pOleDoc->GetNextClientItem(pos);
		COleDocObjectItem* pDocItem = DYNAMIC_DOWNCAST(COleDocObjectItem, pItem);
		if (pDocItem == NULL)
			continue;

		// if this isn't the view, continue
		if (!bPrintAll)
		{
			if (pItem->m_pView == NULL || pItem->m_pView->m_hWnd != pCaller->m_hWnd)
				continue;
		}

		HRESULT hrThisPage = E_UNEXPECTED;

		if (pDocItem->SupportsIPrint())
		{
			DVTARGETDEVICE* pTargetDevice = NULL;
			LPDEVNAMES lpDevNames = NULL;
			LPDEVMODE lpDevMode = NULL;

			lpDevNames = (LPDEVNAMES) GlobalLock(pInfo->m_pPD->m_pd.hDevNames);
			if (lpDevNames != NULL)
			{
				lpDevMode = (LPDEVMODE) GlobalLock(pInfo->m_pPD->m_pd.hDevMode);
				if (lpDevMode != NULL)
				{
					pTargetDevice = _AfxOleCreateTargetDevice(lpDevNames, lpDevMode);
					if (pTargetDevice != NULL)
					{
						PAGESET* pps = (PAGESET*) CoTaskMemAlloc(sizeof(PAGESET));
						if (pps != NULL)
						{
							pps->cbStruct = sizeof(PAGESET);
							ASSERT((pps->cbStruct % 4) == 0);
							pps->fOddPages = TRUE;
							pps->fEvenPages = TRUE;
							pps->cPageRange = 1;
							pps->rgPages[0].nFromPage = pInfo->GetFromPage();
							pps->rgPages[0].nToPage = pInfo->GetToPage();

							LONG lLastPage = pps->rgPages[0].nFromPage;
							LONG lPagesPrinted;

							DWORD dwFlags = PRINTFLAG_RECOMPOSETODEVICE;

							if (pInfo->m_pPD->m_pd.Flags & PD_PRINTTOFILE)
								dwFlags |= PRINTFLAG_PRINTTOFILE;

							hrThisPage = pDocItem->m_pIPrint->Print(dwFlags,
									&pTargetDevice, &pps, NULL, NULL,
									pInfo->m_nCurPage, &lPagesPrinted,
									&lLastPage);

							if (!SUCCEEDED(hrThisPage))
								TRACE1("IPrint::Print() returned %8.8X\n", hrThisPage);
							CoTaskMemFree(pps);
						}
						CoTaskMemFree(pTargetDevice);
					}
					GlobalUnlock(pInfo->m_pPD->m_pd.hDevMode);
				}
				GlobalUnlock(pInfo->m_pPD->m_pd.hDevNames);
			}
		}
		else
		{
			// try through IOleCommandTarget

			hrThisPage = pDocItem->ExecCommand(OLECMDID_PRINT);
			if (!SUCCEEDED(hrThisPage))
				TRACE1("IOleCommandTarget::Exec() returned %8.8X\n", hrThisPage);
		}
	}

	return;
}
