Click here to Skip to main content
15,881,204 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.6K   3.6K   36  
Masked numeric edit ActiveX control.
/////////////////////////////////////////////////////////////////////////////////////////
//	Project:		SP Numeric Edit Control 1.0
//
//	File:			NumericEditCore.h		
//
//	Developer(s):	Sergei Pavlovsky
//					sergei_vp@hotmail.com
//					Copyright (c) 2004
//
//	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"

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

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

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

class CCoreFormatter
{
// Constants
private:
	enum StateMasksEnum
	{
		SM_SIZE		= 0x00ff,
		SM_STATE	= 0xff00
	};

	enum StatesEnum
	{
		FS_USECUSTOMDISPLAYMASK	= 0x0100,
		FS_USECUSTOMEDITINGMASK	= 0x0200
	};

// Types
public:
	struct PROPERTY
	{
		PROPERTY()
		{
		}

		PROPERTY(SP::CDataFormatter::PropertiesEnum	enID, 
				 LPCTSTR lpcszValue)
			: m_enID(enID),
			  m_sValue(lpcszValue)
		{
		}		

		PROPERTY(SP::CDataFormatter::PropertiesEnum	enID, 
				 LPCTSTR lpcValue, UINT_PTR cchValue)
			: m_enID(enID),
			  m_sValue(lpcValue, cchValue)
		{
		}		


		SP::CDataFormatter::PropertiesEnum	m_enID;
		tstring								m_sValue;
	};

	typedef std::vector<PROPERTY> CPropertyVector;

// Construction & destruction
public:
	CCoreFormatter()
		: m_fState(0),
		  m_pDisplayFmt(NULL),
		  m_pEditingFmt(NULL)
	{
	}

	~CCoreFormatter()
	{
		if ( m_pDisplayFmt )
			m_pDisplayFmt->Release();

		if ( m_pEditingFmt )
			m_pEditingFmt->Release();
	}

// Helpers: Formatter configuration
private:
	static BOOL SetDefaultMask(SP::CDataFormatter* pcFormatter, DWORD fOptions);

	const SP::CDataFormatter* GetFmtForMode(FormatModeEnum enMode) const
	{
		switch( enMode )
		{
		case FM_DISPLAY:
			return m_pDisplayFmt;

		case FM_EDITING:
			return m_pEditingFmt;
		}

		return NULL;
	}

	SP::CDataFormatter* GetFmtForMode(FormatModeEnum enMode)
	{
		switch( enMode )
		{
		case FM_DISPLAY:
			return m_pDisplayFmt;

		case FM_EDITING:
			return m_pEditingFmt;
		}

		return NULL;
	}

	void SetUseCustomMaskFlag(FormatModeEnum enMode, BOOL bValue)
	{
		WORD wFlag;

		switch( enMode )
		{
		case FM_DISPLAY:
			wFlag = FS_USECUSTOMDISPLAYMASK;
			break;

		case FM_EDITING:
			wFlag = FS_USECUSTOMEDITINGMASK;
			break;

		default:
			return;
		}

		if ( bValue )
			m_fState |= wFlag;
		else
			m_fState &= (~wFlag);
	}

// Helpes: Value
private:
	void SetValueSize(size_t size)
	{
		ATLASSERT( size <= 0xff );

		m_fState &= (~SM_SIZE);
		m_fState |= WORD(size);
	}

	WORD GetState() const
	{
		return m_fState & SM_STATE;
	}

// Operations: Value
private:
	void UpdateValueTypeParams(SP::CDataFormatter::TypeEnum enType);

	static DWORD GetMaskCompositionOptions(FormatModeEnum enMode) 
	{
		switch ( enMode )
		{
		case FM_DISPLAY:
			return 0;

		case FM_EDITING:
			return SP::CDataFormatter::CMO_OPTFRACTIONPART|
				   SP::CDataFormatter::CMO_OPTEXPONENTPART;
		}

		return 0;
	}


// Utilities: Formatter configuration
private:
	static SP::CDataFormatter* CreateDisplayFormatter(
									SP::CDataFormatter::TypeEnum enType, DWORD fTraits,
									const PROPERTY* rgFmtProps = NULL,
									UINT_PTR cchFmtProps = 0,
									LPCTSTR lpcszMask = NULL);

	static SP::CDataFormatter* CreateEditingFormatter(
									SP::CDataFormatter::TypeEnum enType, DWORD fTraits,
									const PROPERTY* rgFmtProps = NULL,
									UINT_PTR cchFmtProps = 0,
									LPCTSTR lpcszMask = NULL);

// Utilities: Formatter configuration
public:
	static BOOL IsPropertyUsed(SP::CDataFormatter::TypeEnum enType, DWORD fTraits,
							   SP::CDataFormatter::PropertiesEnum enProperty);

// Operations: Formatter configuration
public:
	BOOL Configure(SP::CDataFormatter::TypeEnum enType, DWORD fFmtTraits,
				   const PROPERTY* rgDisplayFmtProps = NULL,
				   UINT_PTR cchDisplayFmtProps = 0,
				   LPCTSTR lpcszDisplayMask = NULL,
				   const PROPERTY* rgEditingFmtProps = NULL,
				   UINT_PTR cchEditingFmtProps = 0,
				   LPCTSTR lpcszEditingMask = NULL);

	void Unconfigure()
	{		
		ATLASSERT( m_pDisplayFmt && m_pEditingFmt);

		m_pDisplayFmt->Release();
		m_pDisplayFmt = NULL;

		m_pEditingFmt->Release();
		m_pEditingFmt = NULL;

		m_fState = 0;
	}

	BOOL IsConfigured() const
	{
		ATLASSERT( (m_pDisplayFmt && m_pEditingFmt) ||
				   (!m_pDisplayFmt && !m_pEditingFmt) );

		return m_pDisplayFmt != NULL;
	}

// Operations: Formatting
public:
	UINT_PTR FormatDisplayText(const void* lpcValue, LPTSTR lpBuffer, 
							   UINT_PTR cchBuffer) const
	{		
		ATLASSERT( !lpcValue || !::IsBadReadPtr(lpcValue, GetDataSize()) );
		ATLASSERT( !::IsBadWritePtr(lpBuffer, cchBuffer * sizeof(TCHAR)) );
		ATLASSERT( m_pDisplayFmt );

		return m_pDisplayFmt->Format(lpcValue, lpBuffer, cchBuffer);
	}

	UINT_PTR FormatEditingText(const void* lpcValue, LPTSTR lpBuffer, 
							   UINT_PTR cchBuffer) const
	{
		ATLASSERT( !lpcValue || !::IsBadReadPtr(lpcValue, GetDataSize()) );
		ATLASSERT( !::IsBadWritePtr(lpBuffer, cchBuffer * sizeof(TCHAR)) );
		ATLASSERT( m_pEditingFmt );

		return m_pEditingFmt->Format(lpcValue, lpBuffer, cchBuffer);
	}

	BOOL ScanEditingText(LPCTSTR lpszText, UINT_PTR cchText, void* lpValue, 
						 BOOL* pbIsValid, BOOL* pbIsNull) const
	{
		ATLASSERT( !::IsBadReadPtr(lpszText, cchText * sizeof(TCHAR)) );
		ATLASSERT( !::IsBadWritePtr(lpValue, GetDataSize()) );
		ATLASSERT( !::IsBadWritePtr(pbIsNull, sizeof(BOOL)) );
		ATLASSERT( !::IsBadWritePtr(pbIsValid, sizeof(BOOL)) );
		ATLASSERT( m_pEditingFmt );

		// Read value from text
		bool bIsNotNull;
		UINT_PTR unResult = m_pEditingFmt->Scan(lpszText, cchText, 
												lpValue, &bIsNotNull);
		ATLASSERT( unResult != SP::CDataFormatter::ERROR_FAIL );
		if ( unResult == SP::CDataFormatter::ERROR_FAIL )
		{
			
			SP::AccountError(SP::AE_LOG, IDS_ELS_SPCONVERSION, 0, 
							 IDS_ERCW_SCANNINGTEXT);
			return FALSE;
		}	

		*pbIsValid = unResult == SP::CDataFormatter::STATUS_TRUE;
		*pbIsNull = !bIsNotNull;

		return TRUE;
	}

// Accessors & mutators
public:
	SP::CDataFormatter::TypeEnum GetType() const
	{
		ATLASSERT( IsConfigured() );

		return m_pDisplayFmt->GetType();
	}

    DWORD GetTraits() const
	{
		ATLASSERT( IsConfigured() );

		return m_pDisplayFmt->GetTraits();
	}

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

		return SIZE_T(m_fState & SM_SIZE);
	}


	BOOL IsPropertyUserValue(FormatModeEnum enMode, 
							 SP::CDataFormatter::PropertiesEnum enID) const
	{
		ATLASSERT( IsConfigured() );
		ATLASSERT( GetFmtForMode(enMode) );
		
		return GetFmtForMode(enMode)->IsPropertyUserValue(enID);
	}

	UINT_PTR GetPropertyValue(FormatModeEnum enMode, 
							  SP::CDataFormatter::PropertiesEnum enID, 
							  LPTSTR lpBuf, UINT_PTR cchBuf) const
	{
		ATLASSERT( IsConfigured() );
		ATLASSERT( GetFmtForMode(enMode) );

		return GetFmtForMode(enMode)->GetProperty(enID, lpBuf, cchBuf);
	}

	BOOL SetPropertyValue(FormatModeEnum enMode, 
						  SP::CDataFormatter::PropertiesEnum enID, 
						  LPCTSTR lpcValue, UINT_PTR cchValue)
	{
		ATLASSERT( IsConfigured() );		
		ATLASSERT( GetFmtForMode(enMode) );

		return GetFmtForMode(enMode)->SetProperty(enID, lpcValue, cchValue);
	}

	void DiscardPropertyUserValue(FormatModeEnum enMode, 
								  SP::CDataFormatter::PropertiesEnum enID)
	{
		ATLASSERT( IsConfigured() );
		ATLASSERT( GetFmtForMode(enMode) );

		GetFmtForMode(enMode)->DiscardPropertyUserValue(enID);
	}


	BOOL IsMaskCustomized(FormatModeEnum enMode) const
	{
		ATLASSERT( IsConfigured() );
		
		switch( enMode )
		{
		case FM_DISPLAY:
			return m_fState & FS_USECUSTOMDISPLAYMASK;

		case FM_EDITING:
			return m_fState & FS_USECUSTOMEDITINGMASK;
		}

		return FALSE;
	}

	BOOL DiscardCustomizedMask(FormatModeEnum enMode)
	{
		ATLASSERT( IsConfigured() );

		BOOL bResult = SetDefaultMask(GetFmtForMode(enMode), 
									  GetMaskCompositionOptions(enMode));
		if ( bResult )
		{
			SetUseCustomMaskFlag(enMode, FALSE);
		}

		return bResult;
	}

	UINT_PTR GetMask(FormatModeEnum enMode, LPTSTR lpszMask, UINT_PTR cchMask) const
	{
		ATLASSERT( IsConfigured() );
		ATLASSERT( GetFmtForMode(enMode) );

		return GetFmtForMode(enMode)->GetMask(lpszMask, cchMask);
	}

// Data members
private:
	WORD						m_fState;	
	
	SP::CDataFormatter*			m_pDisplayFmt;
	SP::CDataFormatter*			m_pEditingFmt;
};

/////////////////////////////////////////////////////////////////////////////////////////
// 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() = 0;
};

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

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

// 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()
		: m_fVlaueStatus(VS_VALUENULL|VS_VALUEVALID),
		  m_lpszEditText(NULL),
		  m_hbrBkg(NULL),
		  m_pEventSink(NULL)
	{
		::ZeroMemory(m_rgValue, sizeof(Numeric));
	}

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

		free(m_lpszEditText);
	}

// Helpers
private:
	BOOL IsSameValue(const BYTE* lpcValue, WORD fValueStatus) const
	{
		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;
	}

	BOOL SetEditingText()
	{
		ATLASSERT( IsWindow() );

		if ( IsValueValid() )
		{
			UINT_PTR cchText = GetEditingText(NULL, 0);
			ATLASSERT( cchText != SP::CDataFormatter::ERROR_FAIL );
			if ( cchText == SP::CDataFormatter::ERROR_FAIL )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								IDS_ERCW_FORMATEDITINGTEXT);
				return FALSE;
			}

			BOOL bResult = ReallocEditTextBuf(cchText);
			ATLASSERT( bResult );
			if ( !bResult )
				return FALSE;

			cchText = GetEditingText(m_lpszEditText, cchText);
			ATLASSERT( cchText != SP::CDataFormatter::ERROR_FAIL );
			if ( cchText == SP::CDataFormatter::ERROR_FAIL )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
									IDS_ERCW_FORMATEDITINGTEXT);
				return FALSE;
			}

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

		// Set text in the control
		SetWindowText(m_lpszEditText);

		return TRUE;
	}

	BOOL SetDisplayText()
	{
		ATLASSERT( m_formatter.IsConfigured() );
		ATLASSERT( IsWindow() );

		if ( IsValueValid() )
		{
			UINT_PTR cchText = GetDisplayText(NULL, 0);
			ATLASSERT( cchText != SP::CDataFormatter::ERROR_FAIL );
			if ( cchText == SP::CDataFormatter::ERROR_FAIL )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_FORMATDISPLAYTEXT);
				return FALSE;
			}
			
			BOOL bResult = ReallocEditTextBuf(cchText);
			ATLASSERT( bResult );
			if ( !bResult )
				return FALSE;

			cchText = GetDisplayText(m_lpszEditText, cchText);
			ATLASSERT( cchText != SP::CDataFormatter::ERROR_FAIL );
			if ( cchText == SP::CDataFormatter::ERROR_FAIL )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_FORMATDISPLAYTEXT);
				return FALSE;
			}

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

			// Set text in the control
			SetWindowText(m_lpszEditText);
		}
		else
		{
			UINT_PTR cchText = GetDisplayText(NULL, 0);
			ATLASSERT( cchText != SP::CDataFormatter::ERROR_FAIL );
			if ( cchText == SP::CDataFormatter::ERROR_FAIL )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_FORMATDISPLAYTEXT);
				return FALSE;
			}

			LPTSTR lpszText = (LPTSTR)_alloca((cchText + 1) * sizeof(TCHAR));

			cchText = GetDisplayText(lpszText, cchText);
			ATLASSERT( cchText != SP::CDataFormatter::ERROR_FAIL );
			if ( cchText == SP::CDataFormatter::ERROR_FAIL )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_FORMATDISPLAYTEXT);
				return FALSE;
			}

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

			// Set text in the control
			SetWindowText(lpszText);
		}

		return TRUE;
	}

	BOOL SaveEditText() const
	{
		ATLASSERT( IsWindow() );

		// Save edit text
		int cchText = GetWindowTextLength();

		BOOL bResult = ReallocEditTextBuf(cchText);
		ATLASSERT( bResult );
		if ( !bResult )
			return FALSE;
		
		GetWindowText(m_lpszEditText, cchText + 1);

		return TRUE;
	}

	BOOL ScanValue() const
	{
		ATLASSERT( m_formatter.IsConfigured() );
		ATLASSERT( IsWindow() );

		const SIZE_T sizeValue = m_formatter.GetDataSize();

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

		BOOL bIsValid; 
		BOOL bIsNull;

		// Read value from control
		if ( !m_formatter.ScanEditingText(m_lpszEditText, _tcslen(m_lpszEditText), 
										  rgNewValue, &bIsValid, &bIsNull) )
		{			
			SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
							 IDS_ERCW_SCANEDITINGTEXT);
			return FALSE;
		}

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

		return TRUE;
	}

// Operations
public:
	BOOL BeginEdit()
	{
		ATLASSERT( IsWindow() );

		if ( m_formatter.IsConfigured() ) 
		{
			// Set editing text
			BOOL bResult = SetEditingText();
			ATLASSERT( bResult );
			if ( !bResult )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_SETEDITINGTEXT);

				return FALSE;
			}

			// Change state
			_SetEditing(TRUE);
		}

		// Update modified flag
		SendMessage(EM_SETMODIFY, _GetModify());

		return TRUE;
	}

	BOOL EndEdit()
	{	
		if ( m_formatter.IsConfigured() )
		{
			BOOL bResult;

			// Save text edit control	
			if ( _GetEditing() )
			{
				bResult = SaveEditText();
				ATLASSERT( bResult );

				// Change state
				_SetEditing(FALSE);

				if ( !bResult )
				{				
					SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
									 IDS_ERCW_SAVEEDITTEXT);

					return FALSE;
				}
			}

			// Get user value
			bResult = ScanValue();
			ATLASSERT( bResult );
			if ( !bResult )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_SCANEDITTEXT);

				return FALSE;
			}

			// Update display text
			bResult = SetDisplayText();
			ATLASSERT( bResult );
			if ( !bResult )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_SETDISPLAYTEXT);

				return FALSE;
			}
		}

		return TRUE;
	}

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

		m_pEventSink = pEventSink;
	}

	void UnadviseEventSink()
	{
		ATLASSERT( m_pEventSink );

		m_pEventSink = NULL;
	}

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

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

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

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

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

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

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

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

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

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

			m_hbrBkg = ::CreateSolidBrush(_GetBackColor());

			InvalidateRect(NULL);
		}
	}

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

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

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

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

			m_hbrBkg = ::CreateSolidBrush(_GetReadOnlyBackColor());

			InvalidateRect(NULL);
		}
	}

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

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

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

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

			InvalidateRect(NULL);
		}
	}

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

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

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

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

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

		if ( IsWindow() && !_GetEditing() && !IsValueValid() )
		{
			SetDisplayText();
		}
	}

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

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

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

			BOOL bResult;

			if ( _GetEditing() )
				bResult = SetEditingText();
			else
				bResult = SetDisplayText();

			ATLASSERT( bResult );
			if ( !bResult && m_pEventSink )
			{
				const UINT nErrID = _GetEditing() ? IDS_ERCW_SETEDITINGTEXT 
												  : IDS_ERCW_SETDISPLAYTEXT;

				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, nErrID);				
				m_pEventSink->_OnError();
			}
		}
	}

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

	}

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

		// Update current text
		if ( IsWindow() )
		{
			BOOL bResult;

			if ( _GetEditing() )
				bResult = SetEditingText();
			else
				bResult = SetDisplayText();

			ATLASSERT( bResult );
			if ( !bResult && m_pEventSink )
			{
				const UINT nErrID = _GetEditing() ? IDS_ERCW_SETEDITINGTEXT 
												  : IDS_ERCW_SETDISPLAYTEXT;

				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, nErrID);				
				m_pEventSink->_OnError();
			}
		}
	}

// Operations: Window
public:
	void DefineWindowStyles(DWORD* pdwStyle, DWORD* pdwStyleEx, DWORD fProperties) const
	{
		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() )
			{
				switch ( _GetAppearance() )
				{
				case acFlat:
					*pdwStyle |= WS_BORDER;
					break;

				case ac3D:
					*pdwStyleEx |= WS_EX_CLIENTEDGE;
					break;

				case acSoft:
					*pdwStyleEx |= WS_EX_STATICEDGE;
					break;
				}
			}
		}

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

			*pdwStyle &= (~fAlignmentMask);

			switch ( _GetAlignment() )
			{
			case alGeneral:
				*pdwStyle |= ES_RIGHT;
				break;

			case alLeft:
				*pdwStyle |= ES_LEFT;
				break;

			case alCenter:
				*pdwStyle |= ES_CENTER;
				break;

			case alRight:
				*pdwStyle |= ES_RIGHT;
				break;
			}
		}

		// 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)
	{
		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()
	{
		ATLASSERT( IsWindow() );

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

		if ( _GetRefFont() )
		{
			hFont = _GetFontHandle();
			if ( hFont )
				SetFont(hFont, FALSE);
		}
	}

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

		if ( m_hbrBkg )
			::DeleteObject(m_hbrBkg);

		if ( bReadOnly )
			m_hbrBkg = ::CreateSolidBrush(_GetReadOnlyBackColor());
		else
			m_hbrBkg = ::CreateSolidBrush(_GetBackColor());

		SendMessage(EM_SETREADONLY, bReadOnly);
	}

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

		::SetTextColor(hdc, _GetForeColor());
		::SetBkColor(hdc, _GetBackColor());

		*phbrBkg = m_hbrBkg;

		return TRUE;
	}

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

		::SetTextColor(hdc, _GetReadOnlyForeColor());
		::SetBkColor(hdc, _GetReadOnlyBackColor());

		*phbrBkg = m_hbrBkg;

		return TRUE;
	}

// Accessors & mutators
public:
	// Formatter
	const TFormatter& GetFormatter() const
	{
		return m_formatter;
	}

	TFormatter& GetFormatter()
	{
		return m_formatter;
	}

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

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

	const void* GetValue() const
	{
		ATLASSERT( m_formatter.IsConfigured() );

		if ( _GetEditing() )
		{
			// Save text edit control	
			BOOL bResult = SaveEditText();
			ATLASSERT( bResult );
			if ( !bResult )
			{				
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
									IDS_ERCW_SAVEEDITTEXT);

				return FALSE;
			}

			// Get user value
			bResult = ScanValue();
			ATLASSERT( bResult );
			if ( !bResult )
			{
				SP::AccountError(SP::AE_LOG, IDS_ELS_SPNUMERICEDITBOX, 0, 
								 IDS_ERCW_SCANEDITTEXT);

				return FALSE;
			}
		}

		return IsValueNull() ? NULL : m_rgValue;
	}

	void SetValue(const void* lpcValue, BOOL bValid)
	{
		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);
		}
	}

	// Edit text
	LPCTSTR GetEditText() const
	{
		return m_lpszEditText;
	}

	LPTSTR GetEditText()
	{
		return m_lpszEditText;
	}

	BOOL ReallocEditTextBuf(UINT_PTR cchBuf) const
	{
		void* lpBuf = realloc(m_lpszEditText, (cchBuf + 1) * sizeof(TCHAR));
		ATLASSERT( lpBuf );
		if ( !lpBuf )
		{
			SP::AccountError(SP::AE_LOG|SP::AE_SETMSG, IDS_ELS_CRT, 2, IDS_ELR_OUTOFMEMORY);
			return FALSE;
		}

		m_lpszEditText = reinterpret_cast<LPTSTR>(lpBuf);

		return TRUE;
	}

	BOOL SetEditText(LPCTSTR lpszEditText)
	{
		ATLASSERT( lpszEditText );

		return SetEditText(lpszEditText, _tcslen(lpszEditText));
	}

	BOOL SetEditText(LPCTSTR lpcEditText, UINT_PTR cchText)
	{
		bool bResult = ReallocEditTextBuf(cchText);
		ATLASSERT( bResult );
		if ( !bResult )
			return FALSE;

		_tcsncpy(m_lpszEditText, lpcEditText, cchText);

		return TRUE;
	}

	UINT_PTR GetDisplayText(LPTSTR lpszText, UINT_PTR cchText) const
	{
		ATLASSERT( !::IsBadWritePtr(lpszText, cchText * sizeof(TCHAR)) );
		ATLASSERT( m_formatter.IsConfigured() );

		if ( IsValueValid() )
		{
			return m_formatter.FormatDisplayText(GetValue(), lpszText, cchText);
		}
		else if ( m_lpszEditText )
		{

			LPCTSTR lpcszErrorSymbol = _GetErrorSymbol();
			ATLASSERT( lpcszErrorSymbol );
			UINT_PTR cchErrorSymbol = _tcslen(lpcszErrorSymbol);

			UINT_PTR cchResult = _tcslen(m_lpszEditText) * cchErrorSymbol;
			if ( cchResult > 64 )
				cchResult = 64;

			if ( lpszText )
			{
				UINT_PTR iSrcErrorSymbol = 0;

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

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

				return i;
			}
			else
			{
				return cchResult;
			}

		}

		return 0;
	}

	UINT_PTR GetEditingText(LPTSTR lpszText, UINT_PTR cchText) const
	{
		ATLASSERT( !::IsBadWritePtr(lpszText, cchText * sizeof(TCHAR)) );
		ATLASSERT( m_formatter.IsConfigured() );

		if ( IsValueValid() )
		{
			return m_formatter.FormatEditingText(GetValue(), lpszText, cchText);
		}
		else if ( m_lpszEditText )
		{
			if ( lpszText )
			{
				for ( UINT_PTR i = 0; i < cchText && m_lpszEditText[i]; i++ )
					lpszText[i] = m_lpszEditText[i];

				return i;
			}
			else
			{
				return _tcslen(m_lpszEditText);
			}
		}		

		return 0;
	}

// Data members
private:
	TFormatter					m_formatter;

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

	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