// 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"
#include "occimpl.h"

#ifdef AFX_OCC_SEG
#pragma code_seg(AFX_OCC_SEG)
#endif

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

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// OLE event sink handler

typedef BOOL (AFX_MSG_CALL CCmdTarget::*PFN_CHANGED)();
typedef BOOL (AFX_MSG_CALL CCmdTarget::*PFN_REQUEST)(BOOL*);
typedef BOOL (AFX_MSG_CALL CCmdTarget::*PFN_DSCNOTIFY)(DSCSTATE, DSCREASON, BOOL*);
typedef BOOL (AFX_MSG_CALL CCmdTarget::*PFN_CHANGED_RANGE)(UINT);
typedef BOOL (AFX_MSG_CALL CCmdTarget::*PFN_REQUEST_RANGE)(UINT, BOOL*);
typedef BOOL (AFX_MSG_CALL CCmdTarget::*PFN_DSCNOTIFY_RANGE)(UINT, DSCSTATE, DSCREASON, BOOL*);

BOOL CCmdTarget::OnEvent(UINT idCtrl, AFX_EVENT* pEvent,
	AFX_CMDHANDLERINFO* pHandlerInfo)
{
	HRESULT hResult = S_OK;
	UINT uArgError = (UINT)-1;    // no error yet
	const AFX_EVENTSINKMAP_ENTRY* pEntry = GetEventSinkEntry(idCtrl, pEvent);

	// no handler for this event
	if (pEntry == NULL)
		return FALSE;

	if (pHandlerInfo != NULL)
	{
		// just fill in the information, don't do it
		pHandlerInfo->pTarget = this;
		switch (pEvent->m_eventKind)
		{
		case AFX_EVENT::event:
		case AFX_EVENT::propRequest:
			pHandlerInfo->pmf = pEntry->dispEntry.pfn;
			break;

		case AFX_EVENT::propChanged:
			pHandlerInfo->pmf = pEntry->dispEntry.pfnSet;
			break;

		default:
			ASSERT(FALSE);  // bogus value for pEvent->m_eventKind
		}

		return (pHandlerInfo->pmf != NULL);
	}

	BOOL bRange = (pEntry->nCtrlIDLast != (UINT)-1);
	BOOL bHandled = FALSE;

	TRY
	{
		switch (pEvent->m_eventKind)
		{
		case AFX_EVENT::event:
			// do standard method call
			VARIANT var;
			AfxVariantInit(&var);

			DISPPARAMS dispparams;
			dispparams.rgvarg = NULL;

			if (bRange)
			{
				memcpy(&dispparams, pEvent->m_pDispParams, sizeof(DISPPARAMS));
				dispparams.rgvarg = new VARIANT[++dispparams.cArgs];
				memcpy(dispparams.rgvarg, pEvent->m_pDispParams->rgvarg,
					sizeof(VARIANT) * (dispparams.cArgs-1));
				VARIANT* pvarID = &dispparams.rgvarg[dispparams.cArgs-1];
				V_VT(pvarID) = VT_I4;
				V_I4(pvarID) = idCtrl;
			}

			hResult = CallMemberFunc(&pEntry->dispEntry, DISPATCH_METHOD, &var,
				(bRange ? &dispparams : pEvent->m_pDispParams), &uArgError);
			ASSERT(FAILED(hResult) || (V_VT(&var) == VT_BOOL));
			bHandled = V_BOOL(&var);

			if (bRange)
				delete [] dispparams.rgvarg;

			break;

		case AFX_EVENT::propChanged:
			{
				if (bRange)
				{
					PFN_CHANGED_RANGE pfn = (PFN_CHANGED_RANGE)pEntry->dispEntry.pfnSet;
					bHandled = (this->*pfn)(idCtrl);
				}
				else
				{
					PFN_CHANGED pfn = (PFN_CHANGED)pEntry->dispEntry.pfnSet;
					bHandled = (this->*pfn)();
				}

				hResult = S_OK;
			}
			break;

		case AFX_EVENT::propRequest:
			{
				BOOL bAllow = TRUE;

				if (bRange)
				{
					PFN_REQUEST_RANGE pfn = (PFN_REQUEST_RANGE)pEntry->dispEntry.pfn;
					bHandled = (this->*pfn)(idCtrl, &bAllow);
				}
				else
				{
					PFN_REQUEST pfn = (PFN_REQUEST)pEntry->dispEntry.pfn;
					bHandled = (this->*pfn)(&bAllow);
				}

				hResult = bAllow ? S_OK : S_FALSE;
			}
			break;

		case AFX_EVENT::propDSCNotify:
			{
				BOOL bAllow = TRUE;

				if (bRange)
				{
					PFN_DSCNOTIFY_RANGE pfn = (PFN_DSCNOTIFY_RANGE)pEntry->dispEntry.pfn;
					bHandled = (this->*pfn)(idCtrl, pEvent->m_nDSCState,
						pEvent->m_nDSCReason, &bAllow);
				}
				else
				{
					PFN_DSCNOTIFY pfn = (PFN_DSCNOTIFY)pEntry->dispEntry.pfn;
					bHandled = (this->*pfn)(pEvent->m_nDSCState,
						pEvent->m_nDSCReason, &bAllow);
				}

				hResult = bAllow ? S_OK : S_FALSE;
			}
			break;

		default:
			ASSERT(FALSE);  // bogus value for pEvent->m_eventKind
		}
	}
	CATCH_ALL(e)
	{
		if (pEvent->m_pExcepInfo != NULL)
		{
			// fill exception with translation of MFC exception
			COleDispatchException::Process(pEvent->m_pExcepInfo, e);
		}
		DELETE_EXCEPTION(e);
		hResult = DISP_E_EXCEPTION;
	}
	END_CATCH_ALL

	// fill error argument if one is available
	if (FAILED(hResult) && pEvent->m_puArgError != NULL && uArgError != -1)
		*pEvent->m_puArgError = uArgError;

	// fill result code
	pEvent->m_hResult = hResult;

	return bHandled;
}

/////////////////////////////////////////////////////////////////////////////
// Locate event sink map entry

const AFX_EVENTSINKMAP_ENTRY* PASCAL CCmdTarget::GetEventSinkEntry(
	UINT idCtrl, AFX_EVENT* pEvent)
{
	const AFX_EVENTSINKMAP* pSinkMap = GetEventSinkMap();
	const AFX_EVENTSINKMAP_ENTRY* pEntry;
	size_t flag = (pEvent->m_eventKind != AFX_EVENT::event);

	while (pSinkMap != NULL)
	{
		// find matching AFX_EVENTSINKMAP_ENTRY
		pEntry = pSinkMap->lpEntries;
		while (pEntry->dispEntry.nPropOffset != -1)
		{
			if ((pEntry->dispEntry.lDispID == pEvent->m_dispid) &&
				(pEntry->dispEntry.nPropOffset == flag))
			{
				if (pEntry->nCtrlIDLast == (UINT)-1)
				{
					// check for wildcard match or exact match
					if ((pEntry->nCtrlIDFirst == (UINT)-1) ||
						(pEntry->nCtrlIDFirst == idCtrl))
						return pEntry;
				}
				else
				{
					// check for range match
					if ((pEntry->nCtrlIDFirst <= idCtrl) &&
						(idCtrl <= pEntry->nCtrlIDLast))
						return pEntry;
				}
			}

			++pEntry;
		}
		// check base class
#ifdef _AFXDLL
		pSinkMap = (*pSinkMap->pfnGetBaseMap)();
#else
		pSinkMap = pSinkMap->pBaseMap;
#endif
	}

	return NULL;    // no matching entry
}
