// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1997 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 AFXCTL_PROP_SEG
#pragma code_seg(AFXCTL_PROP_SEG)
#endif

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

#ifndef _DEBUG
#pragma intrinsic(memset)
#endif

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// Stock property mask

#define STOCKPROP_BACKCOLOR     0x00000001
#define STOCKPROP_CAPTION       0x00000002
#define STOCKPROP_FONT          0x00000004
#define STOCKPROP_FORECOLOR     0x00000008
#define STOCKPROP_TEXT          0x00000010
#define STOCKPROP_BORDERSTYLE   0x00000020
#define STOCKPROP_ENABLED       0x00000040
#define STOCKPROP_APPEARANCE    0x00000080

void COleControl::InitStockPropMask()
{
	static const DWORD _dwStockFlags[] =
	{
		STOCKPROP_BACKCOLOR,    // -501
		0,                      // -502
		0,                      // -503
		STOCKPROP_BORDERSTYLE,  // -504
		0,                      // -505
		0,                      // -506
		0,                      // -507
		0,                      // -508
		0,                      // -509
		0,                      // -510
		0,                      // -511
		STOCKPROP_FONT,         // -512
		STOCKPROP_FORECOLOR,    // -513
		STOCKPROP_ENABLED,      // -514
		0,                      // -515
		0,                      // -516
		STOCKPROP_TEXT,         // -517
		STOCKPROP_CAPTION,      // -518
		0,                      // -519
		STOCKPROP_APPEARANCE,   // -520
	};

	const AFX_DISPMAP* pDispMap = GetDispatchMap();
	const AFX_DISPMAP_ENTRY* pEntry;
	ASSERT(pDispMap != NULL);

	// If stock property mask is already initialized, we're outta here.
	if (*pDispMap->lpStockPropMask != (DWORD)-1)
		return;

	AfxLockGlobals(CRIT_STOCKMASK);

	if (*pDispMap->lpStockPropMask == (DWORD)-1)
	{
		const AFX_DISPMAP* pDispMapTop = pDispMap;
		DWORD dwStockPropMask = 0;

		while (pDispMap != NULL)
		{
			pEntry = pDispMap->lpEntries;
			while (pEntry->nPropOffset != -1)
			{
				int nIndex = DISPID_BACKCOLOR - pEntry->lDispID;
				DWORD dwFlag;
				if (nIndex >= 0 && nIndex < _countof(_dwStockFlags) &&
					(dwFlag = _dwStockFlags[nIndex]) != 0)
				{
					dwStockPropMask |= dwFlag;
				}

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

		*pDispMapTop->lpStockPropMask = dwStockPropMask;
	}

	AfxUnlockGlobals(CRIT_STOCKMASK);
}

static void ToggleBorderStyle(CWnd* pWnd)
{
	if (pWnd->m_hWnd != NULL)
	{
		// toggle border style and force redraw of border
		::SetWindowLong(pWnd->m_hWnd, GWL_STYLE, pWnd->GetStyle() ^ WS_BORDER);
		::SetWindowPos(pWnd->m_hWnd, NULL, 0, 0, 0, 0,
			SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
	}
}

static void ToggleAppearance(CWnd* pWnd)
{
	if (pWnd->m_hWnd != NULL)
	{
		// toggle border style and force redraw of border
		::SetWindowLong(pWnd->m_hWnd, GWL_EXSTYLE, pWnd->GetExStyle() ^
			WS_EX_CLIENTEDGE);
		::SetWindowPos(pWnd->m_hWnd, NULL, 0, 0, 0, 0,
			SWP_DRAWFRAME | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
	}
}

/////////////////////////////////////////////////////////////////////////////
// Property exchange for stock properties

void COleControl::ExchangeStockProps(CPropExchange* pPX)
{
	BOOL bLoading = pPX->IsLoading();
	DWORD dwStockPropMask = GetStockPropMask();
	DWORD dwPersistMask = dwStockPropMask;

	PX_ULong(pPX, _T("_StockProps"), dwPersistMask);

	if (dwStockPropMask & (STOCKPROP_CAPTION | STOCKPROP_TEXT))
	{
		CString strText;

		if (dwPersistMask & (STOCKPROP_CAPTION | STOCKPROP_TEXT))
		{
			if (!bLoading)
				strText = InternalGetText();
			if (dwStockPropMask & STOCKPROP_CAPTION)
				PX_String(pPX, _T("Caption"), strText, _T(""));
			if (dwStockPropMask & STOCKPROP_TEXT)
				PX_String(pPX, _T("Text"), strText, _T(""));
		}
		if (bLoading)
		{
			TRY
				SetText(strText);
			END_TRY
		}
	}

	if (dwStockPropMask & STOCKPROP_FORECOLOR)
	{
		if (dwPersistMask & STOCKPROP_FORECOLOR)
			PX_Color(pPX, _T("ForeColor"), m_clrForeColor, AmbientForeColor());
		else if (bLoading)
			m_clrForeColor = AmbientForeColor();
	}

	if (dwStockPropMask & STOCKPROP_BACKCOLOR)
	{
		if (dwPersistMask & STOCKPROP_BACKCOLOR)
			PX_Color(pPX, _T("BackColor"), m_clrBackColor, AmbientBackColor());
		else if (bLoading)
			m_clrBackColor = AmbientBackColor();
	}

	if (dwStockPropMask & STOCKPROP_FONT)
	{
		LPFONTDISP pFontDispAmbient = AmbientFont();
		BOOL bChanged = TRUE;

		if (dwPersistMask & STOCKPROP_FONT)
			bChanged = PX_Font(pPX, _T("Font"), m_font, NULL, pFontDispAmbient);
		else if (bLoading)
			m_font.InitializeFont(NULL, pFontDispAmbient);

		if (bLoading && bChanged)
			OnFontChanged();

		RELEASE(pFontDispAmbient);
	}

	if (dwStockPropMask & STOCKPROP_BORDERSTYLE)
	{
		short sBorderStyle = m_sBorderStyle;

		if (dwPersistMask & STOCKPROP_BORDERSTYLE)
			PX_Short(pPX, _T("BorderStyle"), m_sBorderStyle, 0);
		else if (bLoading)
			m_sBorderStyle = 0;

		if (sBorderStyle != m_sBorderStyle)
			ToggleBorderStyle(this);
	}

	if (dwStockPropMask & STOCKPROP_ENABLED)
	{
		BOOL bEnabled = m_bEnabled;

		if (dwPersistMask & STOCKPROP_ENABLED)
			PX_Bool(pPX, _T("Enabled"), m_bEnabled, TRUE);
		else if (bLoading)
			m_bEnabled = TRUE;

		if ((bEnabled != m_bEnabled) && (m_hWnd != NULL))
			::EnableWindow(m_hWnd, m_bEnabled);
	}

	if (dwStockPropMask & STOCKPROP_APPEARANCE)
	{
		short sAppearance = m_sAppearance;

		if (dwPersistMask & STOCKPROP_APPEARANCE)
			PX_Short(pPX, _T("Appearance"), m_sAppearance, 0);
		else if (bLoading)
			m_sAppearance = AmbientAppearance();

		if (sAppearance != m_sAppearance)
			ToggleAppearance(this);
	}
}

/////////////////////////////////////////////////////////////////////////////
// Serialization for stock properties

void COleControl::SerializeStockProps(CArchive& ar)
{
	BOOL bLoading = ar.IsLoading();
	DWORD dwStockPropMask = GetStockPropMask();
	DWORD dwPersistMask = dwStockPropMask;

	if (bLoading)
	{
		ar >> dwPersistMask;

		if (dwStockPropMask & (STOCKPROP_CAPTION | STOCKPROP_TEXT))
		{
			if (dwPersistMask & (STOCKPROP_CAPTION | STOCKPROP_TEXT))
			{
				CString strText;
				ar >> strText;
				TRY
					SetText(strText);
				END_TRY
			}
			else
			{
				TRY
					SetText(_T(""));
				END_TRY
			}
		}

		if (dwStockPropMask & STOCKPROP_FORECOLOR)
		{
			if (dwPersistMask & STOCKPROP_FORECOLOR)
				ar >> m_clrForeColor;
			else
				m_clrForeColor = AmbientForeColor();
		}

		if (dwStockPropMask & STOCKPROP_BACKCOLOR)
		{
			if (dwPersistMask & STOCKPROP_BACKCOLOR)
				ar >> m_clrBackColor;
			else
				m_clrBackColor = AmbientBackColor();
		}

		if (dwStockPropMask & STOCKPROP_FONT)
		{
			BOOL bRead = FALSE;

			if (dwPersistMask & STOCKPROP_FONT)
			{
				BYTE bFlag;
				ar >> bFlag;
				if (bFlag != 0xFF)
				{
					CArchiveStream stm(&ar);
					LPSTREAM pstm = _AfxGetArchiveStream(ar, stm);
					LPFONT pFont = _AfxCreateFontFromStream(pstm);
					if (pFont != NULL)
					{
						m_font.SetFont(pFont);
						bRead = TRUE;
					}
				}
			}

			if (! bRead)
			{
				LPFONTDISP pFontDispAmbient = AmbientFont();
				m_font.InitializeFont(NULL, pFontDispAmbient);
				RELEASE(pFontDispAmbient);
			}

			OnFontChanged();
		}

		if (dwStockPropMask & STOCKPROP_BORDERSTYLE)
		{
			if (dwPersistMask & STOCKPROP_BORDERSTYLE)
			{
				short sBorderStyle = m_sBorderStyle;
				ar >> m_sBorderStyle;
				if (sBorderStyle != m_sBorderStyle)
					ToggleBorderStyle(this);
			}
			else
			{
				m_sBorderStyle = 0;
			}
		}

		if (dwStockPropMask & STOCKPROP_ENABLED)
		{
			if (dwPersistMask & STOCKPROP_ENABLED)
			{
				BOOL bEnabled = m_bEnabled;
				ar >> m_bEnabled;
				if ((bEnabled != m_bEnabled) && (m_hWnd != NULL))
					::EnableWindow(m_hWnd, m_bEnabled);
			}
			else
			{
				m_bEnabled = TRUE;
			}
		}

		if (dwStockPropMask & STOCKPROP_APPEARANCE)
		{
			if (dwPersistMask & STOCKPROP_APPEARANCE)
			{
				short sAppearance = m_sAppearance;
				ar >> m_sAppearance;

				if (sAppearance != m_sAppearance)
					ToggleAppearance(this);
			}
			else
			{
				m_sAppearance = AmbientAppearance();
			}
		}
	}
	else
	{
		ar << dwPersistMask;

		if (dwStockPropMask & (STOCKPROP_CAPTION | STOCKPROP_TEXT))
			ar << m_strText;

		if (dwStockPropMask & STOCKPROP_FORECOLOR)
			ar << m_clrForeColor;

		if (dwStockPropMask & STOCKPROP_BACKCOLOR)
			ar << m_clrBackColor;

		if (dwStockPropMask & STOCKPROP_FONT)
		{
			BOOL bWrite = FALSE;
			LPFONT pFont = m_font.m_pFont;
			if (pFont != NULL)
			{
				// If same as ambient font (or error), write 0xFF for the flag
				LPFONTDISP pFontDispAmbient = AmbientFont();
				if (!_AfxIsSameFont(m_font, NULL, pFontDispAmbient))
				{
					LPPERSISTSTREAM pps = NULL;
					if (SUCCEEDED(pFont->QueryInterface(IID_IPersistStream,
						(LPVOID*)&pps)))
					{
						ASSERT_POINTER(pps, IPersistStream);
						ar << (BYTE)0x00;
						CArchiveStream stm(&ar);
						LPSTREAM pstm = _AfxGetArchiveStream(ar, stm);
						bWrite = SUCCEEDED(::OleSaveToStream(pps, pstm));
						pps->Release();
						if (!bWrite)
							AfxThrowArchiveException(CArchiveException::generic);
					}
				}
				RELEASE(pFontDispAmbient);
			}
			if (! bWrite)
			{
				ar << (BYTE)0xFF;
			}
		}

		if (dwStockPropMask & STOCKPROP_BORDERSTYLE)
			ar << m_sBorderStyle;

		if (dwStockPropMask & STOCKPROP_ENABLED)
			ar << m_bEnabled;

		if (dwStockPropMask & STOCKPROP_APPEARANCE)
			ar << m_sAppearance;
	}
}

/////////////////////////////////////////////////////////////////////////////
// Initialization for stock properties

void COleControl::ResetStockProps()
{
	DWORD dwStockPropMask = GetStockPropMask();

	if (dwStockPropMask & (STOCKPROP_CAPTION | STOCKPROP_TEXT))
	{
		TRY
			SetText(_T(""));
		END_TRY
	}

	if (dwStockPropMask & STOCKPROP_FORECOLOR)
		m_clrForeColor = AmbientForeColor();

	if (dwStockPropMask & STOCKPROP_BACKCOLOR)
		m_clrBackColor = AmbientBackColor();

	if (dwStockPropMask & STOCKPROP_FONT)
	{
		LPFONTDISP pFontDispAmbient = AmbientFont();
		m_font.InitializeFont(NULL, pFontDispAmbient);
		RELEASE(pFontDispAmbient);
		OnFontChanged();
	}

	if (dwStockPropMask & STOCKPROP_BORDERSTYLE)
		m_sBorderStyle = 0;

	if (dwStockPropMask & STOCKPROP_ENABLED)
		m_bEnabled = TRUE;

	if (dwStockPropMask & STOCKPROP_APPEARANCE)
		m_sAppearance = AmbientAppearance();
}

/////////////////////////////////////////////////////////////////////////////
// Appearance property

short COleControl::GetAppearance()
{
	return m_sAppearance;
}

void COleControl::SetAppearance(short sAppearance)
{
	if (sAppearance != 0 && sAppearance != 1)
		ThrowError(CTL_E_INVALIDPROPERTYVALUE, AFX_IDP_E_INVALIDPROPERTYVALUE);

	// Is the property changing?
	if (m_sAppearance == sAppearance)
		return;

	if (!BoundPropertyRequestEdit(DISPID_APPEARANCE))
		SetNotPermitted();

	ASSERT((m_hWnd == NULL) ||
		((GetExStyle() & WS_EX_CLIENTEDGE) == (DWORD)(m_sAppearance ?
			WS_EX_CLIENTEDGE : 0)));

	m_sAppearance = sAppearance;
	m_bModified = TRUE;

	ToggleAppearance(this);
	OnAppearanceChanged();

	BoundPropertyChanged(DISPID_APPEARANCE);
}

void COleControl::OnAppearanceChanged()
{
	// Can be overridden by subclass

	InvalidateControl();
}

/////////////////////////////////////////////////////////////////////////////
// BackColor property

OLE_COLOR COleControl::GetBackColor()
{
	return m_clrBackColor;
}

void COleControl::SetBackColor(OLE_COLOR clrBackColor)
{
	// Is the property changing?
	if (m_clrBackColor == clrBackColor)
		return;

	if (FAILED(_AfxOleTranslateColor(clrBackColor, NULL, NULL)))
		ThrowError(CTL_E_INVALIDPROPERTYVALUE, AFX_IDP_E_INVALIDPROPERTYVALUE);

	if (!BoundPropertyRequestEdit(DISPID_BACKCOLOR))
		SetNotPermitted();

	m_clrBackColor = clrBackColor;
	m_bModified = TRUE;
	OnBackColorChanged();

	BoundPropertyChanged(DISPID_BACKCOLOR);
}

void COleControl::OnBackColorChanged()
{
	// Can be overridden by subclass
	InvalidateControl();
}

/////////////////////////////////////////////////////////////////////////////
// BorderStyle property

short COleControl::GetBorderStyle()
{
	return m_sBorderStyle;
}

void COleControl::SetBorderStyle(short sBorderStyle)
{
	if (sBorderStyle != 0 && sBorderStyle != 1)
		ThrowError(CTL_E_INVALIDPROPERTYVALUE, AFX_IDP_E_INVALIDPROPERTYVALUE);

	// Is the property changing?
	if (m_sBorderStyle == sBorderStyle)
		return;

	if (!BoundPropertyRequestEdit(DISPID_BORDERSTYLE))
		SetNotPermitted();

	ASSERT((m_hWnd == NULL) ||
		((GetStyle() & WS_BORDER) == (DWORD)(m_sBorderStyle ? WS_BORDER : 0)));

	m_sBorderStyle = sBorderStyle;
	m_bModified = TRUE;

	ToggleBorderStyle(this);
	OnBorderStyleChanged();

	BoundPropertyChanged(DISPID_BORDERSTYLE);
}

void COleControl::OnBorderStyleChanged()
{
	// Can be overridden by subclass

	InvalidateControl();
}

/////////////////////////////////////////////////////////////////////////////
// Text and Caption properties

const CString& COleControl::InternalGetText()
{
	// Some subclassed controls (such as edit controls and comboboxes) change
	// the window text without sending WM_SETTEXT. Therefore, we need to
	// ensure that m_strText is up-to-date.

	if (m_hWnd != NULL && IsSubclassedControl())
	{
		// Usually, the window text will be shorter than 32 characters.
		// When it is, we can be more efficient.

		static const int _cchUsual = 32;

		if (DefWindowProc(WM_GETTEXT, (WPARAM)_cchUsual,
			(LPARAM)m_strText.GetBufferSetLength(_cchUsual)) >= _cchUsual - 1)
		{
			// Text was too long: allocate a bigger buffer.

			int nLen = DefWindowProc(WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0) + 1;
			DefWindowProc(WM_GETTEXT, (WPARAM)nLen,
				(LPARAM)m_strText.GetBufferSetLength(nLen));
		}
		m_strText.ReleaseBuffer();
	}

	return m_strText;
}

BSTR COleControl::GetText()
{
	return ((CString&)InternalGetText()).AllocSysString();
}

void COleControl::SetText(LPCTSTR pszText)
{
	LRESULT lResult;
	if (m_hWnd != NULL)
		lResult = SendMessage(WM_SETTEXT, 0, (LPARAM)pszText);
	else
		lResult = OnSetText(0, (LPARAM)pszText);

	if (lResult == -1)
		SetNotPermitted();
}

LRESULT COleControl::OnSetText(WPARAM wParam, LPARAM lParam)
{
	ASSERT(lParam == 0 || AfxIsValidString((LPCTSTR)lParam));

	// Is the property changing?
	if ((lParam == 0 && InternalGetText().IsEmpty()) ||
		(lParam != 0 && m_strText == (LPCTSTR)lParam))
		return 0;

	DWORD dwStockPropMask = GetStockPropMask();

	if (dwStockPropMask & STOCKPROP_CAPTION)
		if (!BoundPropertyRequestEdit(DISPID_CAPTION))
			return -1;

	if (dwStockPropMask & STOCKPROP_TEXT)
		if (!BoundPropertyRequestEdit(DISPID_TEXT))
			return -1;

	LRESULT lResult = 0;
	m_strText = (LPCTSTR)lParam;
	m_bModified = TRUE;

	if (m_hWnd != NULL)
		lResult = DefWindowProc(WM_SETTEXT, wParam, lParam);

	OnTextChanged();

	if (dwStockPropMask & STOCKPROP_CAPTION)
		BoundPropertyChanged(DISPID_CAPTION);

	if (dwStockPropMask & STOCKPROP_TEXT)
		BoundPropertyChanged(DISPID_TEXT);

	return lResult;
}

void COleControl::OnTextChanged()
{
	// Can be overridden by subclass
	InvalidateControl();
}

/////////////////////////////////////////////////////////////////////////////
// Enabled property

BOOL COleControl::GetEnabled()
{
	return m_bEnabled;
}

void COleControl::SetEnabled(BOOL bEnabled)
{
	// Is the property changing?
	if (m_bEnabled == bEnabled)
		return;

	if (!BoundPropertyRequestEdit(DISPID_ENABLED))
		SetNotPermitted();

	m_bEnabled = bEnabled;
	m_bModified = TRUE;
	if (m_hWnd != NULL)
		::EnableWindow(m_hWnd, m_bEnabled);

	// If the control is UI Active and the Enabled property changed to FALSE,
	// then UI Deactivate the control.

	if (m_bUIActive && !bEnabled)
		m_xOleInPlaceObject.UIDeactivate();

	OnEnabledChanged();

	BoundPropertyChanged(DISPID_ENABLED);
}

void COleControl::OnEnabledChanged()
{
	// Can be overridden by subclass
	InvalidateControl();
}

/////////////////////////////////////////////////////////////////////////////
// Font property

CFontHolder& COleControl::InternalGetFont()
{
	return m_font;
}

LPFONTDISP COleControl::GetFont()
{
	return m_font.GetFontDispatch();
}

void COleControl::SetFont(LPFONTDISP pFontDisp)
{
	ASSERT((pFontDisp == NULL) ||
		   AfxIsValidAddress(pFontDisp, sizeof(IDispatch), FALSE));

	m_font.InitializeFont(NULL, pFontDisp);
	m_bModified = TRUE;
	OnFontChanged();

	BoundPropertyChanged(DISPID_FONT);
}

void COleControl::OnFontChanged()
{
	// Can be overridden by subclass

	// Send WM_SETFONT to control's window
	if ((m_hWnd != NULL) &&
		(GetStockPropMask() & STOCKPROP_FONT) &&
		IsSubclassedControl())
	{
		HFONT hFontPrev = (HFONT)SendMessage(WM_GETFONT, 0, 0);

		CFontHolder& font = InternalGetFont();

		if (font.m_pFont != NULL)
		{
			HFONT hFont = font.GetFontHandle();
			font.m_pFont->AddRefHfont(hFont);
			SendMessage(WM_SETFONT, (WPARAM)hFont, 0);

			if (m_hFontPrev != NULL)
			{
				ASSERT(hFontPrev == m_hFontPrev);
				font.m_pFont->ReleaseHfont(hFontPrev);
			}
			m_hFontPrev = hFont;
		}
		else
		{
			SendMessage(WM_SETFONT, NULL, 0);
			m_hFontPrev = NULL;
		}
	}

	// Invalidate the control
	InvalidateControl();
}

/////////////////////////////////////////////////////////////////////////////
// ForeColor property

OLE_COLOR COleControl::GetForeColor()
{
	return m_clrForeColor;
}

void COleControl::SetForeColor(OLE_COLOR clrForeColor)
{
	// Is the property changing?
	if (m_clrForeColor == clrForeColor)
		return;

	if (FAILED(_AfxOleTranslateColor(clrForeColor, NULL, NULL)))
		ThrowError(CTL_E_INVALIDPROPERTYVALUE, AFX_IDP_E_INVALIDPROPERTYVALUE);

	if (!BoundPropertyRequestEdit(DISPID_FORECOLOR))
		SetNotPermitted();

	m_clrForeColor = clrForeColor;
	m_bModified = TRUE;
	OnForeColorChanged();

	BoundPropertyChanged(DISPID_FORECOLOR);
}

void COleControl::OnForeColorChanged()
{
	// Can be overridden by subclass
	InvalidateControl();
}

/////////////////////////////////////////////////////////////////////////////
// hWnd property

OLE_HANDLE COleControl::GetHwnd()
{
	return (OLE_HANDLE)((m_bInPlaceActive || m_bOpen) ? m_hWnd : NULL);
}

/////////////////////////////////////////////////////////////////////////////
// ReadyState property

long COleControl::GetReadyState()
{
	return m_lReadyState;
}

////////////////////////////////////////////////////////////////////////////
// COleControl::XFontNotification

STDMETHODIMP_(ULONG) COleControl::XFontNotification::AddRef()
{
	return 1;
}

STDMETHODIMP_(ULONG) COleControl::XFontNotification::Release()
{
	return 0;
}

STDMETHODIMP COleControl::XFontNotification::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	ASSERT(ppvObj != NULL);

	if (IsEqualIID(iid, IID_IUnknown) ||
		IsEqualIID(iid, IID_IPropertyNotifySink))
	{
		*ppvObj = this;
		return S_OK;
	}

	return E_NOINTERFACE;
}

STDMETHODIMP COleControl::XFontNotification::OnChanged(DISPID)
{
	METHOD_PROLOGUE_EX(COleControl, FontNotification)
	pThis->OnFontChanged();
	pThis->m_bModified = TRUE;
	pThis->BoundPropertyChanged(DISPID_FONT);
	return S_OK;
}

STDMETHODIMP COleControl::XFontNotification::OnRequestEdit(DISPID)
{
	return S_OK;
}

CFont* COleControl::SelectStockFont(CDC* pDC)
{
	return SelectFontObject(pDC, m_font);
}

CFont* COleControl::SelectFontObject(CDC* pDC, CFontHolder& fontHolder)
{
	return fontHolder.Select(pDC, m_rcBounds.Height(), m_cyExtent);
}

void COleControl::GetStockTextMetrics(LPTEXTMETRIC lptm)
{
	m_font.QueryTextMetrics(lptm);
}

void COleControl::GetFontTextMetrics(LPTEXTMETRIC lptm, CFontHolder& fontHolder)
{
	fontHolder.QueryTextMetrics(lptm);
}

/////////////////////////////////////////////////////////////////////////////
// Ambient property access

BOOL AFXAPI _GetI4Property(LPDISPATCH pDispatch, DISPID dwDispID, DWORD* pdwResult)
{
	if (pDispatch == NULL)
		return FALSE;

	DISPPARAMS dispparams;
	memset(&dispparams, 0, sizeof dispparams);

	VARIANT vaResult;
	AfxVariantInit(&vaResult);

	EXCEPINFO excepInfo;
	memset(&excepInfo, 0, sizeof excepInfo);

	UINT nArgErr = (UINT)-1;  // initialize to invalid arg

	HRESULT hr = pDispatch->Invoke(dwDispID, IID_NULL, 0, DISPATCH_PROPERTYGET,
		&dispparams, &vaResult, &excepInfo, &nArgErr);

	if (SUCCEEDED(hr))
	{
		if ((V_VT(&vaResult) == VT_I4) ||
			SUCCEEDED(VariantChangeType(&vaResult, &vaResult, 0, VT_I4)))
		{
			*pdwResult = V_I4(&vaResult);
			return TRUE;
		}
	}

	VariantClear(&vaResult);

	if (excepInfo.bstrSource != NULL)
		SysFreeString(excepInfo.bstrSource);
	if (excepInfo.bstrDescription != NULL)
		SysFreeString(excepInfo.bstrDescription);
	if (excepInfo.bstrHelpFile != NULL)
		SysFreeString(excepInfo.bstrHelpFile);

	return FALSE;
}

COleDispatchDriver* COleControl::GetAmbientDispatchDriver()
{
	if (m_ambientDispDriver.m_lpDispatch == NULL)
	{
		// Initialize pointer to ambient property dispinterface.
		IDispatch* pDispatch = NULL;

		if (m_pClientSite != NULL &&
			SUCCEEDED(m_pClientSite->QueryInterface(IID_IDispatch,
				reinterpret_cast<void**>(&pDispatch))))
		{
			ASSERT(pDispatch != NULL);
			m_ambientDispDriver.AttachDispatch(pDispatch);
		}
	}

	return &m_ambientDispDriver;
}

BOOL COleControl::GetAmbientProperty(DISPID dwDispID, VARTYPE vtProp, void* pvProp)
{
	// First, check whether ambient property can be obtained from cache.

	_AFXCTL_AMBIENT_CACHE* pAmbientCache = _afxAmbientCache;
	if (pAmbientCache->m_bValid)
	{
		switch (vtProp)
		{
		// Fetch boolean by selecting appropriate flag in cache.
		case VT_BOOL:
			{
				static const DWORD _dwAmbientFlags[] =
				{
					QACONTAINER_MESSAGEREFLECT,     // -706
					0,                              // -707
					0,                              // -708
					QACONTAINER_USERMODE,           // -709
					QACONTAINER_UIDEAD,             // -710
					QACONTAINER_SHOWGRABHANDLES,    // -711
					QACONTAINER_SHOWHATCHING,       // -712
					QACONTAINER_DISPLAYASDEFAULT,   // -713
					QACONTAINER_SUPPORTSMNEMONICS,  // -714
					QACONTAINER_AUTOCLIP,           // -715
				};

				int nIndex = DISPID_AMBIENT_MESSAGEREFLECT - dwDispID;
				DWORD dwFlag;
				if (nIndex >= 0 && nIndex < _countof(_dwAmbientFlags) &&
					(dwFlag = _dwAmbientFlags[nIndex]) != 0)
				{
					*(BOOL*)pvProp =
						((pAmbientCache->m_dwAmbientFlags & dwFlag) != 0);
					return TRUE;
				}
			}
			break;

		// Fetch color, appearance, or font from corresponding cache entry.
		case VT_I4:
			switch (dwDispID)
			{
			case DISPID_AMBIENT_FORECOLOR:
				*(DWORD*)pvProp = pAmbientCache->m_colorFore;
				return TRUE;

			case DISPID_AMBIENT_BACKCOLOR:
				*(DWORD*)pvProp = pAmbientCache->m_colorBack;
				return TRUE;

			case DISPID_AMBIENT_APPEARANCE:
				*(DWORD*)pvProp = pAmbientCache->m_dwAppearance;
				return TRUE;
			}
			break;

		case VT_I2:
			if (dwDispID == DISPID_AMBIENT_APPEARANCE)
			{
				*(short*)pvProp = (short)pAmbientCache->m_dwAppearance;
				return TRUE;
			}
			break;

		case VT_DISPATCH:
			if ((dwDispID == DISPID_AMBIENT_FONT) &&
				(pAmbientCache->m_pFont != NULL) &&
				SUCCEEDED(pAmbientCache->m_pFont->QueryInterface(IID_IFontDisp,
					reinterpret_cast<void**>(pvProp))))
			{
				return TRUE;
			}
			break;
		}
	}

	// If there's no ambient dispatch interface available, then fail.

	COleDispatchDriver* pDispDriver = GetAmbientDispatchDriver();
	if (pDispDriver->m_lpDispatch == NULL)
		return FALSE;

	// If requested property is of type VT_I4, use optimized function.

	if (vtProp == VT_I4)
		return _GetI4Property(pDispDriver->m_lpDispatch, dwDispID,
			(DWORD*)pvProp);

	// If none of the above apply, just use the dispatch driver.

	BOOL bSuccess = FALSE;
	TRY
	{
		pDispDriver->GetProperty(dwDispID, vtProp, pvProp);
		bSuccess = TRUE;
	}
	END_TRY

	return bSuccess;
}

short COleControl::AmbientAppearance()
{
	DWORD dwAppearance;
	if (!GetAmbientProperty(DISPID_AMBIENT_APPEARANCE, VT_I4, &dwAppearance))
		dwAppearance = 0;
	return (short)dwAppearance;
}

OLE_COLOR COleControl::AmbientBackColor()
{
	OLE_COLOR clrBackColor;
	if (!GetAmbientProperty(DISPID_AMBIENT_BACKCOLOR, VT_I4, &clrBackColor))
		clrBackColor = GetSysColor(COLOR_WINDOW);
	return clrBackColor;
}

CString COleControl::AmbientDisplayName()
{
	CString strDisplayName;
	GetAmbientProperty(DISPID_AMBIENT_DISPLAYNAME, VT_BSTR, &strDisplayName);
	return strDisplayName;
}

LPFONTDISP COleControl::AmbientFont()
{
	// Note: Caller MUST Release the font!
	LPFONTDISP pDisp;
	if (!GetAmbientProperty(DISPID_AMBIENT_FONT, VT_DISPATCH, &pDisp))
		pDisp = NULL;
	return pDisp;
}

OLE_COLOR COleControl::AmbientForeColor()
{
	OLE_COLOR clrForeColor;
	if (!GetAmbientProperty(DISPID_AMBIENT_FORECOLOR, VT_I4, &clrForeColor))
		clrForeColor = GetSysColor(COLOR_WINDOWTEXT);
	return clrForeColor;
}

LCID COleControl::AmbientLocaleID()
{
	LCID lcid;
	if (!GetAmbientProperty(DISPID_AMBIENT_LOCALEID, VT_I4, &lcid))
		lcid = 0;
	return lcid;
}

CString COleControl::AmbientScaleUnits()
{
	CString strScaleUnits;
	GetAmbientProperty(DISPID_AMBIENT_SCALEUNITS, VT_BSTR, &strScaleUnits);
	return strScaleUnits;
}

short COleControl::AmbientTextAlign()
{
	short iTextAlign;
	if (!GetAmbientProperty(DISPID_AMBIENT_TEXTALIGN, VT_I2, &iTextAlign))
		iTextAlign = 0;
	return iTextAlign;
}

BOOL COleControl::AmbientUserMode()
{
	BOOL bUserMode;
	if (!GetAmbientProperty(DISPID_AMBIENT_USERMODE, VT_BOOL, &bUserMode))
		bUserMode = TRUE;
	return bUserMode;
}

BOOL COleControl::AmbientUIDead()
{
	BOOL bUIDead;
	if (!GetAmbientProperty(DISPID_AMBIENT_UIDEAD, VT_BOOL, &bUIDead))
		bUIDead = FALSE;
	return bUIDead;
}

BOOL COleControl::AmbientShowGrabHandles()
{
	BOOL bShowGrab;
	if (!GetAmbientProperty(DISPID_AMBIENT_SHOWGRABHANDLES, VT_BOOL, &bShowGrab))
		bShowGrab = TRUE;
	return bShowGrab;
}

BOOL COleControl::AmbientShowHatching()
{
	BOOL bShowHatch;
	if (!GetAmbientProperty(DISPID_AMBIENT_SHOWHATCHING, VT_BOOL, &bShowHatch))
		bShowHatch = TRUE;
	return bShowHatch;
}

/////////////////////////////////////////////////////////////////////////////
// Calls to IPropertyNotifySink

void COleControl::BoundPropertyChanged(DISPID dispid)
{
	POSITION pos = m_xPropConnPt.GetStartPosition();
	LPPROPERTYNOTIFYSINK pPropNotifySink;

	while (pos != NULL)
	{
		pPropNotifySink =
			(LPPROPERTYNOTIFYSINK)m_xPropConnPt.GetNextConnection(pos);
		ASSERT(pPropNotifySink != NULL);
		pPropNotifySink->OnChanged(dispid);
	}
}

BOOL COleControl::BoundPropertyRequestEdit(DISPID dispid)
{
	POSITION pos = m_xPropConnPt.GetStartPosition();
	LPPROPERTYNOTIFYSINK pPropNotifySink;

	while (pos != NULL)
	{
		pPropNotifySink =
			(LPPROPERTYNOTIFYSINK)m_xPropConnPt.GetNextConnection(pos);
		ASSERT(pPropNotifySink != NULL);
		if (pPropNotifySink->OnRequestEdit(dispid) != S_OK)
			return FALSE;
	}

	// All of the sinks said yes, so it's ok.
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Function to call when BoundPropertyRequestEdit fails

void COleControl::SetNotPermitted()
{
	ThrowError(CTL_E_SETNOTPERMITTED, AFX_IDP_E_SETNOTPERMITTED);
}

/////////////////////////////////////////////////////////////////////////////
// Placeholder functions for read-only or write-only properties

void COleControl::SetNotSupported()
{
	ThrowError(CTL_E_SETNOTSUPPORTED, AFX_IDP_E_SETNOTSUPPORTED);
}

void COleControl::GetNotSupported()
{
	ThrowError(CTL_E_GETNOTSUPPORTED, AFX_IDP_E_GETNOTSUPPORTED);
}

/////////////////////////////////////////////////////////////////////////////
// COleControl::XPerPropertyBrowsing

STDMETHODIMP_(ULONG) COleControl::XPerPropertyBrowsing::AddRef()
{
	METHOD_PROLOGUE_EX_(COleControl, PerPropertyBrowsing)
	return (ULONG)pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) COleControl::XPerPropertyBrowsing::Release()
{
	METHOD_PROLOGUE_EX_(COleControl, PerPropertyBrowsing)
	return (ULONG)pThis->ExternalRelease();
}

STDMETHODIMP COleControl::XPerPropertyBrowsing::QueryInterface(
	REFIID iid, LPVOID* ppvObj)
{
	METHOD_PROLOGUE_EX_(COleControl, PerPropertyBrowsing)
	return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP COleControl::XPerPropertyBrowsing::GetDisplayString(
	DISPID dispid, BSTR* lpbstr)
{
	METHOD_PROLOGUE_EX(COleControl, PerPropertyBrowsing)

	ASSERT_NULL_OR_POINTER(lpbstr, BSTR);

	CString strValue;
	BOOL bSuccess = pThis->OnGetDisplayString(dispid, strValue);

	if (lpbstr != NULL)
		*lpbstr = (bSuccess ? strValue.AllocSysString() : NULL);

	return bSuccess ? S_OK : S_FALSE;
}

BOOL COleControl::OnGetDisplayString(DISPID dispid, CString& strValue)
{
	TRY
	{
		switch (dispid)
		{
		case DISPID_FONT:
			return m_font.GetDisplayString(strValue);

		case DISPID_BORDERSTYLE:
			return strValue.LoadString(m_sBorderStyle == 0 ?
				AFX_IDS_BORDERSTYLE_0 : AFX_IDS_BORDERSTYLE_1);
		}
	}
	END_TRY

	return FALSE;
}

STDMETHODIMP COleControl::XPerPropertyBrowsing::MapPropertyToPage(
	DISPID dispid, LPCLSID lpclsid)
{
	METHOD_PROLOGUE_EX(COleControl, PerPropertyBrowsing)

	ASSERT_NULL_OR_POINTER(lpclsid, CLSID);

	CLSID clsid = GUID_NULL;
	BOOL bPageOptional = FALSE;
	BOOL bSuccess = pThis->OnMapPropertyToPage(dispid, &clsid, &bPageOptional);

	if (lpclsid != NULL)
		*lpclsid = (bSuccess ? clsid : GUID_NULL);

	return bSuccess ? (bPageOptional ? S_OK : S_FALSE) :
		PERPROP_E_NOPAGEAVAILABLE;
}

BOOL COleControl::OnMapPropertyToPage(DISPID dispid, LPCLSID lpclsid,
	BOOL* pbPageOptional)
{
	switch (dispid)
	{
	case DISPID_FONT:
		*lpclsid = CLSID_CFontPropPage;
		*pbPageOptional = TRUE;
		return TRUE;

	case DISPID_BACKCOLOR:
	case DISPID_FORECOLOR:
		*lpclsid = CLSID_CColorPropPage;
		*pbPageOptional = TRUE;
		return TRUE;
	}

	return FALSE;
}

inline LPOLESTR AFXAPI _AfxCopyString(LPCTSTR psz)
{
	if (psz == NULL)
		return NULL;

	int cch = lstrlen(psz) + 1;
	LPOLESTR pszCopy = NULL;

	if ((pszCopy = (LPOLESTR)CoTaskMemAlloc(cch * sizeof(OLECHAR))) != NULL)
	{
#ifdef _UNICODE
		wcscpy(pszCopy, psz);
#elif !defined(OLE2ANSI)
		MultiByteToWideChar(CP_ACP, 0, psz, -1, pszCopy, cch);
#else
		lstrcpy(pszCopy, psz);
#endif
	}

	return pszCopy;
}

STDMETHODIMP COleControl::XPerPropertyBrowsing::GetPredefinedStrings(
	DISPID dispid, CALPOLESTR* lpcaStringsOut, CADWORD* lpcaCookiesOut)
{
	METHOD_PROLOGUE_EX(COleControl, PerPropertyBrowsing)

	if ((lpcaStringsOut == NULL) || (lpcaCookiesOut == NULL))
		return E_POINTER;

	ASSERT_POINTER(lpcaStringsOut, CALPOLESTR);
	ASSERT_POINTER(lpcaCookiesOut, CADWORD);

	CStringArray stringArray;
	CDWordArray cookieArray;

	BOOL bSuccess = pThis->OnGetPredefinedStrings(dispid, &stringArray,
		&cookieArray);

	if (bSuccess)
	{
		// Allocate and fill arrays to return.

		ASSERT(stringArray.GetSize() == cookieArray.GetSize());

		int iElem = 0;
		LPOLESTR lpszCopy;
		ULONG cElems = stringArray.GetSize();

		lpcaStringsOut->pElems = (LPOLESTR*)CoTaskMemAlloc(
			sizeof(LPOLESTR) * cElems);

		if (lpcaStringsOut->pElems == NULL)
			return E_OUTOFMEMORY;

		lpcaCookiesOut->pElems = (DWORD*)CoTaskMemAlloc(
			sizeof(DWORD*) * cElems);

		if (lpcaCookiesOut->pElems == NULL)
		{
			CoTaskMemFree(lpcaStringsOut->pElems);
			return E_OUTOFMEMORY;
		}

		lpcaStringsOut->cElems = cElems;
		lpcaCookiesOut->cElems = cElems;

		for (iElem = 0; iElem < (int)cElems; iElem++)
		{
			lpszCopy = _AfxCopyString(stringArray.GetAt(iElem));

			if (lpszCopy == NULL)
			{
				// cleanup everything allocated so far...
				while (--iElem >= 0)
					CoTaskMemFree(lpcaStringsOut->pElems[iElem]);

				CoTaskMemFree(lpcaCookiesOut->pElems);
				CoTaskMemFree(lpcaStringsOut->pElems);

				return E_OUTOFMEMORY;
			}

			lpcaStringsOut->pElems[iElem] = lpszCopy;
			lpcaCookiesOut->pElems[iElem] = cookieArray.GetAt(iElem);
		}
	}

	return bSuccess ? S_OK : S_FALSE;
}

BOOL COleControl::OnGetPredefinedStrings(DISPID dispid,
	CStringArray* pStringArray, CDWordArray* pCookieArray)
{
	BOOL bResult = FALSE;

	switch (dispid)
	{
	case DISPID_BORDERSTYLE:
		TRY
		{
			CString str;
			str.LoadString(AFX_IDS_BORDERSTYLE_0);
			pStringArray->Add(str);
			pCookieArray->Add(0);
			str.LoadString(AFX_IDS_BORDERSTYLE_1);
			pStringArray->Add(str);
			pCookieArray->Add(1);
			bResult = TRUE;
		}
		CATCH (CException, e)
		{
			pStringArray->RemoveAll();
			pCookieArray->RemoveAll();
			bResult = FALSE;
		}
		END_CATCH
		break;
	}

	return bResult;
}

STDMETHODIMP COleControl::XPerPropertyBrowsing::GetPredefinedValue(
	DISPID dispid, DWORD dwCookie, VARIANT* lpvarOut)
{
	METHOD_PROLOGUE_EX(COleControl, PerPropertyBrowsing)

	ASSERT_POINTER(lpvarOut, VARIANT);

	return pThis->OnGetPredefinedValue(dispid, dwCookie, lpvarOut) ?
		S_OK : E_FAIL;
}

BOOL COleControl::OnGetPredefinedValue(DISPID dispid, DWORD dwCookie,
	VARIANT* lpvarOut)
{
	switch (dispid)
	{
	case DISPID_BORDERSTYLE:
		if ((dwCookie == 0) || (dwCookie == 1))
		{
			VariantClear(lpvarOut);
			V_VT(lpvarOut) = VT_I4;
			V_I4(lpvarOut) = dwCookie;
			return TRUE;
		}
		break;
	}

	return FALSE;
}

#ifndef _MAC
void COleControl::Load(LPCTSTR strNewPath, CDataPathProperty& prop)
{
	prop.SetControl(this);
	prop.Open(strNewPath);
}
#endif

/////////////////////////////////////////////////////////////////////////////
// CDataPathProperty implementation

#ifndef _MAC

BOOL CDataPathProperty::Open(CFileException* pError)
{
	return CAsyncMonikerFile::Open(m_strPath, m_pControl ? m_pControl->GetClientSite() : NULL, pError);
}

BOOL CDataPathProperty::Open(LPCTSTR lpszPath, CFileException* pError)
{
	SetPath(lpszPath);
	return Open(pError);
}

BOOL CDataPathProperty::Open(COleControl* pControl, CFileException* pError)
{
	SetControl(pControl);
	return Open(pError);
}

BOOL CDataPathProperty::Open(LPCTSTR lpszPath, COleControl* pControl, CFileException* pError)
{
	SetControl(pControl);
	SetPath(lpszPath);
	return Open(pError);
}

void CDataPathProperty::ResetData()
{
}

#ifdef _DEBUG
void CDataPathProperty::AssertValid() const
{
	CAsyncMonikerFile::AssertValid();
}

void CDataPathProperty::Dump(CDumpContext& dc) const
{
	CAsyncMonikerFile::Dump(dc);

	dc << "\nm_pControl = " << m_pControl;
	dc << "\nm_strPath = \"" << m_strPath;
	dc << "\"\n";
}
#endif //_DEBUG

#endif // !_MAC

/////////////////////////////////////////////////////////////////////////////
// CCachedDataPathProperty implementation

#ifndef _MAC

static inline
DWORD AfxTransferFileContent(CFile* pFrom, CFile* pTo)
{
	BYTE buff[1024];
	DWORD dwRead = 0;
	DWORD dwActual;
	do
	{
		dwActual = pFrom->Read(buff, 1024);
		pTo->Write(buff, dwActual);

		dwRead += dwActual;
	}
	while (dwActual > 0);
	return dwRead;
}

void CCachedDataPathProperty::OnDataAvailable(DWORD dwSize, DWORD bscfFlag)
{
	UNUSED_ALWAYS(bscfFlag);
	UNUSED_ALWAYS(dwSize);
	DWORD dwPos = m_Cache.GetPosition();
	TRY
	{
		// Cache the data in our mem file.
		m_Cache.SeekToEnd();
		AfxTransferFileContent(this, &m_Cache);
	}
	CATCH_ALL(e)
	{
		m_Cache.Seek(dwPos, CFile::begin);
		THROW_LAST();
	}
	END_CATCH_ALL
	m_Cache.Seek(dwPos, CFile::begin);
}

void CCachedDataPathProperty::Close()
{
	m_Cache.SetLength(0);
	CDataPathProperty::Close();
}

void CCachedDataPathProperty::ResetData()
{
	m_Cache.SetLength(0);
}
#ifdef _DEBUG
void CCachedDataPathProperty::AssertValid() const
{
	CDataPathProperty::AssertValid();
	m_Cache.AssertValid();
}

void CCachedDataPathProperty::Dump(CDumpContext& dc) const
{
	CDataPathProperty::Dump(dc);

	m_Cache.Dump(dc);
}
#endif //_DEBUG

#endif // !_MAC

/////////////////////////////////////////////////////////////////////////////
// Force any extra compiler-generated code into AFX_INIT_SEG

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

#ifndef _MAC
IMPLEMENT_DYNAMIC(CDataPathProperty, CAsyncMonikerFile)
IMPLEMENT_DYNAMIC(CCachedDataPathProperty, CDataPathProperty)
#endif //!_MAC
