Click here to Skip to main content
15,892,480 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 74.9K   3.6K   36  
Masked numeric edit ActiveX control.
/////////////////////////////////////////////////////////////////////////////////////////
//	Project:		SP Numeric Edit Control 1.2
//
//	File:			NumericEditBox.cpp
//
//	Developer(s):	Sergei Pavlovsky
//					sergei_vp@hotmail.com
//					Copyright (c) 2004-2005
//
//	Description:	Implementation of CoNumericEditBox class.
//					CoNumericEditBox represents ActiveX control.
//
//	Platforms:		Win32, ATL
//
//	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>
#include <sstream>

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

/////////////////////////////////////////////////////////////////////////////////////////
// Operations

void CoNumericEditBox::CFontPropertyNotifySink::Advise(IFont* piFont) // throw(ELogError)
{
	ATLASSERT( piFont && !m_dwCookie );

	try
	{
		HRESULT hr = AtlAdvise(piFont, static_cast<IPropertyNotifySink*>(this), 
							IID_IPropertyNotifySink, &m_dwCookie);
		ATLASSERT( SUCCEEDED(hr) );
		if ( FAILED(hr) )
			throw EComLogError(hr);
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_CONNECTFONTEVENTSINK, &e);
	}
}

void CoNumericEditBox::CFontPropertyNotifySink::Unadvise(IFont* piFont) throw()
{
	ATLASSERT( piFont && m_dwCookie );

#ifdef _DEBUG
	HRESULT hr = AtlUnadvise(piFont, IID_IPropertyNotifySink, m_dwCookie);
	ATLASSERT( SUCCEEDED(hr) );
#else // _DEBUG
	AtlUnadvise(piFont, IID_IPropertyNotifySink, m_dwCookie);
#endif // _DEBUG

	m_dwCookie = 0;
}

/////////////////////////////////////////////////////////////////////////////////////////
// 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() throw()
	: m_ctlEdit(_T("Edit"), this, 1),
	  m_pcoFontNS(NULL),
	  m_pcoFormatter(NULL)
{
	m_bWindowOnly = TRUE;
}

#pragma warning(pop)

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

HRESULT CoNumericEditBox::FinalConstruct() throw()
{
	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()throw()
{
	ATLASSERT( m_pcoFontNS );

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

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

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

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

CoNumericEditBox::ParamsEnum CoNumericEditBox::ReadParamID(LPCOLESTR lpParams, 
														   SIZE_T cchParams, 
														   UINT_PTR unPos, 
														   UINT_PTR* punNext) 
														   // throw(ELogError);
{
	typedef std::basic_string<OLECHAR> olestring;

	ATLASSERT( !::IsBadReadPtr(lpParams, cchParams * sizeof(OLECHAR)) );
	ATLASSERT( unPos < cchParams);
	ATLASSERT( !::IsBadReadPtr(punNext, sizeof(UINT_PTR)) );

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

			for ( UINT_PTR i = unPos; i < cchParams; i++ )
			{
				const 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 )
					return static_cast<ParamsEnum>(i + 1);
			}

/*
			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);
*/
			throw EAppLogError(ENEC_UNRECOGNIZEDPARAM);
		}
		catch( std::bad_alloc& e )
		{
			throw EStlLogError(e);
		}
	}
	catch( ELogError& e )
	{	
		throw EAppLogError(ENEC_READPARAMNAME, &e);
	}
}

void CoNumericEditBox::WriteParamID(std::wostringstream& os, ParamsEnum enID)
									// throw(ELogError)
{
	ATLASSERT( enID <= sizeof(ms_rgszParamNames)/sizeof(LPCOLESTR) );

	typedef std::basic_string<OLECHAR> olestring;

	try
	{
		try
		{
			os << olestring(ms_rgszParamNames[enID - 1]);
		}
		catch( std::bad_alloc& e )
		{
			throw EStlLogError(e);
		}
	}
	catch( ELogError& e )
	{	
		throw EAppLogError(ENEC_WRITEPARAMNAME, &e);
	}
}

void CoNumericEditBox::ReadParamValue(LPCOLESTR lpParams, SIZE_T cchParams, 
									  UINT_PTR unPos, UINT_PTR * punNext, 
									  BSTR* pbsValue) // throw(ELogError)
{
	ATLASSERT( !::IsBadReadPtr(lpParams, cchParams * sizeof(OLECHAR)) );
	ATLASSERT( unPos < cchParams);
	ATLASSERT( !::IsBadReadPtr(punNext, sizeof(UINT_PTR)) );
	ATLASSERT( !::IsBadReadPtr(pbsValue, sizeof(BSTR)) );

	typedef std::basic_string<OLECHAR> olestring;

	try
	{
		try
		{
			std::wostringstream os;

			*punNext = unPos;

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

					if ( chCurrent == L'"' )
					{
						const UINT_PTR iNext = i + 1;

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

					os << chCurrent;
				}

				if ( lpParams[i] != L'"' || i == unPos )
					throw EAppLogError(ENEC_NOCLOSINGQUOTE);

				*punNext = i + 1;
			}
			else
			{
				// Value is not surrounded with double quotes.
				for ( UINT_PTR i = unPos; i < cchParams; i++ )
				{
					const OLECHAR chCurrent = lpParams[i];

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

					if ( chCurrent == L'"' )
						throw EAppLogError(ENEC_PARAMVALUEQUOTES);

					os << chCurrent;
				}

				*punNext = i;
			}

			olestring sValue = os.str();

			if ( !sValue.empty() )
			{
				*pbsValue = ::SysAllocStringLen(sValue.c_str(), UINT(sValue.length()));
				ATLASSERT( *pbsValue );
				if ( !(*pbsValue) )
					throw EComLogError(E_OUTOFMEMORY);
			}
			else
			{
				*pbsValue = NULL;
			}
		}
		catch( std::bad_alloc& e )
		{
			throw EStlLogError(e);
		}
		catch( CAtlException& e )
		{
			throw EAtlLogError(e);
		}
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_READPARAMVALUE, &e);
	}
}

void CoNumericEditBox::WriteParamValue(std::wostringstream& os, 
									   LPCOLESTR lpValue, SIZE_T cchValue, 
									   bool bInsideQuotes) // throw(ELogError)
{
	ATLASSERT( !::IsBadReadPtr(lpValue, cchValue * sizeof(WCHAR)) );

	typedef std::basic_string<OLECHAR> olestring;

	try
	{
		try
		{
			if ( bInsideQuotes )
			{
				os << L'\"';

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

					if ( chCurrent == L'\"' )
						os << L'\"';
						
					os << chCurrent;
				}

				os <<  L'\"';
			}
			else
			{
				os << olestring(lpValue, lpValue + cchValue);
			}
		}
		catch( std::bad_alloc& e )
		{
			throw EStlLogError(e);
		}
	}
	catch( ELogError& e )
	{	
		throw EAppLogError(ENEC_WRITEPARAMVALUE, &e);
	}
}

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

void CoNumericEditBox::ParseFormatterParamString(BSTR bsParams, 
												 ValueTypeConstants* penValueType,
												 FormatTypeConstants* penFormatType,
												 BSTR* pbsDisplayMask,
												 BSTR* pbsEditingMask)
												 // throw(ELogError)
{
	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;

	try
	{
		try
		{
			*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 )
							throw EAppLogError(ENEC_UNEXPECTEDASSIGNMENTSIGN);

						i++;
						enParseStatus = PS_EXPECTPARAMVALUE;
						break;

					case _T(';'):
						if ( enParseStatus != PS_EXPECTPARAMEND &&
							enParseStatus != PS_EXPECTPARAMVALUE )
							throw EAppLogError(ENEC_UNEXPECTEDSEMICOLONSIGN);

						pairInsRes = mapParams.insert(
							CParamValuesMap::value_type(enCurrentParam,
														cbsCurrentValue));
						if ( !pairInsRes.second )
							throw EAppLogError(ENEC_DUPLICATEDPARAM);

						enCurrentParam = TN_NON;
						cbsCurrentValue = BSTR(NULL);

						i++;
						enParseStatus = PS_EXPECTPARAMNAME;
						break;

					default:
						if ( enParseStatus == PS_EXPECTPARAMNAME )
						{
							// Define Param
							UINT iNext;
							enCurrentParam = ReadParamID(bsParams, cchParams, 
														 i, &iNext);

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

							UINT iNext;
							ReadParamValue(bsParams, cchParams, i, &iNext, 
										   &cbsCurrentValue);

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

				// Add last parameter
				if ( enCurrentParam != TN_NON )
				{
					pairInsRes = mapParams.insert(
						CParamValuesMap::value_type(enCurrentParam,
													cbsCurrentValue));
					if ( !pairInsRes.second )
						throw EAppLogError(ENEC_DUPLICATEDPARAM);
				}

				// Convert parameter values
				CComPtr<ITypeLib> spiTLB;
				CSpNumericEditModule::LoadTypeLib(&spiTLB);

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

					switch ( (*it).first )
					{
					case TN_VALUETYPE:
						{
							CComPtr<ITypeInfo> spiTI;
							CSpNumericEditModule::GetValueTypesTypeInfo(spiTLB, &spiTI);
							
							const LONG lValue = 
								CSpNumericEditModule::ConvertEnumValueName(
									spiTI, cbsValue);

							*penValueType = ValueTypeConstants(lValue);
						}
						break;

					case TN_FORMATTYPE:
						{
							CComPtr<ITypeInfo> spiTI;
							CSpNumericEditModule::GetFormatTypesTypeInfo(spiTLB, &spiTI);
							
							const LONG lValue = 
								CSpNumericEditModule::ConvertEnumValueName(
									spiTI, cbsValue);

							*penFormatType = FormatTypeConstants(lValue);
						}
						break;

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

					case TN_EDITINGMASK:
						*pbsEditingMask = cbsValue.Detach();
						break;
					}
				}
			}
		}
		catch( CAtlException& e )
		{
			throw EAtlLogError(e);
		}
		catch( std::bad_alloc& e )
		{
			throw EStlLogError(e);
		}
	}
	catch( EAppLogError& e )
	{
		throw EAppLogError(ENEC_PARSEFMTPARAMSSTRING, &e);
	}
}

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

	typedef std::basic_string<OLECHAR> olestring;

	try
	{
		try
		{
			*pbsParams = NULL;

			CComPtr<ITypeLib> spiTLB;
			CSpNumericEditModule::LoadTypeLib(&spiTLB);

			std::wostringstream oss;

			bool bEmpty = true;

			// Add ValueType parameter
			if ( enValueType )
			{
				CComPtr<ITypeInfo> spiTI;
				CSpNumericEditModule::GetValueTypesTypeInfo(spiTLB, &spiTI);
				
				CComBSTR cbsValue;
				CSpNumericEditModule::ConvertEnumValueName(spiTI, enValueType, &cbsValue);

				WriteParamID(oss, TN_VALUETYPE);

				oss << L'=';

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

				oss << L';';

				bEmpty = false;
			}

			// Add FormatType parameter
			if ( enFormatType )
			{
				if ( !bEmpty )
					oss << L' ';

				CComPtr<ITypeInfo> spiTI;
				CSpNumericEditModule::GetFormatTypesTypeInfo(spiTLB, &spiTI);
				
				CComBSTR cbsValue;
				CSpNumericEditModule::ConvertEnumValueName(spiTI, enFormatType, &cbsValue);

				WriteParamID(oss, TN_FORMATTYPE);

				oss << L'=';

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

				oss << L';';

				bEmpty = false;
			}

			// Add DisplayMask parameter
			if ( lpszDisplayMask )
			{
				if ( !bEmpty )
					oss << L' ';

				WriteParamID(oss, TN_DISPLAYMASK);

				oss << L'=';

				WriteParamValue(oss, lpszDisplayMask, wcslen(lpszDisplayMask), true);

				oss << L';';

				bEmpty = false;
			}

			// Add EditingMask parameter
			if ( lpszEditingMask )
			{
				if ( !bEmpty )
					oss << L' ';

				WriteParamID(oss, TN_EDITINGMASK);

				oss << L'=';

				WriteParamValue(oss, lpszEditingMask, wcslen(lpszEditingMask), true);

				oss << L';';
			}

			// Allocate system string
			if ( !bEmpty )
			{
				olestring strBuffer = oss.str();

				*pbsParams = ::SysAllocStringLen(strBuffer.c_str(), 
												 static_cast<UINT>(strBuffer.length()));
				ATLASSERT( *pbsParams );
				if ( !(*pbsParams) )
					throw EComLogError(E_OUTOFMEMORY);
			}
		}
		catch( CAtlException& e )
		{
			throw EAtlLogError(e);
		}
		catch( std::bad_alloc& e )
		{
			throw EStlLogError(e);
		}
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_GENERATEFMTPARAMSSTRING, &e);
	}
}

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

void CoNumericEditBox::ProvideFormatter(IFormatter** ppiFormatter) // throw(ELogError)
{
	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) )
				throw EComLogError(hr);

			m_pcoFormatter->Initialize(this);
		}

		*ppiFormatter = m_pcoFormatter;

		(*ppiFormatter)->AddRef();
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_CREATEOBJFORMATTER, &e);
	}
	catch( ... )
	{
		ATLASSERT( 0 );
		Unlock();
		throw;
	}

	Unlock();
}

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()  throw()
{
	if ( m_nFreezeEvents == 0 )
		return FireOnRequestEdit(DISPID_FORMATTERPARAMS);

	return S_OK;
}

void CoNumericEditBox::_OnFormatterParamsChanged(BOOL bValueTypeChanged, 
												 BOOL bUpdateCtrl)  throw()
{
	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;
	
	COleFormatterParams::ConvertValue(m_ctlEdit.GetFormatter().GetOleValueType(), 
									  lpcNewValue, bValid, &cvtNewValue);

	VARIANT_BOOL vbAccepted;
	Fire_OnValueChanging(cvtNewValue, &vbAccepted);

	return vbAccepted;
}

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

void CoNumericEditBox::_OnError(ELogError& e)
{
	Fire_OnError();
}

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

void CoNumericEditBox::DrawBorder(HDC hDC, RECT* prc, BOOL bAdjustRect) throw()
{
	struct DEParams
	{
		UINT fEdgeType;
		UINT fEdgeFlags;
	};

	const DEParams aDEParams[] = 
	{
		{ BDR_SUNKENOUTER,					BF_MONO|BF_FLAT|BF_SOFT },	// acFlat
		{ BDR_SUNKENINNER|BDR_SUNKENOUTER,	0 },						// ac3D
		{ BDR_SUNKENINNER,					BF_SOFT }					// acSoft
	};

	ATLASSERT( m_ctlEdit.GetAppearance() < sizeof(aDEParams)/sizeof(DEParams) );

	if ( m_ctlEdit.GetBorderVisible() )
	{
		const DEParams& deParams = 
			aDEParams[static_cast<SIZE_T>(m_ctlEdit.GetAppearance())];

		const UINT fEdgeType = deParams.fEdgeType;
		const UINT fEdgeFlags = (bAdjustRect) 
			? deParams.fEdgeFlags|BF_RECT|BF_ADJUST
			: deParams.fEdgeFlags|BF_RECT;

		::DrawEdge(hDC, prc, fEdgeType, fEdgeFlags);
	}
}

void CoNumericEditBox::CalcMargins(HDC hDC, LONG* pdxLeft, LONG* pdxRight, 
								   LONG* pdyTopBottom) throw()
{
	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;
}

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

void CoNumericEditBox::InitData() // throw(ELogError)
{
	ATLASSERT( !m_ctlEdit.IsWindow() );
	ATLASSERT( m_pcoFontNS && !m_pcoFontNS->GetCookie() );

	try
	{
		// 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 );

			m_ctlEdit.SetOleFontCopy(spiAmbientFont);
		}
		else
		{
			HFONT hFont = reinterpret_cast<HFONT>(::GetStockObject(DEFAULT_GUI_FONT));

			m_ctlEdit.SetFontHandle(hFont);
		}

		ATLASSERT( m_ctlEdit.GetRefOleFont() );

		m_pcoFontNS->Advise(m_ctlEdit.GetRefOleFont());

		// 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
		m_ctlEdit.GetFormatter().Configure(COleFormatterParams(vtInt32, ftNumeric));	
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_INITCONTROLDATA, &e);		
	}
}

void CoNumericEditBox::GetDataSizeMax(ULARGE_INTEGER* pcbSize) // throw(ELogError)
{
	ATLASSERT( !::IsBadWritePtr(pcbSize, sizeof(ULARGE_INTEGER)) );

	try
	{
		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.GetRefOleFont());
		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;
		ProvideFormatter(&spiFormatter);
		ATLASSERT( m_pcoFormatter );

		ULARGE_INTEGER cbSize;
		m_pcoFormatter->GetDataSizeMax(&cbSize);
		pcbSize->LowPart += cbSize.LowPart;
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_CALCSIZECONTROLDATA, &e);
	}
}

void CoNumericEditBox::LoadData(IStream* piStream) // throw(ELogError)
{
	try
	{
		try
		{
			VARIANT_BOOL vbValue;
			OLE_COLOR oleClr;

			// Check version
			DWORD dwVer;
			HRESULT hr = piStream->Read(&dwVer, sizeof(DWORD), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			if ( dwVer > _ATL_VER )
				throw EAppLogError(ENEC_ATLVERSION);

			// Read size of control extents.
			hr = piStream->Read(&m_sizeExtent, sizeof(SIZE), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Read Appearance.
			AppearanceConstants enAppearance;
			hr = piStream->Read(&enAppearance, sizeof(AppearanceConstants), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetAppearance(enAppearance);

			// Read BorderVisible.	
			hr = piStream->Read(&vbValue, sizeof(VARIANT_BOOL), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetBorderVisible(vbValue == VARIANT_TRUE);

			// Read AlignmentConstants
			AlignmentConstants enAlignment;
			hr = piStream->Read(&enAlignment, sizeof(AlignmentConstants), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetAlignment(enAlignment);

			// Read ForeColor	
			hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetForeOleColor(oleClr); 

			// Read BackColor	
			hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetBackOleColor(oleClr); 

			// Read ReadOnlyForeColor
			hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetReadOnlyForeColor(oleClr); 

			// Read ReadOnlyBackColor
			hr = piStream->Read(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetReadOnlyBackColor(oleClr); 

			// Read Font object
			CComVariant cvtFont;
			hr = cvtFont.ReadFromStream(piStream);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr);

			hr = cvtFont.ChangeType(VT_UNKNOWN);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr);

			CComPtr<IFont> spiFont;
			if ( cvtFont.punkVal )
			{
				hr = cvtFont.punkVal->QueryInterface(IID_IFont, (void**)(&spiFont));
				ATLASSERT( SUCCEEDED(hr) );
				if ( FAILED(hr) )
					throw EComLogError(hr, cvtFont.punkVal, IID_IUnknown);
			}

			m_ctlEdit.SetOleFont(spiFont);

			// Read ReadOnly.
			hr = piStream->Read(&vbValue, sizeof(VARIANT_BOOL), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetReadOnly(vbValue == VARIANT_TRUE);

			// Read RightToLeft
			hr = piStream->Read(&vbValue, sizeof(VARIANT_BOOL), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			m_ctlEdit.SetRightToLeft(vbValue == VARIANT_TRUE);

			// ErrorSymbol
			CComBSTR cbsErrorSymbol;
			hr = cbsErrorSymbol.ReadFromStream(piStream);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr);

			m_ctlEdit.SetErrorOleSymbol(cbsErrorSymbol);

			// Read Formatter parameters
			CComPtr<IFormatter> spiFormatter;
			ProvideFormatter(&spiFormatter);
			ATLASSERT( m_pcoFormatter );

			m_pcoFormatter->LoadData(piStream);
		}
		catch( CAtlException& e )
		{
			throw EAtlLogError(e);
		}
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_LOADCONTROLDATA, &e);
	}
}

void CoNumericEditBox::SaveData(IStream* piStream) // throw(ELogError)
{
	try
	{
		try
		{
			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) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write control extent.
			hr = piStream->Write(&m_sizeExtent, sizeof(SIZE), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write Appearance.
			AppearanceConstants enAppearance = m_ctlEdit.GetAppearance();
			hr = piStream->Write(&enAppearance, sizeof(AppearanceConstants), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write BorderVisible.	
			vbValue = m_ctlEdit.GetBorderVisible() ? VARIANT_TRUE : VARIANT_FALSE;
			hr = piStream->Write(&vbValue, sizeof(VARIANT_BOOL), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write AlignmentConstants
			AlignmentConstants enAlignment = m_ctlEdit.GetAlignment();
			hr = piStream->Write(&enAlignment, sizeof(AlignmentConstants), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write ForeColor	
			oleClr = m_ctlEdit.GetForeOleColor();
			hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write BackColor	
			oleClr = m_ctlEdit.GetBackOleColor();
			hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write ReadOnlyForeColor
			oleClr = m_ctlEdit.GetReadOnlyForeOleColor();
			hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write ReadOnlyBackColor
			oleClr = m_ctlEdit.GetReadOnlyForeOleColor();
			hr = piStream->Write(&oleClr, sizeof(OLE_COLOR), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write Font object
			CComVariant cvtFont = m_ctlEdit.GetRefOleFont();
			hr = cvtFont.WriteToStream(piStream);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write ReadOnly.
			vbValue = m_ctlEdit.GetReadOnly() ? VARIANT_TRUE : VARIANT_FALSE;
			hr = piStream->Write(&vbValue, sizeof(VARIANT_BOOL), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write RightToLeft.
			vbValue = m_ctlEdit.GetRightToLeft() ? VARIANT_TRUE : VARIANT_FALSE;
			hr = piStream->Write(&vbValue, sizeof(VARIANT_BOOL), NULL);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// ErrorSymbol
			CComBSTR cbsErrorSymbol;
			m_ctlEdit.GetErrorOleSymbol(&cbsErrorSymbol);
			hr = cbsErrorSymbol.WriteToStream(piStream);
			ATLASSERT( SUCCEEDED(hr) );
			if ( FAILED(hr) )
				throw EComLogError(hr, piStream, IID_IStream);

			// Write Formatter parameters
			CComPtr<IFormatter> spiFormatter;
			ProvideFormatter(&spiFormatter);
			ATLASSERT( m_pcoFormatter );

			m_pcoFormatter->SaveData(piStream);
		}
		catch( CAtlException& e )
		{
			throw EAtlLogError(e);
		}
	}
	catch( ELogError& e )
	{
		throw EAppLogError(ENEC_SAVECONTROLDATA, &e);
	}
}

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

BOOL CoNumericEditBox::PreTranslateAccelerator(LPMSG pMsg, HRESULT& hRet) throw()
{
	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) throw()
{
	if ( m_ctlEdit.IsWindow() )
		return S_OK;

	try
	{
		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);

		const DWORD aDTOptions[] =
		{
			DT_EDITCONTROL|DT_NOPREFIX|DT_RIGHT,
			DT_EDITCONTROL|DT_NOPREFIX|DT_LEFT,
			DT_EDITCONTROL|DT_NOPREFIX|DT_CENTER,
			DT_EDITCONTROL|DT_NOPREFIX|DT_RIGHT
		};

		ATLASSERT( m_ctlEdit.GetAlignment() < sizeof(aDTOptions)/sizeof(DWORD) );

		DWORD fDTOptions = aDTOptions[static_cast<SIZE_T>(m_ctlEdit.GetAlignment())];

		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);
	}
	catch( ELogError& )
	{
		ATLASSERT( 0 );
		return E_FAIL;
	}

	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;
	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;
	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.IsEditing() )
	{
		bool bModifyWnd = m_ctlEdit.SendMessage(EM_GETMODIFY) != FALSE;
		bool bModifyCtl = m_ctlEdit.IsModified() != 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;

	try
	{
		m_ctlEdit.BeginEdit();
	}
	catch( ELogError& e )
	{
		_OnError(e);
	}
	catch( ... )
	{
		ATLASSERT( 0 );
		_OnError(EAppLogError(ENEC_UNEXPECTED));
	}

	return 0;
}

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

	try
	{
		m_ctlEdit.EndEdit();
	}
	catch( ELogError& e )
	{
		_OnError(e);
	}
	catch( ... )
	{
		ATLASSERT( 0 );
		_OnError(EAppLogError(ENEC_UNEXPECTED));
	}

	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

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

	try
	{
		InitData();
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), 
											 IID_IPersistStreamInit);
	}

	return S_OK;
}

HRESULT CoNumericEditBox::_PersistStream_GetSizeMax(ULARGE_INTEGER* pcbSize)
{
	ATLASSERT( !::IsBadWritePtr(pcbSize, sizeof(ULARGE_INTEGER)) );
	
	try
	{
		GetDataSizeMax(pcbSize);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), 
											 IID_IPersistStreamInit);
	}

	return S_OK;
}

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

	try
	{
		LoadData(piStream);

		m_bRequiresSave = FALSE;
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), 
											 IID_IPersistStreamInit);
	}

	return S_OK;
}

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

	try
	{
		SaveData(piStream);

	//	if ( fClearDirty )
	//		m_bRequiresSave = FALSE;
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), 
											 IID_IPersistStreamInit);
	}

	return S_OK;
}

HRESULT CoNumericEditBox::_PersistStorage_InitNew(IStorage* piStorage)
{
	ATLASSERT( !m_ctlEdit.IsWindow() );
	ATLASSERT( m_pcoFontNS && !m_pcoFontNS->GetCookie() );

	try
	{
		InitData();
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), 
											 IID_IPersistStreamInit);
	}

	return S_OK;
}

HRESULT CoNumericEditBox::_PersistStorage_Load(IStorage* piStorage)
{
	ATLASSERT( piStorage );

	try
   	{
		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)
			throw EComLogError(hr, piStorage, IID_IStorage);

		LoadData(spiStream);

		m_bRequiresSave = FALSE;
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_IPersistStorage);
	}

	return S_OK;
}

HRESULT CoNumericEditBox::_PersistStorage_Save(IStorage* piStorage, BOOL fSameAsLoad)
{
	ATLASSERT( piStorage );

	try
	{
		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)
			throw EComLogError(hr, piStorage, IID_IStorage);

		SaveData(spiStream);

	//	if ( fClearDirty )
	//		m_bRequiresSave = FALSE;
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_IPersistStorage);
	}

	return S_OK;
}

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") );

	try
	{
		BOOL bUserMode;
		HRESULT hr = GetAmbientUserMode(bUserMode);
		if ( SUCCEEDED(hr) && bUserMode && IsWindow() )
			throw EComLogError(ENEC_CANNOTCHANGEALIGNMENT);

		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);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

	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") );

	try
	{
		m_ctlEdit.GetOleFontDisp(ppiFont);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

	return S_OK;
}

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;

	try
	{
		m_ctlEdit._OnFontChanging();

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

		m_ctlEdit.SetCopyOleFontDisp(piFont);

		m_bRequiresSave = TRUE;

		// Connect property notify sink
		if ( m_ctlEdit.GetRefOleFont() )
			m_pcoFontNS->Advise(m_ctlEdit.GetRefOleFont());

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

		m_ctlEdit._OnFontChanged();

		FireViewChange();
		SendOnDataChange(NULL);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}
 
	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;

	try
	{
		m_ctlEdit._OnFontChanging();

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

		m_ctlEdit.SetOleFontDisp(piFont);
		
		m_bRequiresSave = TRUE;

		// Connect property notify sink
		if ( m_ctlEdit.GetRefOleFont() )
			m_pcoFontNS->Advise(m_ctlEdit.GetRefOleFont());

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

		m_ctlEdit._OnFontChanged();

		FireViewChange();
		SendOnDataChange(NULL);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

 	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;

	try
	{
		m_ctlEdit.GetErrorOleSymbol(pbsErrorSymbol);
	}
	catch ( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

	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;

	try
	{
		m_ctlEdit.SetErrorOleSymbol(bsErrorSymbol);

		m_bRequiresSave = TRUE;

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

		m_ctlEdit._OnErrorSymbolChanged();

		FireViewChange();
		SendOnDataChange(NULL);
	}
	catch ( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

	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;

	try
	{
		const ValueTypeConstants enValueType = 
			m_ctlEdit.GetFormatter().GetOleValueType();

		const FormatTypeConstants enFormatType = 
			m_ctlEdit.GetFormatter().GetOleFormatType();

		CComBSTR cbsDisplayMask;
		if ( m_ctlEdit.GetFormatter().IsOleUserMask(FM_DISPLAY) )
			m_ctlEdit.GetFormatter().GetOleMask(FM_DISPLAY, &cbsDisplayMask);

		CComBSTR cbsEditingMask;
		if ( m_ctlEdit.GetFormatter().IsOleUserMask(FM_EDITING) )
			m_ctlEdit.GetFormatter().GetOleMask(FM_EDITING, &cbsEditingMask);


		BuildFormatterParamString(enValueType, enFormatType, 
								  cbsDisplayMask, cbsEditingMask,
								  pbsParams);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

	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;

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

		ParseFormatterParamString(bsParams, &enValueType, &enFormatType,
								  &cbsDisplayMask, &cbsEditingMask);

		const ValueTypeConstants enOldValueType = 
			m_ctlEdit.GetFormatter().GetOleValueType();

		// Set new formatter properties
		const COleFormatterParams params(enValueType, enFormatType, 
										 cbsDisplayMask, cbsEditingMask);

		m_ctlEdit.GetFormatter().Configure(params);

		m_ctlEdit._OnFormatterChanged(enOldValueType != enValueType);

		m_bRequiresSave = TRUE;

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

		FireViewChange();
		SendOnDataChange(NULL);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

	return S_OK;
}

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

	if ( !ppiFormatter )
		return E_POINTER;
	
	try
	{
		ProvideFormatter(ppiFormatter);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

	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.IsModified() ? 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;

	try
	{
		m_ctlEdit.GetOleValue(pvValue);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

 	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;

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

			vValue = cvtTemp;
		}

		m_ctlEdit.SetOleValue(vValue);

		m_bRequiresSave = TRUE;

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

		m_ctlEdit._OnValueChanged();

		FireViewChange();
		SendOnDataChange(NULL);
	}
	catch ( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

 	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;

	try
	{
		m_ctlEdit.GetDisplayOleText(pbsText);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

 	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;

	try
	{
		m_ctlEdit.GetEditingOleText(pbsText);
	}
	catch( ELogError& e )
	{
		return _AtlModule.ReportErrorWithCom(e, GetObjectCLSID(), IID_INumericEditBox);
	}

 	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