Click here to Skip to main content
15,896,063 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.2
//
//	File:			NumericEditCore.h		
//
//	Developer(s):	Sergei Pavlovsky
//					sergei_vp@hotmail.com
//					Copyright (c) 2004-2005
//
//	Description:	Interface for CCoreFormatter class 
//					Declaration of CCoreNumericEdit class template.
//
//	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.
/////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#include "resource.h"

#include "spDataFormatter.h"
#include "spStlLogError.h"
#include "spAtlLogError.h"
#include "AppLogError.h"
#include "spStringUtils.h"

/////////////////////////////////////////////////////////////////////////////////////////
// Declarations
/////////////////////////////////////////////////////////////////////////////////////////

using SP::ELogError;
using SP::EStlLogError;
using SP::EAtlLogError;
using SP::CDataFormatter;

/////////////////////////////////////////////////////////////////////////////////////////
// Types
/////////////////////////////////////////////////////////////////////////////////////////

enum FormatModeEnum
{
	FM_NON		= 0x00000000,
	FM_DISPLAY	= 0x00000001,
	FM_EDITING	= 0x00000002
};

/////////////////////////////////////////////////////////////////////////////////////////
// CCoreFormatterParams class
/////////////////////////////////////////////////////////////////////////////////////////

class CCoreFormatterParams
{
// Declarations
private:
	friend class CCoreFormatter;

// Types
public:
	typedef CDataFormatter::EnumDataType	EnumDataType;
	typedef CDataFormatter::EnumFormatTrait	EnumFormatTrait;
	typedef CDataFormatter::EnumPropertyID	EnumPropertyID;
	typedef CDataFormatter::Property		Property;

// Constants
private:
	static const EnumPropertyID ms_aPropertyIDs[];
	static const SIZE_T			ms_cPropertyIDs;

	static const Property		ms_aNonStdDefEditingPrpos[];
	static const SIZE_T			ms_cNonStdDefEditingPrpos;

// Types
protected:
	typedef std::map<EnumPropertyID, tstring> CPropertyMap;

// Utilities
public:
	static BOOL IsPropertyUsed(EnumDataType enType, WORD fTraits, 
							   EnumPropertyID enProperty) throw();

// Construction & destruction
public:
	CCoreFormatterParams(); // throw(ELogError)

	CCoreFormatterParams(EnumDataType enDataType, WORD fFmtTraits, 
						 const Property* pcaDisplayFmtProps = NULL,
						 const SIZE_T cDisplayFmtProps = 0, 
						 LPCTSTR lpcszDisplayMask = NULL,
						 const Property* pcaEditingFmtProps = NULL,
						 const SIZE_T cEditingFmtProps = 0, 
						 LPCTSTR lpcszEditingMask = NULL); // throw(ELogError)

	CCoreFormatterParams(const CCoreFormatterParams& params); // throw(ELogError)

private:
	CCoreFormatterParams(const CDataFormatter* pcDisplayFmt,
						 const CDataFormatter* pcEditingFmt); // throw(ELogError)

// Operators
public:
	CCoreFormatterParams& operator=(const CCoreFormatterParams& src); // throw(ELogError)

// Helpers
private:
	void InitNonStandardDefaultEditingProperties();  // throw(ELogError)
	void AssignMaskAndProperties(FormatModeEnum enMode, const CDataFormatter* pcFmt);
								 // throw(ELogError)

// Helpers
protected:
	const tstring& _GetMaskString(FormatModeEnum enMode) const throw()
	{
		ATLASSERT( enMode == FM_DISPLAY || enMode == FM_EDITING );

		return m_aMasks[static_cast<SIZE_T>(enMode) - 1];
	}

	tstring& _GetMaskString(FormatModeEnum enMode) throw()
	{
		ATLASSERT( enMode == FM_DISPLAY || enMode == FM_EDITING );

		return m_aMasks[static_cast<SIZE_T>(enMode) - 1];
	}

	const CPropertyMap& _GetPropertyMap(FormatModeEnum enMode) const throw()
	{
		ATLASSERT( enMode == FM_DISPLAY || enMode == FM_EDITING );

		return m_aPropMaps[static_cast<SIZE_T>(enMode) - 1];
	}

	CPropertyMap& _GetPropertyMap(FormatModeEnum enMode) throw()
	{
		ATLASSERT( enMode == FM_DISPLAY || enMode == FM_EDITING );

		return m_aPropMaps[static_cast<SIZE_T>(enMode) - 1];
	}

// Accessors & mutators
protected:
	LPCTSTR _GetProperty(FormatModeEnum enMode, EnumPropertyID enPropID) const throw()
	{
		const CPropertyMap& mapProps = _GetPropertyMap(enMode);

		CPropertyMap::const_iterator it = mapProps.find(enPropID);

		return ( it != mapProps.end() ) ? (*it).second.c_str() : NULL;
	}

	void _SetProperty(FormatModeEnum enMode, EnumPropertyID enPropID, 
					  LPCTSTR lpcszValue); // throw(ELogError)


	SIZE_T _GetProperties(FormatModeEnum enMode, Property* paProps, 
						 SIZE_T cProps) const throw();

	void _SetProperties(FormatModeEnum enMode, const Property* pcaProps = NULL,
					   SIZE_T cProps = 0);  // throw(ELogError)

	void _ClearProperties(FormatModeEnum enMode) throw()
	{		
		_GetPropertyMap(enMode).clear();

		if ( enMode == FM_EDITING )
			InitNonStandardDefaultEditingProperties();
	}


	LPCTSTR _GetMask(FormatModeEnum enMode) const throw()
	{
		return _GetMaskString(enMode).empty() ? NULL : _GetMaskString(enMode).c_str();
	}

	void _SetMask(FormatModeEnum enMode, LPCTSTR lpcszValue); // throw(ELogError)


// Accessors & mutators
public:
	EnumDataType GetDataType() const throw()
	{
		return m_enDataType;
	}

	WORD GetFormatTraits() const throw()
	{
		return m_fFmtTraits;
	}


	LPCTSTR GetProperty(FormatModeEnum enMode, EnumPropertyID enPropID) const throw()
	{
		return _GetProperty(enMode, enPropID);
	}

	void SetProperty(FormatModeEnum enMode, EnumPropertyID enPropID, 
					 LPCTSTR lpcszValue) // throw(ELogError)
	{
		try
		{
			_SetProperty(enMode, enPropID, lpcszValue);
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_SETFMTPROPVALUE, &e);
		}
	}


	SIZE_T GetProperties(FormatModeEnum enMode, Property* paProps, 
						 SIZE_T cProps) const throw()
	{
		return _GetProperties(enMode, paProps, cProps);
	}

	void SetProperties(FormatModeEnum enMode, const Property* pcaProps = NULL,
					   SIZE_T cProps = 0)  // throw(ELogError)
	{
		try
		{
			_SetProperties(enMode, pcaProps, cProps);
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_SETFMTPROPVALUES, &e);
		}
	}

	void ClearProperties(FormatModeEnum enMode) throw()
	{
		_ClearProperties(enMode);
	}


	LPCTSTR GetMask(FormatModeEnum enMode) const throw()
	{
		return _GetMask(enMode);
	}

	void SetMask(FormatModeEnum enMode, LPCTSTR lpcszValue) // throw(ELogError)
	{
		try
		{
			_SetMask(enMode, lpcszValue);
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_SETFMTMASK, &e);
		}
	}

// Data members
private:
	EnumDataType	m_enDataType;
	WORD			m_fFmtTraits;

	tstring			m_aMasks[2];
	CPropertyMap	m_aPropMaps[2];
};

/////////////////////////////////////////////////////////////////////////////////////////
// CCoreFormatter class
/////////////////////////////////////////////////////////////////////////////////////////

class CCoreFormatter
{
// Types
public:
	typedef CDataFormatter::EnumDataType	EnumDataType;
	typedef CDataFormatter::EnumFormatTrait	EnumFormatTrait;
	typedef CDataFormatter::EnumPropertyID	EnumPropertyID;
	typedef CDataFormatter::Property		Property;
	typedef std::vector<Property>			CPropertyVector;

// Utilities
public:
	static DWORD GetMaskCompositionOptions(FormatModeEnum enMode) throw()
	{
		ATLASSERT( enMode == FM_DISPLAY || enMode == FM_EDITING );

		const DWORD adwCompositionOptions[] = 
		{ 
			0, 

			CDataFormatter::CMO_OPTFRACTIONPART|
			CDataFormatter::CMO_OPTEXPONENTPART
		};

		return adwCompositionOptions[static_cast<SIZE_T>(enMode) - 1];
	}

// Construction & destruction
public:
	CCoreFormatter() throw()
	{
		m_paFormatters[0] = NULL;
		m_paFormatters[1] = NULL;
	}

	~CCoreFormatter() throw()
	{
		if ( m_paFormatters[0] )
			m_paFormatters[0]->Release();

		if ( m_paFormatters[1] )
			m_paFormatters[1]->Release();
	}

// Helpers
private:
	const CDataFormatter* GetRawFormatter(FormatModeEnum enMode) const throw()
	{
		ATLASSERT( enMode == FM_DISPLAY || enMode == FM_EDITING );

		return m_paFormatters[static_cast<SIZE_T>(enMode) - 1];
	}

	CDataFormatter* GetRawFormatter(FormatModeEnum enMode) throw()
	{
		ATLASSERT( enMode == FM_DISPLAY || enMode == FM_EDITING );

		return m_paFormatters[static_cast<SIZE_T>(enMode) - 1];
	}

	static void CreateRawFormatter(FormatModeEnum enMode, 
								   const CCoreFormatterParams& params,
								   CDataFormatter** ppFmt);	  // throw(ELogError)

// Operations: Formatter configuration
public:
	void Configure(const CCoreFormatterParams& params); // throw(ELogError)
	void Unconfigure() throw();

	BOOL IsConfigured() const throw()
	{
		ATLASSERT( (m_paFormatters[0] && m_paFormatters[1]) ||
				   (!m_paFormatters[0] && !m_paFormatters[1]) );

		return m_paFormatters[0] != NULL;
	}

	const CCoreFormatterParams GetParams() const // throw(ELogError)
	{
		ATLASSERT( IsConfigured() );

		return CCoreFormatterParams(GetRawFormatter(FM_DISPLAY), 
									GetRawFormatter(FM_EDITING));
	}

// Operations: Formatting
public:
	SIZE_T FormatDisplayText(const void* lpcValue, LPTSTR lpBuffer, 
							 SIZE_T cchBuffer) const; // throw(ELogError)

	SIZE_T FormatEditingText(const void* lpcValue, LPTSTR lpBuffer, 
							 SIZE_T cchBuffer) const; // throw(ELogError)

	void ScanEditingText(LPCTSTR lpszText, SIZE_T cchText, void* lpValue, 
						 BOOL* pbIsValid, BOOL* pbIsNull) const; // throw(ELogError)

// Accessors
protected:
	BOOL _IsPropertyUserValue(FormatModeEnum enMode, EnumPropertyID enID) const throw()
	{
		ATLASSERT( IsConfigured() );
			
		return GetRawFormatter(enMode)->IsPropertyUserValue(enID);
	}

	SIZE_T _GetPropertyValue(FormatModeEnum enMode, EnumPropertyID enID, 
							LPTSTR lpBuf = NULL, SIZE_T cchBuf = 0) const
							// throw(ELogError)
	{
		return GetRawFormatter(enMode)->GetProperty(enID, lpBuf, cchBuf);
	}

	BOOL _IsUserMask(FormatModeEnum enMode) const throw()
	{
		ATLASSERT( IsConfigured() );
		
		return GetRawFormatter(enMode)->IsUserMask();
	}

	SIZE_T _GetMask(FormatModeEnum enMode, LPTSTR lpszMask = NULL, 
				   SIZE_T cchMask = 0) const // throw(ELogError)
	{
		return GetRawFormatter(enMode)->GetMask(lpszMask, cchMask);
	}

// Accessors
public:
	EnumDataType GetDataType() const throw()
	{
		ATLASSERT( IsConfigured() );

		return (*m_paFormatters)->GetDataType();
	}

	SIZE_T GetDataSize() const throw()
	{
		ATLASSERT( IsConfigured() );

		return (*m_paFormatters)->GetDataSize();
	}

    WORD GetFormatTraits() const throw()
	{
		ATLASSERT( IsConfigured() );

		return (*m_paFormatters)->GetTraits();
	}


	BOOL IsPropertyUserValue(FormatModeEnum enMode, EnumPropertyID enID) const throw()
	{
		ATLASSERT( IsConfigured() );
			
		return GetRawFormatter(enMode)->IsPropertyUserValue(enID);
	}

	SIZE_T GetPropertyValue(FormatModeEnum enMode, EnumPropertyID enID, 
							LPTSTR lpBuf = NULL, SIZE_T cchBuf = 0) const;
							// throw(ELogError)


	BOOL IsUserMask(FormatModeEnum enMode) const throw()
	{
		ATLASSERT( IsConfigured() );
		
		return GetRawFormatter(enMode)->IsUserMask();
	}

	SIZE_T GetMask(FormatModeEnum enMode, LPTSTR lpszMask = NULL, 
				   SIZE_T cchMask = 0) const; // throw(ELogError)

// Data members
private:
	CDataFormatter*	m_paFormatters[2];
};

/////////////////////////////////////////////////////////////////////////////////////////
// CCoreNumericEditEvents class
/////////////////////////////////////////////////////////////////////////////////////////

class CCoreNumericEditEventSink
{
// Event
public:
	virtual void _OnModified() = 0;
	virtual BOOL _OnValueChanging(const void* lpcNewValue, BOOL bValid) = 0;
	virtual void _OnValueChanged() = 0;
	virtual void _OnError(ELogError& e) = 0;
};

/////////////////////////////////////////////////////////////////////////////////////////
// CCoreNumericEdit class template
/////////////////////////////////////////////////////////////////////////////////////////

template < class TWindow, class TFormatter, class TParams >
class CCoreNumericEdit : public TParams,
						 public TWindow
{
// Constants
private:
	enum EnumValueStatus
	{
		VS_VALUENULL			= 0x0001,		
		VS_VALUEVALID			= 0x0002
	};

	enum EnumControlState
	{
		CST_MODIFY				= 0x0001,
		CST_EDITING				= 0x0001,

		CST_ALL					= 0xffff
	};

// Types
private:
	union Numeric
	{
		char				Int8;
		unsigned char		UInt8;
		short				Int16;
		unsigned short		UInt16;
		int					Int32;
		unsigned int		UInt32;
		__int64				Int64;
		unsigned __int64	UInt64;
		float				Float;
		double				Double;
	};

// Construction & destruction
protected:
	CCoreNumericEdit() throw()
		: m_fVlaueStatus(VS_VALUENULL|VS_VALUEVALID),
		  m_lpszCurrentText(NULL),
		  m_fStates(0),
		  m_hbrBkg(NULL),
		  m_pEventSink(NULL)
	{
		::ZeroMemory(m_rgValue, sizeof(Numeric));
	}

	~CCoreNumericEdit() throw()
	{
		if ( m_hbrBkg )
			::DeleteObject(m_hbrBkg);

		free(m_lpszCurrentText);
	}

// Helpers
private:
	void ReallocCurrentTextBuf(SIZE_T cchBuf) const // throw(ELogError)
	{
		void* lpBuf = realloc(m_lpszCurrentText, (cchBuf + 1) * sizeof(TCHAR));
		ATLASSERT( lpBuf );
		if ( !lpBuf )
			throw EAppLogError(ENEC_OUTOFMEMORY);		

		m_lpszCurrentText = reinterpret_cast<LPTSTR>(lpBuf);
	}

	BOOL IsSameValue(const BYTE* lpcValue, WORD fValueStatus) const throw()
	{
		ATLASSERT( m_formatter.IsConfigured() );
		ATLASSERT( !lpcValue || !::IsBadReadPtr(lpcValue, m_formatter.GetDataSize()) );

		if ( fValueStatus != m_fVlaueStatus )
			return FALSE;

		if ( IsValueValid() && !IsValueNull() )
		{
			const SIZE_T sizeValue = m_formatter.GetDataSize();

			for ( SIZE_T i = 0; i < sizeValue; i++ )
			{
				if ( lpcValue[i] != m_rgValue[i] )
					return FALSE;
			}
		}

		return TRUE;
	}

	void SetEditingText() // throw(ELogError)
	{
		ATLASSERT( IsWindow() );

		try
		{
			if ( IsValueValid() )
			{
				const SIZE_T cchText = GetEditingText(NULL, 0);
				ReallocCurrentTextBuf(cchText);
				GetEditingText(m_lpszCurrentText, cchText);

				m_lpszCurrentText[cchText] = _T('\x0');
			}

			// Set text in the control
			SetWindowText(m_lpszCurrentText);
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_SETEDITINGTEXT, &e);
		}
	}

	void SetDisplayText() // throw(ELogError)
	{
		ATLASSERT( m_formatter.IsConfigured() );
		ATLASSERT( IsWindow() );

		try
		{
			if ( IsValueValid() )
			{
				const SIZE_T cchText = GetDisplayText(NULL, 0);
				
				ReallocCurrentTextBuf(cchText);

				GetDisplayText(m_lpszCurrentText, cchText);

				m_lpszCurrentText[cchText] = _T('\x0');

				// Set text in the control
				SetWindowText(m_lpszCurrentText);
			}
			else
			{
				try
				{
					const SIZE_T cchText = GetDisplayText(NULL, 0);
					const SIZE_T cchTextBuf = cchText + 1;

					CTempBuffer<TCHAR> bufText(cchTextBuf);

					GetDisplayText(bufText, cchTextBuf);
					ATLASSERT( bufText[cchText] == _T('\x0') );

					// Set text in the control
					SetWindowText(bufText);
				}
				catch( CAtlException& e )
				{
					throw EAtlLogError(e);
				}
			}
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_SETDISPLAYTEXT, &e);
		}
	}

	void SaveEditText() const // throw(ELogError)
	{
		ATLASSERT( IsWindow() );
		
		try
		{
			// Save edit text
			const SIZE_T cchText = static_cast<SIZE_T>(GetWindowTextLength());

			ReallocCurrentTextBuf(cchText);
			
			GetWindowText(m_lpszCurrentText, static_cast<int>(cchText + 1));
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_SAVEEDITTEXT, &e);
		}
	}

	void ScanValue() const  // throw(ELogError)
	{
		ATLASSERT( m_formatter.IsConfigured() );
		ATLASSERT( IsWindow() );
		ATLASSERT( m_lpszCurrentText );

		try
		{
			const SIZE_T sizeValue = m_formatter.GetDataSize();

			WORD fNewVlaueState = 0;
			BYTE rgNewValue[sizeof(Numeric)];

			BOOL bIsValid; 
			BOOL bIsNull;

			// Read value from control
			m_formatter.ScanEditingText(m_lpszCurrentText, _tcslen(m_lpszCurrentText), 
										rgNewValue, &bIsValid, &bIsNull);

			if ( bIsValid )
				fNewVlaueState |= VS_VALUEVALID;

			if ( bIsNull )
				fNewVlaueState |= VS_VALUENULL;

			// Assign new value
			if ( !IsSameValue(rgNewValue, fNewVlaueState) )
			{
				if ( m_pEventSink )
				{
					// Fire OnValueChanging
					if ( m_pEventSink->_OnValueChanging(bIsNull ? NULL : rgNewValue, 
														bIsValid) )
					{
						// Set new value
						m_fVlaueStatus = fNewVlaueState;
						CopyMemory(m_rgValue, rgNewValue, sizeValue);

						// Fire OnValueChanged
						m_pEventSink->_OnValueChanged();
					}
				}
				else
				{
					// Set new value
					m_fVlaueStatus = fNewVlaueState;
					CopyMemory(m_rgValue, rgNewValue, sizeValue);
				}
			}
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_SCANEDITTEXT, &e);
		}
	}

// Operations
public:
	void BeginEdit() // throw(ELogError)
	{
		ATLASSERT( IsWindow() );

		if ( m_formatter.IsConfigured() ) 
		{
			// Set editing text
			SetEditingText();

			// Change state
			SetEditing(TRUE);
		}

		// Update modified flag
		SendMessage(EM_SETMODIFY, IsModified());
	}

	void EndEdit() // throw(ELogError)
	{	
		if ( m_formatter.IsConfigured() )
		{
			// Save text edit control	
			if ( IsEditing() )
			{
				try
				{
					SaveEditText();
				}
				catch( ... )
				{
					// Change state
					SetEditing(FALSE);
					throw;
				}

				// Change state
				SetEditing(FALSE);
			}

			// Get user value
			ScanValue();

			// Update display text
			SetDisplayText();
		}
	}

// Operations: Event Sink
public:
	void AdviseEventSink(CCoreNumericEditEventSink* pEventSink) throw()
	{
		ATLASSERT( pEventSink );
		ATLASSERT( !m_pEventSink );

		m_pEventSink = pEventSink;
	}

	void UnadviseEventSink() throw()
	{
		ATLASSERT( m_pEventSink );

		m_pEventSink = NULL;
	}

// Event handlers
public:
	void _OnAppearanceChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnAppearanceChanged\n") );

		if ( IsWindow() )
		{
			UpdateWindowStyles(MES_APPEARANCE);
		}
	}

	void _OnBorderVisibleChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnBorderVisibleChanged\n") );

		if ( IsWindow() )
		{
			UpdateWindowStyles(MES_BORDER);
		}
	}

	void _OnAlignmentChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnAlignmentChanged\n") );

		if ( IsWindow() )
		{
			UpdateWindowStyles(MES_ALIGNMENT);
		}
	}

	void _OnForeColorChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnForeColorChanged\n") );

		if ( IsWindow() && !GetReadOnly() )
		{
			InvalidateRect(NULL);
		}
	}

	void _OnBackColorChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnBackColorChanged\n") );

		if ( IsWindow() && !GetReadOnly() )
		{
			if ( m_hbrBkg )
				::DeleteObject(m_hbrBkg);

			try
			{
				m_hbrBkg = ::CreateSolidBrush(GetBackColor());
			}
			catch( ELogError& )
			{
				ATLASSERT( 0 );
				m_hbrBkg = NULL;
			}

			InvalidateRect(NULL);
		}
	}

	void _OnReadOnlyForeColorChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnReadOnlyForeColorChanged\n") );

		if ( IsWindow() && GetReadOnly() )
		{
			InvalidateRect(NULL);
		}
	}

	void _OnReadOnlyBackColorChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnReadOnlyBackColorChanged\n") );

		if ( IsWindow() && GetReadOnly() )
		{
			if ( m_hbrBkg )
				::DeleteObject(m_hbrBkg);

			try
			{
				m_hbrBkg = ::CreateSolidBrush(GetReadOnlyBackColor());
			}
			catch( ELogError& )
			{
				ATLASSERT( 0 );
				m_hbrBkg = NULL;
			}

			InvalidateRect(NULL);
		}
	}

	void _OnFontChanging() throw() 
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnFontChanging\n") );

		if ( IsWindow() )
		{
			HFONT hFont = GetFont();
			if ( hFont )
			{
				SetFont(NULL, FALSE);
				FreeFontHandle(hFont);
			}			
		}
	}

	void _OnFontChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnFontChanged\n") );

		if ( IsWindow() )
		{
			try
			{
				if ( HFONT hFont = GetFontHandle() )
					SetFont(hFont, TRUE);

				InvalidateRect(NULL);
			}
			catch( ELogError& e )
			{
				if ( m_pEventSink )
					m_pEventSink->_OnError(e);
			}
			catch( ... )
			{
				ATLASSERT( 0 );
				if ( m_pEventSink )
					m_pEventSink->_OnError(EAppLogError(ENEC_UNEXPECTED));
			}
		}
	}

	void _OnReadOnlyChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnReadOnlyChanged\n") );

		if ( IsWindow() )
		{
			UpdateReadOnly(GetReadOnly());
		}
	}

	void _OnRightToLeftChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnRightToLeftChanged\n") );

		if ( IsWindow() )
		{
			UpdateWindowStyles(MES_RIGHTTOLEFT);
		}
	}

	void _OnErrorSymbolChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnErrorSymbolChanged\n") );

		if ( IsWindow() && !IsEditing() && !IsValueValid() )
		{
			try
			{
				SetDisplayText();
			}
			catch ( ELogError& e )
			{
				if ( m_pEventSink )
					m_pEventSink->_OnError(e);
			}
			catch( ... )
			{
				ATLASSERT( 0 );
				if ( m_pEventSink )
					m_pEventSink->_OnError(EAppLogError(ENEC_UNEXPECTED));
			}
		}
	}

	void _OnFormatterChanged(BOOL bValueTypeChanged) throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnFormatterChanged\n") );

		// Update current text
		if ( IsWindow() )
		{
			try
			{
				if ( bValueTypeChanged )
				{
					m_fVlaueStatus = VS_VALUENULL|VS_VALUEVALID;
				}

				UpdateReadOnly(GetReadOnly() || !m_formatter.IsConfigured());

				if ( IsEditing() )
					SetEditingText();
				else
					SetDisplayText();
			}
			catch ( ELogError& e )
			{
				if ( m_pEventSink )
					m_pEventSink->_OnError(e);
			}
			catch( ... )
			{
				ATLASSERT( 0 );
				if ( m_pEventSink )
					m_pEventSink->_OnError(EAppLogError(ENEC_UNEXPECTED));
			}
		}
	}

	void _OnModifyChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnModifyChanged\n") );

	}

	void _OnValueChanged() throw()
	{
		ATLTRACE( _T("CCoreNumericEdit::_OnValueChanged\n") );

		// Update current text
		if ( IsWindow() )
		{
			try
			{
				if ( IsEditing() )
					SetEditingText();
				else
					SetDisplayText();
			}
			catch ( ELogError& e )
			{
				if ( m_pEventSink )
					m_pEventSink->_OnError(e);
			}
			catch( ... )
			{
				ATLASSERT( 0 );
				if ( m_pEventSink )
					m_pEventSink->_OnError(EAppLogError(ENEC_UNEXPECTED));
			}
		}
	}

// Operations: Window
public:
	void DefineWindowStyles(DWORD* pdwStyle, DWORD* pdwStyleEx, 
							DWORD fProperties) const throw()
	{
		ATLASSERT( !::IsBadReadPtr(pdwStyle, sizeof(DWORD)) );
		ATLASSERT( !::IsBadReadPtr(pdwStyleEx, sizeof(DWORD)) );

		// Set Autoscroll
		*pdwStyle |= ES_AUTOHSCROLL;

		// Set border
		if ( fProperties & MES_BORDER )
		{
			const DWORD fBorderMask = WS_BORDER;
			const DWORD fBorderMaskEx = WS_EX_CLIENTEDGE|WS_EX_STATICEDGE;

			*pdwStyle &= (~fBorderMask);
			*pdwStyleEx &= (~fBorderMaskEx);

			if ( GetBorderVisible() )
			{
				const DWORD adwBorderStyles[] = 
				{ 
					WS_BORDER, 0, 0 
				};

				ATLASSERT( GetAppearance() < sizeof(adwBorderStyles)/sizeof(DWORD) );

				const DWORD adwBorderExStyles[] = 
				{ 
					0, WS_EX_CLIENTEDGE, WS_EX_STATICEDGE 
				};
				
				ATLASSERT( GetAppearance() < sizeof(adwBorderExStyles)/sizeof(DWORD) );

				*pdwStyle |= adwBorderStyles[GetAppearance()];
				*pdwStyleEx |= adwBorderExStyles[GetAppearance()];
			}
		}

		// Set Text alignment
		if ( fProperties & MES_ALIGNMENT )
		{
			const DWORD fAlignmentMask = ES_LEFT|ES_CENTER|ES_RIGHT;

			*pdwStyle &= (~fAlignmentMask);

			const DWORD adwAlign[] = 
			{ 
				ES_RIGHT, ES_LEFT, ES_CENTER, ES_RIGHT
			};

			ATLASSERT( GetAlignment() < sizeof(adwAlign)/sizeof(DWORD) );

			*pdwStyle |= adwAlign[GetAlignment()];
		}

		// Set ReadOnly
		if ( fProperties & MES_READONLY )
		{
			if ( GetReadOnly() || !m_formatter.IsConfigured() )
				*pdwStyle |= ES_READONLY;
			else
				*pdwStyle &= (~ES_READONLY);
		}

		// Set RightToLeft
		if ( fProperties & MES_RIGHTTOLEFT)
		{
			if ( GetRightToLeft() )
			{
				*pdwStyleEx &= (~WS_EX_LTRREADING);
				*pdwStyleEx |= WS_EX_RTLREADING;
			}
			else
			{
				*pdwStyleEx &= (~WS_EX_RTLREADING);
				*pdwStyleEx |= WS_EX_LTRREADING;
			}
		}
	}

	void UpdateWindowStyles(DWORD fProperties) throw()
	{
		ATLASSERT( IsWindow() );

		// Get current window styles
		DWORD dwStyle	= GetWindowLong(GWL_STYLE);
		DWORD dwStyleEx = GetWindowLong(GWL_EXSTYLE);

		// Correct styles
		DefineWindowStyles(&dwStyle, &dwStyleEx, fProperties);

		// Update window styles
		SetWindowLong(GWL_STYLE, dwStyle);
		SetWindowLong(GWL_EXSTYLE, dwStyleEx);		

		// Redraw window
		if ( fProperties & (MES_BORDER|MES_APPEARANCE) )
		{
			const DWORD fOptions = SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|
								   SWP_NOACTIVATE|SWP_FRAMECHANGED;

			SetWindowPos(NULL, 0, 0, 0, 0, fOptions);
		}

		if ( fProperties & (MES_ALIGNMENT|MES_READONLY|MES_RIGHTTOLEFT) )
		{
			InvalidateRect(NULL);
		}
	}

	void UpdateFont() throw()
	{
		ATLASSERT( IsWindow() );

		try
		{
			HFONT hFont = GetFont();
			if ( hFont )
			{
				SetFont(NULL, FALSE);
				FreeFontHandle(hFont);
			}

			if ( GetRefOleFont() )
			{
				hFont = GetFontHandle();
				if ( hFont )
					SetFont(hFont, FALSE);
			}
		}
		catch( ELogError& e )
		{
			if ( m_pEventSink )
				m_pEventSink->_OnError(e);
		}
	}

	void UpdateReadOnly(BOOL bReadOnly) throw()
	{
		ATLASSERT( IsWindow() );

		if ( m_hbrBkg )
			::DeleteObject(m_hbrBkg);

		try
		{
			m_hbrBkg = ( bReadOnly )
				? ::CreateSolidBrush(GetReadOnlyBackColor())
				: ::CreateSolidBrush(GetBackColor());
		}
		catch( ELogError& e )
		{
			m_hbrBkg = NULL;

			if ( m_pEventSink )
				m_pEventSink->_OnError(e);
		}

		SendMessage(EM_SETREADONLY, bReadOnly);
	}

	void UpdateEditDC(HDC hdc, HBRUSH* phbrBkg) throw()
	{
		ATLASSERT( !::IsBadWritePtr(phbrBkg, sizeof(HBRUSH)) );

		try
		{
			::SetTextColor(hdc, GetForeColor());
			::SetBkColor(hdc, GetBackColor());
		}
		catch ( ELogError& e )
		{
			if ( m_pEventSink )
				m_pEventSink->_OnError(e);
		}

		*phbrBkg = m_hbrBkg;
	}

	void UpdateStaticDC(HDC hdc, HBRUSH* phbrBkg) throw()
	{
		ATLASSERT( !::IsBadWritePtr(phbrBkg, sizeof(HBRUSH)) );

		try
		{
			::SetTextColor(hdc, GetReadOnlyForeColor());
			::SetBkColor(hdc, GetReadOnlyBackColor());
		}
		catch ( ELogError& e )
		{
			if ( m_pEventSink )
				m_pEventSink->_OnError(e);
		}

		*phbrBkg = m_hbrBkg;
	}

// Accessors & mutators
protected:

	// Value
	const void* _GetValue() const // throw(ELogError)
	{
		ATLASSERT( m_formatter.IsConfigured() );

		if ( IsEditing() )
		{
			try
			{
				// Save text edit control	
				SaveEditText();

				// Get user value
				ScanValue();
			}
			catch( ELogError& e )
			{
				throw EAppLogError(ENEC_SAVEEDITTEXT, &e);
			}
		}

		return IsValueNull() ? NULL : m_rgValue;
	}

	void _SetValue(const void* lpcValue, BOOL bValid) throw()
	{
		ATLASSERT( m_formatter.IsConfigured() );
		ATLASSERT( !lpcValue || !::IsBadReadPtr(lpcValue, m_formatter.GetDataSize()) );

		if ( bValid )
		{
			if ( lpcValue )
			{
				::CopyMemory(m_rgValue, lpcValue, m_formatter.GetDataSize());

				m_fVlaueStatus &= (~VS_VALUENULL);
			}
			else
			{
				m_fVlaueStatus |= VS_VALUENULL;
			}

			m_fVlaueStatus |= VS_VALUEVALID;
		}
		else
		{
			m_fVlaueStatus &= (~VS_VALUEVALID);
		}
	}

	// Display text
	SIZE_T _GetDisplayText(LPTSTR lpszText, SIZE_T cchText) const // throw(ELogError)
	{
		ATLASSERT( !::IsBadWritePtr(lpszText, cchText * sizeof(TCHAR)) );
		ATLASSERT( m_formatter.IsConfigured() );

		SIZE_T cchResult;

		if ( IsValueValid() )
		{
			cchResult = m_formatter.FormatDisplayText(GetValue(), lpszText, 
														cchText);
		}
		else if ( m_lpszCurrentText )
		{
			LPCTSTR lpcszErrorSymbol = GetErrorSymbol();
			ATLASSERT( lpcszErrorSymbol );
			const SIZE_T cchErrorSymbol = _tcslen(lpcszErrorSymbol);

			cchResult = _tcslen(m_lpszCurrentText) * cchErrorSymbol;
			if ( cchResult > 64 )
				cchResult = 64;

			if ( lpszText )
			{
				SIZE_T iSrcErrorSymbol = 0;

				for ( SIZE_T i = 0; i < cchText - 1; i++ )
				{
					lpszText[i] = lpcszErrorSymbol[iSrcErrorSymbol];

					if ( ++iSrcErrorSymbol == cchErrorSymbol )
						iSrcErrorSymbol = 0;
				}

				if ( cchText )
					lpszText[i] = _T('\x0');


				return i;
			}
		}

		return cchResult;
	}

	// Editing text
	SIZE_T _GetEditingText(LPTSTR lpszText, SIZE_T cchText) const // throw(ELogError)
	{
		ATLASSERT( !::IsBadWritePtr(lpszText, cchText * sizeof(TCHAR)) );
		ATLASSERT( m_formatter.IsConfigured() );

		if ( IsValueValid() )
		{
			return m_formatter.FormatEditingText(GetValue(), lpszText, cchText);
		}
		else if ( m_lpszCurrentText )
		{
			if ( lpszText )
				return SP::StrCopy(m_lpszCurrentText, lpszText, cchText);
			else
				return _tcslen(m_lpszCurrentText);
		}		

		return cchText ? SP::StrCopy(_T(""), lpszText, cchText) : 0;
	}

// Accessors & mutators
public:
	// Modify
	BOOL IsModified() const throw()
	{
		return m_fStates & CST_MODIFY;
	}

	void SetModify(BOOL bValue) throw()
	{
		if ( bValue )
			m_fStates |= CST_MODIFY;
		else
			m_fStates &= (~CST_MODIFY);
	}

	// Editing
	BOOL IsEditing() const throw()
	{
		return m_fStates & CST_EDITING;
	}

	void SetEditing(BOOL bValue) throw()
	{
		if ( bValue )
			m_fStates |= CST_EDITING;
		else
			m_fStates &= (~CST_EDITING);
	}	

	// Formatter
	const TFormatter& GetFormatter() const throw()
	{
		return m_formatter;
	}

	TFormatter& GetFormatter() throw()
	{
		return m_formatter;
	}

	// Value
	BOOL IsValueNull() const throw()
	{
		return m_fVlaueStatus & VS_VALUENULL;
	}

	BOOL IsValueValid() const throw()
	{
		return m_fVlaueStatus & VS_VALUEVALID;
	}

	const void* GetValue() const // throw(ELogError)
	{
		try
		{
			return _GetValue();
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_GETVALUE, &e);
		}
	}

	void SetValue(const void* lpcValue, BOOL bValid) throw()
	{
		_SetValue(lpcValue, bValid);
	}

	// Display text
	SIZE_T GetDisplayText(LPTSTR lpszText, SIZE_T cchText) const // throw(ELogError)
	{
		try
		{
			return _GetDisplayText(lpszText, cchText);
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_FORMATDISPLAYTEXT, &e);
		}
	}

	// Editing text
	SIZE_T GetEditingText(LPTSTR lpszText, SIZE_T cchText) const // throw(ELogError)
	{
		try
		{
			return _GetEditingText(lpszText, cchText);
		}
		catch( ELogError& e )
		{
			throw EAppLogError(ENEC_FORMATEDITINGTEXT, &e);
		}
	}

// Data members
private:
	TFormatter					m_formatter;

	mutable WORD				m_fVlaueStatus;
	mutable BYTE				m_rgValue[sizeof(Numeric)];
	mutable LPTSTR				m_lpszCurrentText;

	WORD						m_fStates;
	HBRUSH						m_hbrBkg;

	CCoreNumericEditEventSink*	m_pEventSink;
};

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