Click here to Skip to main content
15,895,709 members
Articles / Desktop Programming / ATL

SP Numeric Edit Control

Rate me:
Please Sign up or sign in to vote.
4.78/5 (11 votes)
16 Nov 200511 min read 75K   3.6K   36  
Masked numeric edit ActiveX control.
/////////////////////////////////////////////////////////////////////////////////////////
//	Project:		SP Numeric Edit Control 1.0
//
//	File:			NumericEditBox.cpp
//
//	Developer(s):	Sergei Pavlovsky
//					sergei_vp@hotmail.com
//					Copyright (c) 2004
//
//	Description:	Implementation of CoNumericEditBox class.
//					CoNumericEditBox represents ActiveX control.
//
//	Platforms:		Win32
//
//	This code may be used in compiled form in any way you desire. This file may be 
//	redistributed unmodified by any means PROVIDING it is not sold for profit without 
//	the authors written consent, and providing that this notice and the authors name 
//	is included. If the source code in this file is used in any commercial application 
//	then acknowledgement must be made to the author of this file (in whatever form 
//	you wish).
//
//	This file is provided "as is" with no expressed or implied warranty. The author 
//	accepts no liability for any damage/loss of business that this product may cause.
/////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "NumericEditBox.h"
#include "SpNumericEditModule.h"
#include <map>

/////////////////////////////////////////////////////////////////////////////////////////
// CoNumericEditBox::CFontPropertyNotifySink class implementation
/////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
// IPropertyNotifySink implementation

STDMETHODIMP CoNumericEditBox::CFontPropertyNotifySink::OnRequestEdit(DISPID dispid)
{
	if ( m_pOwner )
	{
		if ( m_pOwner->m_nFreezeEvents )
			return S_FALSE;

		return m_pOwner->FireOnRequestEdit(DISPID_FONT); 
	}

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::CFontPropertyNotifySink::OnChanged(DISPID dispid)
{
	if ( m_pOwner )
	{
		m_pOwner->m_ctlEdit._OnFontChanging();

		m_pOwner->m_bRequiresSave = TRUE;

		if ( m_pOwner->m_nFreezeEvents == 0 )
			m_pOwner->FireOnChanged(DISPID_FONT);

		m_pOwner->m_ctlEdit._OnFontChanged();

		m_pOwner->FireViewChange();
		m_pOwner->SendOnDataChange(NULL);
	}

	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////////////////
// CoNumericEditBox class implementation
/////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
// Constants

LPCOLESTR CoNumericEditBox::ms_rgszParamNames[] =
{
	L"ValueType",
	L"FormatType",
	L"DisplayMask",
	L"EditingMask"
};

SIZE_T CoNumericEditBox::ms_cchMaxParamName = 11;

/////////////////////////////////////////////////////////////////////////////////////////
// Construction & destruction

#pragma warning(push)
#pragma warning(disable: 4355) // 'this' : used in base member initializer list

CoNumericEditBox::CoNumericEditBox()
	: m_ctlEdit(_T("Edit"), this, 1),
	  m_pcoFontNS(NULL),
	  m_pcoFormatter(NULL)
{
	m_bWindowOnly = TRUE;
}

#pragma warning(pop)

CoNumericEditBox::~CoNumericEditBox()
{
	ATLASSERT( m_pcoFontNS == NULL );
	ATLASSERT( m_pcoFormatter == NULL );
}

HRESULT CoNumericEditBox::FinalConstruct()
{
	ATLASSERT( !m_pcoFontNS );

	// Set internal event sink to edit window.
	m_ctlEdit.AdviseEventSink(this);

	// Create Property Notify sink
	HRESULT hr = CComObject<CFontPropertyNotifySink>::CreateInstance(&m_pcoFontNS);	
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
		return hr;

	m_pcoFontNS->AddRef();
	m_pcoFontNS->Initialize(this);

	return S_OK;
}

void CoNumericEditBox::FinalRelease() 
{
	ATLASSERT( m_pcoFontNS );

	// Free Property Notify sink
	if ( m_pcoFontNS->GetCookie() )
		m_pcoFontNS->Unadvise(m_ctlEdit._GetRefFont());

	m_pcoFontNS->Uninitialize();
	m_pcoFontNS->Release();
	m_pcoFontNS = NULL;

	// Discard internal event sink of edit window.
	m_ctlEdit.UnadviseEventSink();
}

/////////////////////////////////////////////////////////////////////////////////////////
// Helpers: Parsing of Params string

bool CoNumericEditBox::ReadParamID(LPCOLESTR lpParams, UINT cchParams, UINT unPos, 
								   UINT* punNext, ParamsEnum* penID)
{
	ATLASSERT( !::IsBadReadPtr(lpParams, cchParams * sizeof(OLECHAR)) );
	ATLASSERT( unPos < cchParams);
	ATLASSERT( !::IsBadReadPtr(punNext, sizeof(UINT)) );
	ATLASSERT( !::IsBadReadPtr(penID, sizeof(ParamsEnum)) );

	*penID = TN_NON;

	olestring sParamName;
	sParamName.reserve(ms_cchMaxParamName);
	*punNext = unPos;

	for ( UINT i = unPos; i < cchParams; i++ )
	{
		OLECHAR chCurrent = lpParams[i];

		if ( chCurrent == L' ' || 
			 chCurrent == L'\x9' ||
			 chCurrent == L'=' )
			break;

		sParamName.push_back(chCurrent);
 	}

	*punNext = i;

	for ( UINT i = 0; i < sizeof(ms_rgszParamNames)/sizeof(LPCOLESTR); i++ )
	{
		LPCOLESTR lpcszCurrentParamName = ms_rgszParamNames[i];

		if ( sParamName == lpcszCurrentParamName )
		{
			*penID = static_cast<ParamsEnum>(i + 1);
			break;
		}
	}

	if ( !(*penID) )
	{
		UINT_PTR cBuf;

		// Load Error Source
		cBuf = _AtlModule.LoadString(IDS_ELS_SPNUMERICEDITBOX, NULL, 0);

		LPTSTR lpszSrc = LPTSTR(_alloca((cBuf + 1) * sizeof(TCHAR)));
		_AtlModule.LoadString(IDS_ELS_SPNUMERICEDITBOX, lpszSrc, cBuf + 1);
		lpszSrc[cBuf] = _T('\x0');

		// Load Error Reason format 
		cBuf = _AtlModule.LoadString(IDS_ERCC_UNRECOGNIZEDPARAM, NULL, 0);

		LPTSTR lpszFmt = LPTSTR(_alloca((cBuf + 1) * sizeof(TCHAR)));
		_AtlModule.LoadString(IDS_ERCC_UNRECOGNIZEDPARAM, lpszFmt, cBuf + 1);
		lpszFmt[cBuf] = _T('\x0');

		// Format Error Message
		cBuf += sParamName.length();
		LPTSTR lpszMsg = LPTSTR(_alloca((cBuf + 1) * sizeof(TCHAR)));
		_stprintf(lpszMsg, lpszFmt, sParamName.c_str());
		
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, lpszSrc, 0, lpszMsg);

		return false;
	}

	return true;
}

void CoNumericEditBox::WriteParamID(ParamsEnum enID, olestring& sParams)
{
	ATLASSERT( enID <= sizeof(ms_rgszParamNames)/sizeof(LPCOLESTR) );

	sParams.append(ms_rgszParamNames[enID - 1]);
}

bool CoNumericEditBox::ReadParamValue(LPCOLESTR lpParams, UINT cchParams, UINT unPos, 
									  UINT* punNext, BSTR* pbsValue)
{
	ATLASSERT( !::IsBadReadPtr(lpParams, cchParams * sizeof(OLECHAR)) );
	ATLASSERT( unPos < cchParams);
	ATLASSERT( !::IsBadReadPtr(punNext, sizeof(UINT)) );
	ATLASSERT( !::IsBadReadPtr(pbsValue, sizeof(BSTR)) );

	olestring sValue;
	sValue.reserve(cchParams - unPos);
	*punNext = unPos;

	if ( unPos < cchParams && lpParams[unPos] == L'"' )
	{
		// Value is surrounded with double quotes.
		for ( UINT i = unPos + 1; i < cchParams; i++ )
		{
			OLECHAR chCurrent = lpParams[i];

			if ( chCurrent == L'"' )
			{
				if ( i + 1 < cchParams && lpParams[i + 1] == L'"' )
					i++;
				else
					break;
			}

			sValue.push_back(chCurrent);
		}

		if ( lpParams[i] != L'"' || i == unPos )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							 IDS_ERCC_NOCLOSINGQUOTE);
			return false;
		}

		*punNext = i + 1;

		*pbsValue = ::SysAllocStringLen(sValue.c_str(), UINT(sValue.length()));
	}
	else
	{
		// Value is not surrounded with double quotes.
		for ( UINT i = unPos; i < cchParams; i++ )
		{
			OLECHAR chCurrent = lpParams[i];

			if ( chCurrent == L';' )
				break;

			if ( chCurrent == L'"' )
			{
				SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 
								 0, IDS_ERCC_PARAMVALUEQUOTES);
				return false;
			}

			sValue.push_back(chCurrent);
		}

		*punNext = i;

		if ( !sValue.empty() )
		{
			*pbsValue = ::SysAllocStringLen(sValue.c_str(), UINT(sValue.length()));
			ATLASSERT( *pbsValue );
			if ( !(*pbsValue) )
			{
				SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_COM, 2, 
								 IDS_ELR_OUTOFMEMORY);
				return false;
			}
		}
		else
		{
			*pbsValue = NULL;
		}
	}

	return true;
}

void CoNumericEditBox::WriteParamValue(LPCOLESTR lpValue, UINT cchValue, 
									   olestring& sParams,  bool bInsideQuotes)
{
	ATLASSERT( !::IsBadReadPtr(lpValue, cchValue * sizeof(WCHAR)) );

	if ( bInsideQuotes )
	{
		sParams.push_back(L'\"');

		sParams.reserve(sParams.size() + cchValue);

		for ( UINT i = 0; i < cchValue; i++ )
		{
			OLECHAR chCurrent = lpValue[i];

			if ( chCurrent == L'\"' )
				sParams.push_back(L'\"');
				
			sParams.push_back(chCurrent);
		}

		sParams.push_back(L'\"');
	}
	else
	{
		sParams.append(lpValue, lpValue + cchValue);
	}
}

/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Parsing of Params string

HRESULT CoNumericEditBox::ParseFormatterParamString(BSTR bsParams, 
													ValueTypeConstants* penValueType,
													FormatTypeConstants* penFormatType,
													BSTR* pbsDisplayMask,
													BSTR* pbsEditingMask)
{
	ATLASSERT( !::IsBadWritePtr(penValueType, sizeof(ValueTypeConstants)) );
	ATLASSERT( !::IsBadWritePtr(penFormatType, sizeof(FormatTypeConstants)) );
	ATLASSERT( !::IsBadWritePtr(pbsDisplayMask, sizeof(BSTR)) );
	ATLASSERT( !::IsBadWritePtr(pbsEditingMask, sizeof(BSTR)) );

	enum ParseStatusEnum
	{
		PS_EXPECTPARAMNAME = 0,
		PS_EXPECTASSIGNMENT = 1,
		PS_EXPECTPARAMVALUE = 2,
		PS_EXPECTPARAMEND = 3
	};

	typedef std::map<ParamsEnum, CComBSTR> CParamValuesMap;

	*penValueType = ValueTypeConstants(0);
	*penFormatType = FormatTypeConstants(0);
	*pbsDisplayMask = NULL; 
	*pbsEditingMask = NULL;

	if ( bsParams )
	{
		CParamValuesMap mapParams;
		std::pair<CParamValuesMap::iterator, bool> pairInsRes;

		ParamsEnum enCurrentParam = TN_NON;
		CComBSTR cbsCurrentValue;

		ParseStatusEnum enParseStatus = PS_EXPECTPARAMNAME;

		UINT cchParams = ::SysStringLen(bsParams);

		for ( UINT i = 0; i < cchParams; )
		{
			switch ( bsParams[i] )
			{
			case _T(' '):
			case _T('\x9'):
				i++; // Skip white spaces
				break;

			case _T('='):
				if ( enParseStatus != PS_EXPECTASSIGNMENT )
				{
					SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 
									0, IDS_ERCC_UNEXPECTEDASSIGNMENTSIGN);
					return E_FAIL;
				}

				i++;
				enParseStatus = PS_EXPECTPARAMVALUE;
				break;

			case _T(';'):
				if ( enParseStatus != PS_EXPECTPARAMEND &&
					 enParseStatus != PS_EXPECTPARAMVALUE )
				{
					SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 
									 0, IDS_ERCC_UNEXPECTEDSEMICOLONSIGN);					
					return E_FAIL;
				}

				pairInsRes = mapParams.insert(CParamValuesMap::value_type(enCurrentParam,
																		cbsCurrentValue));
				if ( !pairInsRes.second )
				{
					SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 
									 0, IDS_ERCC_DUAPLICATEDPARAM);					
					return E_FAIL;
				}

				enCurrentParam = TN_NON;
				cbsCurrentValue = BSTR(NULL);

				i++;
				enParseStatus = PS_EXPECTPARAMNAME;
				break;

			default:
				if ( enParseStatus == PS_EXPECTPARAMNAME )
				{
					// Define Param
					UINT iNext;
					bool bResult = ReadParamID(bsParams, cchParams, i, 
											   &iNext, &enCurrentParam);
					ATLASSERT( bResult );
					if ( !bResult )
					{
						SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 
										 0, IDS_ERCC_READPARAMNAME);					
						return E_FAIL;
					}

					enParseStatus = PS_EXPECTASSIGNMENT;
					i = iNext;
				}
				else 
				{
					ATLASSERT( enParseStatus == PS_EXPECTPARAMVALUE );

					UINT iNext;
					bool bResult = ReadParamValue(bsParams, cchParams, i, 
												&iNext, &cbsCurrentValue);
					ATLASSERT( bResult );
					if ( !bResult )
					{
						SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 
										 0, IDS_ERCC_READPARAMVALUE);					
						return E_FAIL;
					}

					enParseStatus = PS_EXPECTPARAMEND;
					i = iNext;
				}
				break;
			}
		}

		// Add last parameter
		if ( enCurrentParam != TN_NON )
		{
			pairInsRes = mapParams.insert(CParamValuesMap::value_type(enCurrentParam,
																		cbsCurrentValue));
			if ( !pairInsRes.second )
			{
				SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 
								 0, IDS_ERCC_DUAPLICATEDPARAM);					
				return E_FAIL;
			}
		}

		// Convert parameter values
		CComPtr<ITypeLib> spiTLB;
		BOOL bResult = CSpNumericEditModule::LoadTypeLib(&spiTLB);
		ATLASSERT( bResult );
		if ( !bResult )
		{
			SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							IDS_ERCM_GETTYPELIBRARY);
			return E_FAIL;
		}

		for ( CParamValuesMap::iterator it = mapParams.begin(); 
			it != mapParams.end(); it++ )
		{
			CComBSTR& cbsValue = (*it).second;

			switch ( (*it).first )
			{
			case TN_VALUETYPE:
				{
					CComPtr<ITypeInfo> spiTI;
					bResult = CSpNumericEditModule::GetValueTypesTypeInfo(spiTLB, &spiTI);
					ATLASSERT( bResult );
					if ( !bResult )
					{
						SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
										 IDS_ERCM_GETVALUETYPEINFORMATION);
						return E_FAIL;
					}
					
					LONG lValue;
					bResult = CSpNumericEditModule::ConvertEnumValueName(spiTI, cbsValue, 
																		 &lValue);
					ATLASSERT( bResult );
					if ( !bResult )
					{
						SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
										 IDS_ERCM_GETVALUETYPECONSTNAME);						
						return E_FAIL;
					}

					*penValueType = ValueTypeConstants(lValue);
				}
				break;

			case TN_FORMATTYPE:
				{
					CComPtr<ITypeInfo> spiTI;
					bResult = CSpNumericEditModule::GetFormatTypesTypeInfo(spiTLB, &spiTI);
					ATLASSERT( bResult );
					if ( !bResult )
					{
						SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
										IDS_ERCM_GETFORMATTYPEINFORMATION);
						return E_FAIL;
					}
					
					LONG lValue;
					bResult = CSpNumericEditModule::ConvertEnumValueName(spiTI, cbsValue, 
																		 &lValue);
					ATLASSERT( bResult );
					if ( !bResult )
					{
						SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
										 IDS_ERCM_GETFORMATTYPECONSTNAME);						
						return E_FAIL;
					}

					*penFormatType = FormatTypeConstants(lValue);
				}
				break;

			case TN_DISPLAYMASK:
				*pbsDisplayMask = cbsValue.Detach();
				break;

			case TN_EDITINGMASK:
				*pbsEditingMask = cbsValue.Detach();
				break;
			}
		}
	}

	return S_OK;
}

HRESULT CoNumericEditBox::BuildFormatterParamString(ValueTypeConstants enValueType,
													FormatTypeConstants enFormatType,
													LPCOLESTR lpszDisplayMask, 
													LPCOLESTR lpszEditingMask,
													BSTR* pbsParams)
{
	ATLASSERT( !::IsBadWritePtr(pbsParams, sizeof(BSTR)) );

	*pbsParams = NULL;

	olestring sBuffer;
	sBuffer.reserve(128);

	CComPtr<ITypeLib> spiTLB;
	BOOL bResult = CSpNumericEditModule::LoadTypeLib(&spiTLB);
	ATLASSERT( bResult );
	if ( !bResult )
	{
		SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCM_GETTYPELIBRARY);
		return E_FAIL;
	}

	// Add ValueType parameter
	if ( enValueType )
	{
		CComPtr<ITypeInfo> spiTI;
		bResult = CSpNumericEditModule::GetValueTypesTypeInfo(spiTLB, &spiTI);
		ATLASSERT( bResult );
		if ( !bResult )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							 IDS_ERCM_GETVALUETYPEINFORMATION);
			return E_FAIL;
		}
		
		CComBSTR cbsValue;
		bResult = CSpNumericEditModule::ConvertEnumValueName(spiTI, enValueType, &cbsValue);
		ATLASSERT( bResult );
		if ( !bResult )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							 IDS_ERCM_GETVALUETYPECONSTNAME);						
			return E_FAIL;
		}

		WriteParamID(TN_VALUETYPE, sBuffer);

		sBuffer.push_back(L'=');

		WriteParamValue(cbsValue, cbsValue.Length(), sBuffer, false);

		sBuffer.push_back(L';');
	}

	// Add FormatType parameter
	if ( enFormatType )
	{
		if ( !sBuffer.empty() )
			sBuffer.push_back(L' ');

		CComPtr<ITypeInfo> spiTI;
		bResult = CSpNumericEditModule::GetFormatTypesTypeInfo(spiTLB, &spiTI);
		ATLASSERT( bResult );
		if ( !bResult )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							IDS_ERCM_GETFORMATTYPEINFORMATION);

			return E_FAIL;
		}
		
		CComBSTR cbsValue;
		bResult = CSpNumericEditModule::ConvertEnumValueName(spiTI, enFormatType, &cbsValue);
		ATLASSERT( bResult );
		if ( !bResult )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							 IDS_ERCM_GETFORMATTYPECONSTNAME);						
			return E_FAIL;
		}

		WriteParamID(TN_FORMATTYPE, sBuffer);

		sBuffer.push_back(L'=');

		WriteParamValue(cbsValue, cbsValue.Length(), sBuffer, false);

		sBuffer.push_back(L';');
	}

	// Add DisplayMask parameter
	if ( lpszDisplayMask )
	{
		if ( !sBuffer.empty() )
			sBuffer.push_back(L' ');

		WriteParamID(TN_DISPLAYMASK, sBuffer);

		sBuffer.push_back(L'=');

		WriteParamValue(lpszDisplayMask, UINT(wcslen(lpszDisplayMask)), sBuffer, true);

		sBuffer.push_back(L';');
	}

	// Add EditingMask parameter
	if ( lpszEditingMask )
	{
		if ( !sBuffer.empty() )
			sBuffer.push_back(L' ');

		WriteParamID(TN_EDITINGMASK, sBuffer);

		sBuffer.push_back(L'=');

		WriteParamValue(lpszEditingMask, UINT(wcslen(lpszEditingMask)), sBuffer, true);

		sBuffer.push_back(L';');
	}

	// Allocate system string
	if ( !sBuffer.empty() )
	{
		*pbsParams = ::SysAllocStringLen(sBuffer.c_str(), UINT(sBuffer.length()));
		ATLASSERT( *pbsParams );
		if ( !(*pbsParams) )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_COM, 2, 
							 IDS_ELR_OUTOFMEMORY);
			return E_OUTOFMEMORY;
		}
	}

	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Formatter

HRESULT CoNumericEditBox::GetFormatter(IFormatter** ppiFormatter)
{
	ATLASSERT( !::IsBadWritePtr(ppiFormatter, sizeof(IFormatter*)) );

	// Code below is not comletely safe to be used with multithreading.
	Lock();

	__try
	{
		if ( !m_pcoFormatter )
		{
			HRESULT hr = CComObject<CoFormatter>::CreateInstance(&m_pcoFormatter);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
			{
				SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCC_CREATEFORMATTER);
				
				return E_FAIL;
			}

			m_pcoFormatter->Initialize(this);
		}

		*ppiFormatter = m_pcoFormatter;

		(*ppiFormatter)->AddRef();
	}
	__finally
	{
		Unlock();
	}

	return S_OK;
}

void CoNumericEditBox::DiscardFormatter()
{
	ATLASSERT( m_pcoFormatter && m_pcoFormatter->IsInitialized() );

	// Code below is not comletely safe to be used with multithreading.
	Lock();

	__try
	{
		m_pcoFormatter->Uninitialize();
		m_pcoFormatter = NULL;
	}
	__finally
	{
		Unlock();
	}
}

HRESULT CoNumericEditBox::_OnRequestEditFormatterParams()
{
	if ( m_nFreezeEvents == 0 )
		return FireOnRequestEdit(DISPID_FORMATTERPARAMS);

	return S_OK;
}

void CoNumericEditBox::_OnFormatterParamsChanged(BOOL bValueTypeChanged, 
												 BOOL bUpdateCtrl)
{
	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_FORMATTERPARAMS);

	if ( bUpdateCtrl )
		m_ctlEdit._OnFormatterChanged(bValueTypeChanged);

	FireViewChange();
	SendOnDataChange(NULL);
}

/////////////////////////////////////////////////////////////////////////////////////////
// Implementation: Masked edit event handlers

void CoNumericEditBox::_OnModified()
{
	Fire_OnModifyChanged();
}

BOOL CoNumericEditBox::_OnValueChanging(const void* lpcNewValue, BOOL bValid)
{
	CComVariant cvtNewValue;
	

	CNumericEditWindow::ConvertValue(m_ctlEdit.GetFormatter().GetValueType(), 
									 lpcNewValue, bValid, &cvtNewValue);

	VARIANT_BOOL vbAccepted;
	Fire_OnValueChanging(cvtNewValue, &vbAccepted);

	return vbAccepted;
}

void CoNumericEditBox::_OnValueChanged()
{
	Fire_OnValueChanged();
}

void CoNumericEditBox::_OnError()
{
	Fire_OnError();
}

/////////////////////////////////////////////////////////////////////////////////////////
// Helpers

void CoNumericEditBox::DrawBorder(HDC hDC, RECT* prc, BOOL bAdjustRect)
{
	if ( !m_ctlEdit._GetBorderVisible() )
		return;

	const DWORD dwDEParams = ( bAdjustRect ) ? BF_RECT|BF_ADJUST : BF_RECT;

	switch( m_ctlEdit._GetAppearance() )
	{
	case acFlat:
		::DrawEdge(hDC, prc, BDR_SUNKENOUTER, dwDEParams|BF_MONO|BF_FLAT|BF_SOFT);
		break;

	case acSoft:
		::DrawEdge(hDC, prc, BDR_SUNKENINNER, dwDEParams|BF_SOFT);
		break;

	case ac3D:
		::DrawEdge(hDC, prc, BDR_SUNKENINNER|BDR_SUNKENOUTER, dwDEParams);
		break;
	}
}

void CoNumericEditBox::CalcMargins(HDC hDC, LONG* pdxLeft, LONG* pdxRight, LONG* pdyTopBottom)
{
	ATLASSERT( !::IsBadWritePtr(pdxLeft, sizeof(LONG)) );
	ATLASSERT( !::IsBadWritePtr(pdxRight, sizeof(LONG)) );
	ATLASSERT( !::IsBadWritePtr(pdyTopBottom, sizeof(LONG)) );

	TEXTMETRIC tm;
	GetTextMetrics(hDC, &tm);

	if ( tm.tmPitchAndFamily & TMPF_TRUETYPE )
	{
		ABC abc;
		::GetCharABCWidths(hDC, _T('A'), _T('A'), &abc);

		*pdxLeft = tm.tmAveCharWidth - abc.abcC;
		*pdxRight = tm.tmAveCharWidth - abc.abcA;;
	}
	else
	{
		*pdxLeft = tm.tmAveCharWidth - tm.tmOverhang;
		*pdxRight = tm.tmAveCharWidth - tm.tmOverhang;
	}

	if ( *pdxLeft < 0 )
		*pdxLeft = 0;

	if ( *pdxRight < 0 )
		*pdxRight = 0;

	*pdyTopBottom = (tm.tmInternalLeading + tm.tmExternalLeading) / 2;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Overrides

BOOL CoNumericEditBox::PreTranslateAccelerator(LPMSG pMsg, HRESULT& hRet)
{
	if( pMsg->message == WM_KEYDOWN )
	{
		switch( pMsg->wParam )
		{
		case VK_LEFT:
		case VK_RIGHT:
		case VK_UP:
		case VK_DOWN:
		case VK_HOME:
		case VK_END:
		case VK_NEXT:
		case VK_PRIOR:
			hRet = S_FALSE;
			return TRUE;
		}
	}

	//TODO: Add your additional accelerator handling code here

	return FALSE;
}

HRESULT CoNumericEditBox::OnDraw(ATL_DRAWINFO& di)
{
	if ( m_ctlEdit.IsWindow() )
		return S_OK;

	RECT rc = *reinterpret_cast<const RECT*>(di.prcBounds);

	// Draw border
	DrawBorder(di.hdcDraw, &rc, TRUE);

	// Define fore and back colors
	COLORREF crFore;
	COLORREF crBack;
	if ( m_ctlEdit._GetReadOnly() )
	{
		crFore = m_ctlEdit._GetReadOnlyForeColor();
		crBack = m_ctlEdit._GetReadOnlyBackColor();
	}
	else
	{
		crFore = m_ctlEdit._GetForeColor();
		crBack = m_ctlEdit._GetBackColor();
	}

	// Draw background
	HBRUSH hbrBack = CreateSolidBrush(crBack);

	::FillRect(di.hdcDraw, &rc, hbrBack);

	::DeleteObject(hbrBack);

	// Draw text
	HFONT hFont = m_ctlEdit._GetFontHandle();
	HFONT hFontOld = (HFONT)::SelectObject(di.hdcDraw, hFont);

	LONG dxLeft = 0;
	LONG dxRight = 0;
	LONG dyTopBottom = 0;
	CalcMargins(di.hdcDraw, &dxLeft, &dxRight, &dyTopBottom);
	
	LONG cxWidth = rc.right - rc.left;
	if ( cxWidth < dxLeft + dxRight )
	{
		dxLeft = cxWidth / 2;
		dxRight = cxWidth / 2;
	}

	::InflateRect(&rc, 0, -dyTopBottom);

	DWORD fDTOptions = DT_EDITCONTROL|DT_NOPREFIX;

	switch( m_ctlEdit._GetAlignment() )
	{	
	case alLeft:
		fDTOptions |= DT_LEFT;
		break;

	case alCenter:
		fDTOptions |= DT_CENTER;
		break;

	case alGeneral:
	case alRight:
		fDTOptions |= DT_RIGHT;
		break;
	}

	if ( m_ctlEdit._GetRightToLeft() )
	{
		fDTOptions |= DT_RTLREADING;
	}
	
	::SetTextColor(di.hdcDraw, crFore);
	::SetBkColor(di.hdcDraw, crBack);

	DRAWTEXTPARAMS dtp;
	dtp.cbSize = sizeof(DRAWTEXTPARAMS);
	dtp.iLeftMargin = dxLeft;
	dtp.iRightMargin = dxRight;
	dtp.iTabLength = 0;
	dtp.uiLengthDrawn = 0;

	::DrawTextEx(di.hdcDraw, _T("Sample edit box"), 15, &rc, fDTOptions, &dtp);

	// Restore DC font
	::SelectObject(di.hdcDraw, hFontOld);
	m_ctlEdit._FreeFontHandle(hFont);

	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Message handling

LRESULT CoNumericEditBox::OnCtrlCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, 
									   BOOL& bHandled)
{
	RECT rc;
	GetWindowRect(&rc);

	rc.right -= rc.left;
	rc.bottom -= rc.top;
	rc.top = rc.left = 0;

	// Set window styles
	DWORD dwStyle = GetWndStyle(0);
	DWORD dwExStyle = GetWndExStyle(0);

	m_ctlEdit.DefineWindowStyles(&dwStyle, &dwExStyle, 
								 CNumericEditContainedWindow::MES_ALL);

	m_ctlEdit.Create(m_hWnd, rc, NULL, dwStyle, dwExStyle);

	// Update ReadOnly
	m_ctlEdit._OnReadOnlyChanged();

	// Update font
	m_ctlEdit.UpdateFont();

	return 0;
}

LRESULT CoNumericEditBox::OnCtrlSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	LRESULT lRes = CComControl<CoNumericEditBox>::OnSetFocus(uMsg, wParam, lParam, bHandled);

	if ( m_bInPlaceActive )
	{
		if( !IsChild(::GetFocus()) )
			m_ctlEdit.SetFocus();
	}

	return lRes;
}

LRESULT CoNumericEditBox::OnCtrlColorEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	ATLASSERT( m_ctlEdit.m_hWnd == (HWND)lParam );

	HBRUSH hbrBkg = NULL;
	bHandled = m_ctlEdit.UpdateEditDC(reinterpret_cast<HDC>(wParam), &hbrBkg);

	return reinterpret_cast<LRESULT>(hbrBkg);
}

LRESULT CoNumericEditBox::OnCtrlColorStatic(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	ATLASSERT( m_ctlEdit.m_hWnd == (HWND)lParam );

	HBRUSH hbrBkg = NULL;
	bHandled = m_ctlEdit.UpdateStaticDC(reinterpret_cast<HDC>(wParam), &hbrBkg);

	return reinterpret_cast<LRESULT>(hbrBkg);
}

LRESULT CoNumericEditBox::OnCtrlEditChange(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
	if ( m_ctlEdit._GetEditing() )
	{
		bool bModifyWnd = m_ctlEdit.SendMessage(EM_GETMODIFY) != FALSE;
		bool bModifyCtl = m_ctlEdit._GetModify() != FALSE;

		if ( bModifyWnd != bModifyCtl )
		{
			m_ctlEdit._SetModify(bModifyWnd);

			_OnModified();
		}
	}

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Message handling (subclassing window procedure)

LRESULT CoNumericEditBox::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	bHandled = FALSE;

	BOOL bResult = m_ctlEdit.BeginEdit();
	ATLASSERT( bResult );
	if ( !bResult )
	{
		_OnError();
	}

	return 0;
}

LRESULT CoNumericEditBox::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	bHandled = FALSE;

	BOOL bResult = m_ctlEdit.EndEdit();
	ATLASSERT( bResult );
	if ( !bResult )
	{
		_OnError();
	}

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Implementation: ISupportErrorInfo

STDMETHODIMP CoNumericEditBox::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&IID_INumericEditBox,
		&IID_IPersist,
		&IID_IPersistStreamInit,
		&IID_IPersistStorage
	};

	for (int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
	{
		if ( InlineIsEqualGUID(*arr[i], riid) )
			return S_OK;
	}

	return S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Implementation: IOleInPlaceObjectWindowless

STDMETHODIMP CoNumericEditBox::SetObjectRects(LPCRECT prcPos,LPCRECT prcClip)
{
	IOleInPlaceObjectWindowlessImpl<CoNumericEditBox>::SetObjectRects(prcPos, prcClip);

	int cx = prcPos->right - prcPos->left;
	int cy = prcPos->bottom - prcPos->top;

	::SetWindowPos(m_ctlEdit.m_hWnd, NULL, 0, 0, cx, cy, SWP_NOZORDER|SWP_NOACTIVATE);

	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Operations: Persistence

void CoNumericEditBox::_Persist_ReportLastError(HRESULT hr)
{
	SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_IPersist, hr, LPCTSTR(NULL), 0);
}

HRESULT CoNumericEditBox::_Persist_InitNew()
{
	ATLASSERT( !m_ctlEdit.IsWindow() );
	ATLASSERT( m_pcoFontNS && !m_pcoFontNS->GetCookie() );

	// Appearance
	short nAppearance;
	HRESULT hr = GetAmbientAppearance(nAppearance);
	if ( SUCCEEDED(hr) )
	{
		if ( nAppearance )
			m_ctlEdit._SetAppearance(ac3D);
		else
			m_ctlEdit._SetAppearance(acFlat);
	}
	else
	{
		m_ctlEdit._SetAppearance(ac3D);
	}

	// Alignment
	SHORT shAlignment;
	hr = GetAmbientTextAlign(shAlignment);
	if ( FAILED(hr) )
		shAlignment = 0;

	m_ctlEdit._SetAlignment(static_cast<AlignmentConstants>(shAlignment));

	// Font
	CComPtr<IFont> spiAmbientFont;
	hr = GetAmbientFont(&spiAmbientFont); 
	if ( SUCCEEDED(hr) )
	{
		ATLASSERT( spiAmbientFont );

		hr = m_ctlEdit._SetFontCopy(spiAmbientFont);
		ATLASSERT( SUCCEEDED(hr) );
		if ( FAILED(hr) )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							IDS_ERCC_INITFONT);
			return E_FAIL;
		}
	}
	else
	{
		HFONT hFont = reinterpret_cast<HFONT>(::GetStockObject(DEFAULT_GUI_FONT));

		hr = m_ctlEdit._SetFontHandle(hFont);
		ATLASSERT( SUCCEEDED(hr) );
		if ( FAILED(hr) )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							 IDS_ERCC_INITFONT);
			return E_FAIL;
		}
	}

	ATLASSERT( m_ctlEdit._GetRefFont() );

	hr = m_pcoFontNS->Advise(m_ctlEdit._GetRefFont());
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_INITFONT);
		return E_FAIL;
	}

	// We should not use GetAmbientForeColor and GetAmbientBackColor to get 
	// fore and back colors for edit control.
	m_ctlEdit._SetForeOleColor(0x80000000 | COLOR_WINDOWTEXT);
	m_ctlEdit._SetBackOleColor(0x80000000 | COLOR_WINDOW);

	m_ctlEdit._SetReadOnlyForeOleColor(0x80000000 | COLOR_BTNTEXT);
	m_ctlEdit._SetReadOnlyBackOleColor(0x80000000 | COLOR_BTNFACE);

	// RightToLeft
	BOOL bRightToLeft = FALSE;
	BOOL bRightAlignment = FALSE;

	hr = GetAmbientRightToLeft(bRightToLeft);
	if ( SUCCEEDED(hr) )
	{
		bRightAlignment = bRightToLeft;
	}
/*	
	else
	{		
		DWORD fLayout;
		if ( ::GetProcessDefaultLayout(&fLayout) )
		{
			bRightToLeft = fLayout & WS_EX_RTLREADING;
			bRightAlignment = fLayout & WS_EX_RIGHT;
		}
	}
*/
	m_ctlEdit._SetRightToLeft(bRightToLeft);

	// Set default size
	SIZEL size = { 150, 23 };	
	AtlPixelToHiMetric(&size, &m_sizeExtent);

//	GetAmbientLocaleID
//	GetAmbientCodePage
//	GetAmbientCharSet

//	GetAmbientTopToBottom

//	GetAmbientUserMode(

//	DISPID_AMBIENT_SHOWGRABHANDLES
//	GetAmbientSupportsMnemonics
//  GetAmbientPalette

	// Configure formatter by default
	hr = m_ctlEdit.GetFormatter().Configure(vtInt32, ftNumeric);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_CONFIGUREOLEFORMATTER);
		return hr;
	}

	return S_OK;
}


void CoNumericEditBox::_PersistStream_ReportLastError(HRESULT hr)
{
	SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_IPersistStream, hr, 
						   LPCTSTR(NULL), 0);
}

HRESULT CoNumericEditBox::_PersistStream_GetSizeMax(ULARGE_INTEGER* pcbSize)
{
	ATLASSERT( !::IsBadWritePtr(pcbSize, sizeof(ULARGE_INTEGER)) );

	pcbSize->HighPart = 0;

	// Add size of the ATL version we write out.
	pcbSize->LowPart = sizeof(DWORD);

	// Add size of control extent.
	pcbSize->LowPart += sizeof(SIZE);

	// Add size of appearance.
	pcbSize->LowPart += sizeof(AppearanceConstants);

	// Add size of BorderVisible.
	pcbSize->LowPart += sizeof(VARIANT_BOOL);

	// AlignmentConstants
	pcbSize->LowPart += sizeof(AlignmentConstants);

	// Add size of ForeColor, BackColor, ReadOnlyForeColor, ReadOnlyBackColor.
	pcbSize->LowPart += sizeof(OLE_COLOR) * 4;

	// Add size of Font object
	CComVariant cvtFont(m_ctlEdit._GetRefFont());
	pcbSize->LowPart += cvtFont.GetSize();

	// Add size of ReadOnly.
	pcbSize->LowPart += sizeof(VARIANT_BOOL);

	// Add size of RightToLeft.
	pcbSize->LowPart += sizeof(VARIANT_BOOL);
	
	// Add size of Modify
	pcbSize->LowPart += sizeof(VARIANT_BOOL);

	// Add size of Formatter parameters
	CComPtr<IFormatter> spiFormatter;
	HRESULT hr = get_Formatter(&spiFormatter);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountComError(SP::AE_SETLOG|SP::AE_SETMSG, 1, hr, 
							static_cast<INumericEditBox*>(this), IID_INumericEditBox);

		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 2, 
						 IDS_EROW_GETFORMATTER);
		return hr;
	}

	ATLASSERT( m_pcoFormatter );

	ULARGE_INTEGER cbSize;
	hr = m_pcoFormatter->_PersistStream_GetSizeMax(&cbSize);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 2, 
						 IDS_ERCC_GETSIZEFORMATTER);
		return hr;
	}

	pcbSize->LowPart += cbSize.LowPart;

	return S_OK;
}

HRESULT CoNumericEditBox::_PersistStream_Load(LPSTREAM piStream)
{
	ATLASSERT( piStream );

	VARIANT_BOOL vbValue;
	OLE_COLOR oleClr;

	// Check version
	DWORD dwVer;
	HRESULT hr = piStream->Read(&dwVer, sizeof(DWORD), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADATLVERSION);
		return hr;
	}

	if ( dwVer > _ATL_VER )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_INCORRECTATLVERSION);
		return E_FAIL;
	}

	// Read size of control extents.
	hr = piStream->Read(&m_sizeExtent, sizeof(SIZE), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADCTRLEXTENT);
		return hr;
	}

	// Read Appearance.
	AppearanceConstants enAppearance;
	hr = piStream->Read(&enAppearance, sizeof(AppearanceConstants), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADAPPEARANCE);
		return hr;
	}

	m_ctlEdit._SetAppearance(enAppearance);

	// Read BorderVisible.	
	hr = piStream->Read(&vbValue, sizeof(VARIANT_BOOL), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADBORDERVISIBLE);
		return hr;
	}

	m_ctlEdit._SetBorderVisible(vbValue == VARIANT_TRUE);

	// Read AlignmentConstants
	AlignmentConstants enAlignment;
	hr = piStream->Read(&enAlignment, sizeof(AlignmentConstants), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADALIGNMENT);
		return hr;
	}

	m_ctlEdit._SetAlignment(enAlignment);

	// Read ForeColor	
	hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADFORECOLOR);
		return hr;
	}

	m_ctlEdit._SetForeOleColor(oleClr); 

	// Read BackColor	
	hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{		
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADBACKCOLOR);
		return hr;
	}

	m_ctlEdit._SetBackOleColor(oleClr); 

	// Read ReadOnlyForeColor
	hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADREADONLYFORECOLOR);
		return hr;
	}

	m_ctlEdit._SetReadOnlyForeColor(oleClr); 

	// Read ReadOnlyBackColor
	hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADREADONLYBACKCOLOR);
		return hr;
	}

	m_ctlEdit._SetReadOnlyBackColor(oleClr); 

	// Read Font object
	CComVariant cvtFont;
	hr = cvtFont.ReadFromStream(piStream);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADFONT);
		return hr;
	}

	hr = cvtFont.ChangeType(VT_UNKNOWN);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADFONT);
		return hr;
	}

	CComPtr<IFont> spiFont;
	if ( cvtFont.punkVal )
	{
		hr = cvtFont.punkVal->QueryInterface(IID_IFont, (void**)(&spiFont));
		ATLASSERT( SUCCEEDED(hr) );
		if ( FAILED(hr) )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							 IDS_ERCC_LOADFONT);

			return hr;
		}
	}

	m_ctlEdit._SetFont(spiFont);

	// Read ReadOnly.
	hr = piStream->Read(&vbValue, sizeof(VARIANT_BOOL), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADREADONLY);
		return hr;
	}

	m_ctlEdit._SetReadOnly(vbValue == VARIANT_TRUE);

	// Read RightToLeft
	hr = piStream->Read(&vbValue, sizeof(VARIANT_BOOL), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADRIGHTTOLEFT);
		return hr;
	}

	m_ctlEdit._SetRightToLeft(vbValue == VARIANT_TRUE);

	// ErrorSymbol
	CComBSTR cbsErrorSymbol;
	hr = cbsErrorSymbol.ReadFromStream(piStream);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADERRORSYMBOL);
		return hr;
	}

	hr = m_ctlEdit._SetErrorOleSymbol(cbsErrorSymbol);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_EROW_SETERRORSYMBOL);
		return hr;
	}

	// Read Formatter parameters
	CComPtr<IFormatter> spiFormatter;
	hr = get_Formatter(&spiFormatter);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountComError(SP::AE_SETLOG|SP::AE_SETMSG, 1, hr, 
							static_cast<INumericEditBox*>(this), IID_INumericEditBox);

		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_EROW_GETFORMATTER);
		return hr;
	}

	ATLASSERT( m_pcoFormatter );

	hr = m_pcoFormatter->_PersistStream_Load(piStream);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_LOADFORMATTER);

		return hr;
	}

	m_bRequiresSave = FALSE;

	return S_OK;
}

HRESULT CoNumericEditBox::_PersistStream_Save(LPSTREAM piStream, BOOL fClearDirty)
{
	ATLASSERT( piStream );

	VARIANT_BOOL vbValue;
	OLE_COLOR oleClr;

	// Write ATL version.
	const DWORD dw = _ATL_VER;
	HRESULT hr = piStream->Write(&dw, sizeof(DWORD), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEATLVERSION);
		return hr;
	}

	// Write control extent.
	hr = piStream->Write(&m_sizeExtent, sizeof(SIZE), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVECTRLEXTENT);
		return hr;
	}

	// Write Appearance.
	AppearanceConstants enAppearance = m_ctlEdit._GetAppearance();
	hr = piStream->Write(&enAppearance, sizeof(AppearanceConstants), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEAPPEARANCE);
		return hr;
	}

	// Write BorderVisible.	
	vbValue = m_ctlEdit._GetBorderVisible() ? VARIANT_TRUE : VARIANT_FALSE;
	hr = piStream->Write(&vbValue, sizeof(VARIANT_BOOL), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEBORDERVISIBLE);
		return hr;
	}

	// Write AlignmentConstants
	AlignmentConstants enAlignment = m_ctlEdit._GetAlignment();
	hr = piStream->Write(&enAlignment, sizeof(AlignmentConstants), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEALIGNMENT);
		return hr;
	}

	// Write ForeColor	
	oleClr = m_ctlEdit._GetForeColor();
	hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEFORECOLOR);
		return hr;
	}

	// Write BackColor	
	oleClr = m_ctlEdit._GetBackColor();
	hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEBACKCOLOR);
		return hr;
	}

	// Write ReadOnlyForeColor
	oleClr = m_ctlEdit._GetReadOnlyForeColor();
	hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEREADONLYFORECOLOR);
		return hr;
	}

	// Write ReadOnlyBackColor
	oleClr = m_ctlEdit._GetReadOnlyBackColor();
	hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEREADONLYBACKCOLOR);
		return hr;
	}

	// Write Font object
	CComVariant cvtFont = m_ctlEdit._GetRefFont();
	hr = cvtFont.WriteToStream(piStream);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEFONT);
		return hr;
	}

	// Write ReadOnly.
	vbValue = m_ctlEdit._GetReadOnly() ? VARIANT_TRUE : VARIANT_FALSE;
	hr = piStream->Write(&vbValue, sizeof(VARIANT_BOOL), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEREADONLY);
		return hr;
	}

	// Write RightToLeft.
	vbValue = m_ctlEdit._GetRightToLeft() ? VARIANT_TRUE : VARIANT_FALSE;
	hr = piStream->Write(&vbValue, sizeof(VARIANT_BOOL), NULL);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVERIGHTTOLEFT);
		return hr;
	}

	// ErrorSymbol
	CComBSTR cbsErrorSymbol;
	hr = m_ctlEdit._GetErrorOleSymbol(&cbsErrorSymbol);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_EROW_GETERRORSYMBOL);
		return hr;
	}

	hr = cbsErrorSymbol.WriteToStream(piStream);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEERRORSYMBOL);
		return hr;
	}


	// Write Formatter parameters
	CComPtr<IFormatter> spiFormatter;
	hr = get_Formatter(&spiFormatter);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountComError(SP::AE_SETLOG|SP::AE_SETMSG, 1, hr, 
							static_cast<INumericEditBox*>(this), IID_INumericEditBox);

		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_EROW_GETFORMATTER);
		return hr;
	}

	ATLASSERT( m_pcoFormatter );

	hr = m_pcoFormatter->_PersistStream_Save(piStream, fClearDirty);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 0, 
						 IDS_ERCC_SAVEFORMATTER);
		return hr;
	}

//	if ( fClearDirty )
//		m_bRequiresSave = FALSE;

	return S_OK;
}


void CoNumericEditBox::_PersistStorage_ReportLastError(HRESULT hr)
{
	SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_IPersistStorage, hr, 
						   LPCTSTR(NULL), 0);
}

HRESULT CoNumericEditBox::_PersistStorage_InitNew(IStorage* piStorage)
{
	return _Persist_InitNew();
}

HRESULT CoNumericEditBox::_PersistStorage_Load(IStorage* piStorage)
{
	LPCOLESTR lpcszContents = OLESTR("Contents");
	const DWORD fOptions = STGM_DIRECT|STGM_SHARE_EXCLUSIVE;

	CComPtr<IStream> spiStream;
	HRESULT hr = piStorage->OpenStream(lpcszContents, NULL, fOptions, 0, &spiStream);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::AccountComError(SP::AE_SETLOG|SP::AE_SETMSG, 1, hr, piStorage, IID_IStorage);

		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 2, 
						 IDS_ERCC_OPENTREAMINSTORAGE);

		return hr;
	}

	return _PersistStream_Load(spiStream);
}

HRESULT CoNumericEditBox::_PersistStorage_Save(IStorage* piStorage, BOOL fSameAsLoad)
{
	LPCOLESTR lpcszContents = OLESTR("Contents");
	const DWORD fOptions = STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE;

	CComPtr<IStream> spiStream;
	HRESULT hr = piStorage->CreateStream(lpcszContents, fOptions, 0, 0, &spiStream);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::AccountComError(SP::AE_SETLOG|SP::AE_SETMSG, 1, hr, piStorage, IID_IStorage);

		SP::AccountError(SP::AE_LOG|SP::AE_ADDMSG, IDS_ELS_SPNUMERICEDITBOX, 2, 
						 IDS_ERCC_CREATESTREAMINSTORAGE);
		return hr;
	}

	return _PersistStream_Save(spiStream, fSameAsLoad);
}

HRESULT CoNumericEditBox::_PersistStorage_SaveCompleted(IStorage* piStorage)
{
	return S_OK;
}

HRESULT CoNumericEditBox::_PersistStorage_HandsOffStorage()
{
	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////////////////
// Implementation: INumericEditBox

// Appearance
STDMETHODIMP CoNumericEditBox::get_Appearance(AppearanceConstants* penAppearance)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_Appearance\n") );

	ATLASSERT( !::IsBadWritePtr(penAppearance, sizeof(AppearanceConstants)) );
	if ( !penAppearance )
		return E_POINTER;

	*penAppearance = m_ctlEdit._GetAppearance();

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_Appearance(AppearanceConstants enAppearance)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_Appearance\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_APPEARANCE) == S_FALSE )
		return S_FALSE;

	m_ctlEdit._SetAppearance(enAppearance);

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_APPEARANCE);

	m_ctlEdit._OnAppearanceChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK; 
}

// BorderVisible
STDMETHODIMP CoNumericEditBox::get_BorderVisible(VARIANT_BOOL* pvbValue)
{ 
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_TabStop\n") );

	ATLASSERT( !::IsBadWritePtr(pvbValue, sizeof(VARIANT_BOOL)) );
	if ( !pvbValue )
		return E_POINTER;

	*pvbValue = m_ctlEdit._GetBorderVisible() ? ATL_VARIANT_TRUE : ATL_VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_BorderVisible(VARIANT_BOOL vbValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_TabStop\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_TABSTOP) == S_FALSE )
		return S_FALSE;

	m_ctlEdit._SetBorderVisible(vbValue);

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_TABSTOP);

	m_ctlEdit._OnBorderVisibleChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK; 
}

// Alignment
STDMETHODIMP CoNumericEditBox::get_Alignment(AlignmentConstants* penValue)
{ 
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_Alignment\n") );

	ATLASSERT( !::IsBadWritePtr(penValue, sizeof(AlignmentConstants)) );
	if ( !penValue )
		return E_POINTER;

	*penValue = m_ctlEdit._GetAlignment();

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_Alignment(AlignmentConstants enValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_Alignment\n") );

	BOOL bUserMode;
	HRESULT hr = GetAmbientUserMode(bUserMode);
	if ( SUCCEEDED(hr) && bUserMode && IsWindow() )
	{
		SP::ReportErrorWithCom(SP::AE_LOG|SP::AE_SETMSG, GetObjectCLSID(), 
							   IID_INumericEditBox, E_UNEXPECTED, 
							   IDS_ELS_SPCONVERSION, 0, 
							   IDS_ERCC_CANNOTCHANGEALIGNMENT);

		return E_UNEXPECTED;
	}

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_ALIGNMENT) == S_FALSE )
		return S_FALSE;

	m_ctlEdit._SetAlignment(enValue);

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_ALIGNMENT);

	m_ctlEdit._OnAlignmentChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK; 
}

// ForeColor
STDMETHODIMP CoNumericEditBox::get_ForeColor(OLE_COLOR* poleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_ForeColor\n") );

	if ( !poleColor )   
		return E_POINTER;

	*poleColor = m_ctlEdit._GetForeOleColor();

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_ForeColor(OLE_COLOR oleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_ForeColor\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_FORECOLOR) == S_FALSE ) 
		return S_FALSE;

	m_ctlEdit._SetForeOleColor(oleColor); 

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_FORECOLOR);

	m_ctlEdit._OnForeColorChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK;
}

// BackColor
STDMETHODIMP CoNumericEditBox::get_BackColor(OLE_COLOR* poleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_BackColor\n") );

	if ( !poleColor )
		return E_POINTER;

	*poleColor = m_ctlEdit._GetBackOleColor();

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_BackColor(OLE_COLOR oleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_BackColor\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_BACKCOLOR) == S_FALSE )   
		return S_FALSE;

	m_ctlEdit._SetBackOleColor(oleColor); 

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_BACKCOLOR);

	m_ctlEdit._OnBackColorChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK;
}

// ReadOnlyForeColor
STDMETHODIMP CoNumericEditBox::get_ReadOnlyForeColor(OLE_COLOR* poleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_ReadOnlyForeColor\n") );

	if ( !poleColor )   
		return E_POINTER;

	*poleColor = m_ctlEdit._GetReadOnlyForeOleColor();

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_ReadOnlyForeColor(OLE_COLOR oleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_ReadOnlyForeColor\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_READONLYFORECOLOR) == S_FALSE ) 
		return S_FALSE;

	m_ctlEdit._SetReadOnlyForeOleColor(oleColor); 

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_READONLYFORECOLOR);

	m_ctlEdit._OnReadOnlyForeColorChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK;
}

// ReadOnlyBackColor
STDMETHODIMP CoNumericEditBox::get_ReadOnlyBackColor(OLE_COLOR* poleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_ReadOnlyBackColor\n") );

	if ( !poleColor )
		return E_POINTER;

	*poleColor = m_ctlEdit._GetReadOnlyBackOleColor();

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_ReadOnlyBackColor(OLE_COLOR oleColor)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_ReadOnlyBackColor\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_READONLYBACKCOLOR) == S_FALSE )   
		return S_FALSE;

	m_ctlEdit._SetReadOnlyBackOleColor(oleColor); 

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_READONLYBACKCOLOR);

	m_ctlEdit._OnReadOnlyBackColorChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK;
}

// Font
STDMETHODIMP CoNumericEditBox::get_Font(IFontDisp** ppiFont)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_Font\n") );

	return m_ctlEdit._GetFontDisp(ppiFont);
}

STDMETHODIMP CoNumericEditBox::put_Font(IFontDisp* piFont)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_Font\n") );
	ATLASSERT( m_pcoFontNS );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_FONT) == S_FALSE )
		return S_FALSE;

	m_ctlEdit._OnFontChanging();

	// Disconnect property notify sink
	if ( m_pcoFontNS->GetCookie() )  
		m_pcoFontNS->Unadvise(m_ctlEdit._GetRefFont());

	HRESULT hr = m_ctlEdit._SetCopyFontDisp(piFont);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	m_bRequiresSave = TRUE;

	// Connect property notify sink
	if ( m_ctlEdit._GetRefFont() )
	{
		hr = m_pcoFontNS->Advise(m_ctlEdit._GetRefFont());
		ATLASSERT( SUCCEEDED(hr) );
		if FAILED(hr)
		{
			SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
								   LPCTSTR(NULL), 0);

			return E_FAIL;
		}
	}

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_FONT);

	m_ctlEdit._OnFontChanged();

	FireViewChange();
	SendOnDataChange(NULL);
 
	return S_OK;
}

STDMETHODIMP CoNumericEditBox::putref_Font(IFontDisp* piFont)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::piFont\n") );
	ATLASSERT( m_pcoFontNS );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_FONT) == S_FALSE )
		return S_FALSE;

	m_ctlEdit._OnFontChanging();

	// Disconnect property notify sink
	if ( m_pcoFontNS->GetCookie() )  
		m_pcoFontNS->Unadvise(m_ctlEdit._GetRefFont());

	HRESULT hr = m_ctlEdit._SetFontDisp(piFont);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}
	
	m_bRequiresSave = TRUE;

	// Connect property notify sink
	if ( m_ctlEdit._GetRefFont() )
	{
		hr = m_pcoFontNS->Advise(m_ctlEdit._GetRefFont());
		ATLASSERT( SUCCEEDED(hr) );
		if FAILED(hr)
		{
			SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
								   LPCTSTR(NULL), 0);

			return E_FAIL;
		}
	}

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_FONT);

	m_ctlEdit._OnFontChanged();

	FireViewChange();
	SendOnDataChange(NULL);

 	return S_OK;
}

// ReadOnly
STDMETHODIMP CoNumericEditBox::get_ReadOnly(VARIANT_BOOL* pvbValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_ReadOnly\n") );

	ATLASSERT( !::IsBadWritePtr(pvbValue, sizeof(VARIANT_BOOL)) );
	if ( !pvbValue )
		return E_POINTER;

	*pvbValue = m_ctlEdit._GetReadOnly() ? ATL_VARIANT_TRUE : ATL_VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_ReadOnly(VARIANT_BOOL vbValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_ReadOnly\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_READONLY) == S_FALSE )
		return S_FALSE;

	m_ctlEdit._SetReadOnly(vbValue);

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_READONLY);

	m_ctlEdit._OnReadOnlyChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK; 
}

// RightToLeft
STDMETHODIMP CoNumericEditBox::get_RightToLeft(VARIANT_BOOL* pvbValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_RightToLeft\n") );

	ATLASSERT( !::IsBadWritePtr(pvbValue, sizeof(VARIANT_BOOL)) );
	if ( !pvbValue )
		return E_POINTER;

	*pvbValue = m_ctlEdit._GetRightToLeft() ? ATL_VARIANT_TRUE : ATL_VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_RightToLeft(VARIANT_BOOL vbValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_ReadOnly\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_RIGHTTOLEFT) == S_FALSE )
		return S_FALSE;

	m_ctlEdit._SetRightToLeft(vbValue);

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_RIGHTTOLEFT);

	m_ctlEdit._OnRightToLeftChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK; 
}

// ErrorSymbol
STDMETHODIMP CoNumericEditBox::get_ErrorSymbol(BSTR* pbsErrorSymbol)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_ErrorSymbol\n") );

	ATLASSERT( !::IsBadWritePtr(pbsErrorSymbol, sizeof(BSTR)) );
	if ( !pbsErrorSymbol )
		return E_POINTER;

	HRESULT hr = m_ctlEdit._GetErrorOleSymbol(pbsErrorSymbol);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_ErrorSymbol(BSTR bsErrorSymbol)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_ErrorSymbol\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_ERRORSYMBOL) == S_FALSE )
		return S_FALSE;

	HRESULT hr = m_ctlEdit._SetErrorOleSymbol(bsErrorSymbol);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_ERRORSYMBOL);

	m_ctlEdit._OnErrorSymbolChanged();

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK; 
}

// Formatter Params
STDMETHODIMP CoNumericEditBox::get_FormatterParams(BSTR* pbsParams)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_FormatterParams\n") );

	ATLASSERT( !::IsBadWritePtr(pbsParams, sizeof(BSTR)) );
	if ( !pbsParams )
		return E_POINTER;

	ValueTypeConstants enValueType = m_ctlEdit.GetFormatter().GetValueType();
	FormatTypeConstants enFormatType = m_ctlEdit.GetFormatter().GetFormatType();

	CComBSTR cbsDisplayMask;
	HRESULT hr = m_ctlEdit.GetFormatter().GetOleMask(FM_DISPLAY, &cbsDisplayMask);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	CComBSTR cbsEditingMask;
	hr = m_ctlEdit.GetFormatter().GetOleMask(FM_EDITING, &cbsEditingMask);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	hr = BuildFormatterParamString(enValueType, enFormatType, cbsDisplayMask, cbsEditingMask,
								   pbsParams);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_FormatterParams(BSTR bsParams)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_FormatterParams\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_FORMATTERPARAMS) == S_FALSE )
		return S_FALSE;

	ValueTypeConstants enValueType;
	FormatTypeConstants enFormatType;
	CComBSTR cbsDisplayMask;
	CComBSTR cbsEditingMask;

	HRESULT hr = ParseFormatterParamString(bsParams, &enValueType, &enFormatType,
										   &cbsDisplayMask, &cbsEditingMask);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	ValueTypeConstants enOldValueType = m_ctlEdit.GetFormatter().GetValueType();

	// Set new formatter properties
	hr = m_ctlEdit.GetFormatter().Configure(enValueType, enFormatType, 
											cbsDisplayMask, cbsEditingMask);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return hr;
	}

	m_ctlEdit._OnFormatterChanged(enOldValueType != enValueType);

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_FORMATTERPARAMS);

	FireViewChange();
	SendOnDataChange(NULL);

	return S_OK;
}

// Formatter
STDMETHODIMP CoNumericEditBox::get_Formatter(IFormatter** ppiFormatter)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_Formatter\n") );

	if ( !ppiFormatter )
		return E_POINTER;
	
	HRESULT hr = GetFormatter(ppiFormatter);
	ATLASSERT( SUCCEEDED(hr) );
	if ( FAILED(hr) )
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return E_FAIL;
	}

	return S_OK;
}

// HWND
STDMETHODIMP CoNumericEditBox::get_HWND(LONG_PTR* phWnd)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_HWND\n") );

	ATLASSERT( !::IsBadWritePtr(phWnd, sizeof(LONG_PTR)) );
	if ( !phWnd )
		return E_POINTER;

	*phWnd = m_ctlEdit.IsWindow() ? reinterpret_cast<LONG_PTR>(m_ctlEdit.m_hWnd) : NULL;

 	return S_OK;
}

// Modify
STDMETHODIMP CoNumericEditBox::get_Modify(VARIANT_BOOL* pvbModify)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_Modify\n") );

	ATLASSERT( !::IsBadWritePtr(pvbModify, sizeof(VARIANT_BOOL)) );
	if ( !pvbModify )
		return E_POINTER;

	*pvbModify = m_ctlEdit._GetModify() ? ATL_VARIANT_TRUE : ATL_VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_Modify(VARIANT_BOOL vbModify)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_Modify\n") );

	m_ctlEdit._SetModify(vbModify);

	m_ctlEdit._OnModifyChanged();

	return S_OK; 
}

// Value
STDMETHODIMP CoNumericEditBox::get_Value(VARIANT* pvValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_Value\n") );

	if ( !pvValue )
		return E_POINTER;

	HRESULT hr = m_ctlEdit.GetOleValue(pvValue);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return E_FAIL;
	}

 	return S_OK;
}

STDMETHODIMP CoNumericEditBox::put_Value(VARIANT vValue)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::put_Value\n") );

	if ( m_nFreezeEvents == 0 && FireOnRequestEdit(DISPID_VALUE) == S_FALSE )
		return S_FALSE;

	while ( vValue.vt == (VT_BYREF|VT_VARIANT) )
	{
		CComVariant cvtTemp = *vValue.pvarVal;

		vValue = cvtTemp;
	}

	HRESULT hr = m_ctlEdit.SetOleValue(vValue);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return E_FAIL;
	}

	m_bRequiresSave = TRUE;

	if ( m_nFreezeEvents == 0 )
		FireOnChanged(DISPID_VALUE);

	m_ctlEdit._OnValueChanged();

	FireViewChange();
	SendOnDataChange(NULL);

 	return S_OK;
}

// Display Text
STDMETHODIMP CoNumericEditBox::get_DisplayText(BSTR* pbsText)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_DisplayText\n") );

	if ( !pbsText )
		return E_POINTER;

	HRESULT hr = m_ctlEdit.GetDisplayOleText(pbsText);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return E_FAIL;
	}

 	return S_OK;
}

// Edit Text
STDMETHODIMP CoNumericEditBox::get_EditingText(BSTR* pbsText)
{
	ATLTRACE( ATL::atlTraceControls, 2, _T("CoNumericEditBox::get_EditingText\n") );

	if ( !pbsText )
		return E_POINTER;

	HRESULT hr = m_ctlEdit.GetEditingOleText(pbsText);
	ATLASSERT( SUCCEEDED(hr) );
	if FAILED(hr)
	{
		SP::ReportErrorWithCom(0, GetObjectCLSID(), IID_INumericEditBox, hr, 
							   LPCTSTR(NULL), 0);

		return E_FAIL;
	}

 	return S_OK;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions