Click here to Skip to main content
15,886,518 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 254.8K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
//==========================================================================
// 							Class Implementation : 
//					COXEdit & COXNumericEdit & COXCurrencyEdit
//==========================================================================

// Implementation file : OXEdit.cpp

// Version: 9.3

// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.

////////////////
// COXEdit implements a edit control which could be set to use
// user-defined color and font to draw text. Also you can show tooltip
//

#include "StdAfx.h"
#include "OXEdit.h"

#include <math.h> 
#include <stdlib.h>

#pragma warning (push, 3)
#include <iomanip>
#include <sstream>
#pragma warning (pop)

#include "UTBStrOp.h"
#include "UTB64Bit.h"


using namespace std;

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


#if _MFC_VER<=0x0421

static void	RemoveCharFromString(CString& sText, TCHAR chToRemove)
{
	int nPos=sText.Find(chToRemove);
	while(nPos!=-1)
	{
		sText=sText.Left(nPos)+sText.Mid(nPos+1);
		nPos=sText.Find(chToRemove);
	}
}


static void	ReplaceCharInString(CString& sText, TCHAR chToBeReplaced, TCHAR chToReplaceTo)
{
	CString sCopy=sText;
	CString sToReplaceTo(chToReplaceTo);
	int nPos=sCopy.Find(chToBeReplaced);
	sText.Empty();
	while(nPos!=-1)
	{
		sText=sText+sCopy.Left(nPos)+sToReplaceTo;
		sCopy=sCopy.Mid(nPos+1);
		nPos=sCopy.Find(chToBeReplaced);
	}
	sText+=sCopy;
}

#endif


//////////////////////////////////////////////////	
//////////////////////////////////////////////////	
	
IMPLEMENT_DYNCREATE(COXEdit, CEdit)

BEGIN_MESSAGE_MAP(COXEdit, CEdit)
	//{{AFX_MSG_MAP(COXEdit)
	ON_WM_CTLCOLOR_REFLECT()
	ON_WM_SIZE()
	ON_WM_DESTROY()
	ON_WM_KEYDOWN()
	ON_WM_CHAR()
	ON_WM_SETFOCUS()
	ON_WM_LBUTTONDOWN()
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_CUT,OnCut)
	ON_MESSAGE(WM_COPY,OnCopy)
	ON_MESSAGE(WM_PASTE,OnPaste)
	ON_MESSAGE(WM_CLEAR,OnClear)
	ON_MESSAGE(WM_SETTEXT,OnSetText)
END_MESSAGE_MAP()

///////////////////
// Constructor
//
// --- In  :
// --- Out : 
// --- Returns :
// --- Effect : Constructs the object
COXEdit::COXEdit()
{
	m_clrText=::GetSysColor(COLOR_WINDOWTEXT); 
	m_clrBack=::GetSysColor(COLOR_WINDOW); 
	m_clrReadOnlyBack=::GetSysColor(COLOR_BTNFACE);
	m_clrReadOnlyFont=::GetSysColor(COLOR_GRAYTEXT);

	m_bInsertMode=TRUE;

	TCHAR chMask[2];
	chMask[0]=OXEDITABLETEXT_SYMBOL;
	chMask[1]=_T('\0');
	VERIFY(SetMask(chMask));

	m_bInitialized=FALSE;

	m_nSetTextSemaphor=0;
	m_bNotifyParent=TRUE;

	m_bHighlightOnSetFocus=FALSE;

	m_bNoSaveFont=FALSE;
}

void COXEdit::SetTextColor(COLORREF clrText, BOOL bRedraw/*=TRUE*/)
{ 
	if(m_clrText!=clrText)
	{
		m_clrText=clrText; 
		if(bRedraw)
			RedrawWindow();
	}
}

void COXEdit::SetBkColor(COLORREF clrBack, BOOL bRedraw/*=TRUE*/)
{ 
	if(m_clrBack!=clrBack)
	{
		m_clrBack=clrBack; 
		if(bRedraw)
			RedrawWindow();
	}
}

BOOL COXEdit::SetToolTipText(LPCTSTR sText)
{ 
	if(::IsWindow(m_ttc.GetSafeHwnd()))
	{
		m_sToolTipText=sText; 
		m_ttc.UpdateTipText(m_sToolTipText, this, ID_OXEDIT_TOOLTIP);

		return TRUE;
	}

	return FALSE;
}

//////////////////
// Handle reflected WM_CTLCOLOR to set custom control color and font.
HBRUSH COXEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
	UNREFERENCED_PARAMETER(nCtlColor);

	COLORREF clrText=GetTextColor();
	COLORREF clrBk=GetBkColor();
	if(!IsWindowEnabled())
	{
		clrText=::GetSysColor(COLOR_GRAYTEXT);
		clrBk=::GetSysColor(COLOR_BTNFACE);
	}
	else if(GetStyle()&ES_READONLY)
	{
		clrText=m_clrReadOnlyFont;
		clrBk=m_clrReadOnlyBack;
	}
	pDC->SetTextColor(clrText);
	pDC->SetBkColor(clrBk);

	if((HFONT)m_font!=NULL)
	{
		pDC->SelectObject(&m_font);
	}

	static CBrush brush;
	if((HBRUSH)brush!=NULL)
		brush.DeleteObject();
	brush.CreateSolidBrush(clrBk);

	return (HBRUSH)brush;
}

// handle it to pass a mouse message to a tool tip control for processing 
BOOL COXEdit::PreTranslateMessage(MSG* pMsg)
{
	if(::IsWindow(m_ttc.GetSafeHwnd()))
	{
		m_ttc.Activate(TRUE); 
		m_ttc.RelayEvent(pMsg);
	}
	return CEdit::PreTranslateMessage(pMsg);
}

// handle it to initialize tooltip control and save the original size 
// of edit control window
void COXEdit::PreSubclassWindow()
{
	_AFX_THREAD_STATE* pThreadState=AfxGetThreadState();
	// hook not already in progress
	if(pThreadState->m_pWndInit==NULL && !m_bInitialized)
	{
		if(!InitControl())
			TRACE(_T("COXEdit::PreSubclassWindow: failed to initialize edit control\n"));
	}

	CEdit::PreSubclassWindow();
}

// handle it to update original size of window and if m_bFitToText is TRUE then
// resize it to fit to its text
void COXEdit::OnSize(UINT nType, int cx, int cy)
{
	CEdit::OnSize(nType, cx, cy);
	
	// update the original size of the edit control window
	CRect rect;
	GetWindowRect(rect);
	CWnd* pParent=GetParent();
	if(pParent!=NULL)
	{
		pParent->ScreenToClient(rect);
	}

	// Change information about window's rect in tooltip control
	if(::IsWindow(m_ttc.GetSafeHwnd()))
	{
		CToolInfo toolInfo;
		if(m_ttc.GetToolInfo(toolInfo,this,ID_OXEDIT_TOOLTIP))
		{
			toolInfo.rect.left=0;
			toolInfo.rect.top=0;
			toolInfo.rect.right=rect.Width();
			toolInfo.rect.bottom=rect.Height();
			UTBStr::tcscpy(toolInfo.szText, _tcsclen(m_sToolTipText) + 1, m_sToolTipText);
			m_ttc.SetToolInfo(&toolInfo);
		}
	}
}

BOOL COXEdit::InitControl()
{
	ASSERT(::IsWindow(GetSafeHwnd()));

	GetLocaleSettings();

	// Create the tooltip
	CRect rect; 
	GetClientRect(rect);
	m_ttc.Create(this);
	m_ttc.AddTool(this, m_sToolTipText, rect, ID_OXEDIT_TOOLTIP);

	// save font
	VERIFY(SaveFont());

	CString sText;
	GetWindowText(sText);
	EmptyData();
	SetInputData(sText);

	m_bInitialized=TRUE;

	return TRUE;
}

BOOL COXEdit::Create(DWORD dwStyle, const RECT& rect, 
					 CWnd* pParentWnd, UINT nID)
{
	BOOL bResult=CEdit::Create(dwStyle, rect, pParentWnd, nID);
	if(bResult)
	{
		// Initialize edit control
		if(!InitControl())
		{
			TRACE(_T("COXEdit::Create: failed to initialize edit control"));
			return FALSE;
		}
	}

	return bResult;
}

void COXEdit::OnDestroy()
{
	if(::IsWindow(m_ttc.GetSafeHwnd()))
	{
		m_ttc.DelTool(this, ID_OXEDIT_TOOLTIP);
		m_ttc.DestroyWindow();
	}

	CEdit::OnDestroy();
}


BOOL COXEdit::SaveFont()
{
	LOGFONT lf;
	CFont* pFont=GetFont();
	if(pFont!=NULL)
	{
		if(!pFont->GetLogFont(&lf))
			return FALSE;
	}
	
	if((HFONT)m_font!=NULL)
		m_font.DeleteObject();
	if(pFont!=NULL)
	{
		if(!m_font.CreateFontIndirect(&lf))
			return FALSE;
	}

	return TRUE;
}


LRESULT COXEdit::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// TODO: Add your specialized code here and/or call the base class
#if defined (_WINDLL)
#if defined (_AFXDLL)
	AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif

	LRESULT lResult=CEdit::WindowProc(message, wParam, lParam);

	switch(message)
	{
	case WM_SETFONT:
		{
			if(!m_bNoSaveFont)
			{
				VERIFY(SaveFont());
			}
			break;
		}

	case WM_SETTINGCHANGE:
		{
			CString sInputData=GetInputData();
			GetLocaleSettings();
			EmptyData(TRUE);
			VERIFY(SetInputData(sInputData));
			break;
		}
	}

	return lResult;
}


void COXEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// Keep the OnKeyDown processing to a minimum.  This is because the edit 
	// control does lots of processing before OnChar() is sent and we want 
	// to let it continue. 

	if((GetStyle()&ES_READONLY)==ES_READONLY)
	{
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
		return;
	}

	BOOL bIsShiftKeyDown=::GetAsyncKeyState(VK_SHIFT)<0;
	BOOL bIsCtrlKeyDown=::GetAsyncKeyState(VK_CONTROL)<0;
	if(nChar==VK_DELETE)
	{
		if (bIsShiftKeyDown)//cut operation
		{
			CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
			return;
		}
		int nSelectionStart=0;
		int nSelectionEnd=0;
		GetSel(nSelectionStart, nSelectionEnd);
		// Delete has two functions, it can delete the selection and
		// it can delete characters to the right.
		if(nSelectionStart==nSelectionEnd)
		{
			nSelectionEnd++; // Do the equivalent of a selection.
			if(DeleteRange(nSelectionStart, nSelectionEnd))
			{
				Update(nSelectionStart);
			}
			else	// Must be on a literal, so continue moving to right
					// and re-attempting the delete until we either delete
					// a character or run out of characters.
			{
				while(nSelectionEnd<=
					m_arrLeftLiterals.GetSize()+m_arrInputData.GetSize())
				{
					nSelectionStart++;
					nSelectionEnd++; // Do the equivalent of a selection.
					if(DeleteRange(nSelectionStart, nSelectionEnd))
					{
						Update(nSelectionStart);
						break;
					}
				}
			}
		}
		else if(DeleteRange(nSelectionStart, nSelectionEnd))
		{
			Update(nSelectionStart);
		}
		else	// Must be on a literal, so continue moving to right
				// and reattempting the delete until we either delete
				// a character or run out of characters.
		{
			while(nSelectionEnd<=
					m_arrLeftLiterals.GetSize()+m_arrInputData.GetSize())
			{
				nSelectionStart++;
				nSelectionEnd++; // Do the equivalent of a selection.
				if(DeleteRange(nSelectionStart, nSelectionEnd))
				{
					Update(nSelectionStart);
					break;
				}
			}
		}
	}
	else if(nChar==VK_HOME)
	{
		// If the shift key is not down, then HOME is a navigation and we need to 
		// move the insertion point to the first available position. 
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
		if(!bIsShiftKeyDown)
		{
			int nSelectionStart=0;
			int nSelectionEnd=0;
			GetSel(nSelectionStart, nSelectionEnd);
			if(!IsInputPosition(nSelectionStart))
				UpdateInsertionPointForward(nSelectionStart);
		}
	}
	else if(nChar==VK_END)
	{
		// If the shift key is not down, then HOME is a navigation and we need to 
		// move the insertion point to the first available position. 
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
		if(!bIsShiftKeyDown)
		{
			int nSelectionStart=0;
			int nSelectionEnd=0;
			GetSel(nSelectionStart, nSelectionEnd);
			if(!IsInputPosition(nSelectionStart))
				UpdateInsertionPointBackward(nSelectionStart);
		}
	}
	else if(nChar==VK_LEFT)
	{
		// If the shift key is not down, then LEFT is a navigation and we need to 
		// move the insertion point to the previous available position. 
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
		if(!bIsShiftKeyDown)
		{
			int nSelectionStart=0;
			int nSelectionEnd=0;
			GetSel(nSelectionStart, nSelectionEnd);
			UpdateInsertionPointBackward(nSelectionStart);
		}
	}
	else if(nChar==VK_UP)
	{
		// If the shift key is not down, then UP is a navigation and we need to 
		// move the insertion point to the previous available position. 
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
		if(!bIsShiftKeyDown)
		{
			int nSelectionStart=0;
			int nSelectionEnd=0;
			GetSel(nSelectionStart, nSelectionEnd);
			UpdateInsertionPointBackward(nSelectionStart);
		}
	}
	else if(nChar==VK_RIGHT)
	{
		// If the shift key is not down, then RIGHT is a navigation and we need to 
		// move the insertion point to the next available position. 
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
		if(!bIsShiftKeyDown)
		{
			int nSelectionStart=0;
			int nSelectionEnd=0;
			GetSel(nSelectionStart, nSelectionEnd);
			UpdateInsertionPointForward(nSelectionStart);
		}
	}
	else if(nChar==VK_DOWN)
	{
		// If the shift key is not down, then DOWN is a navigation and we need to 
		// move the insertion point to the next available position. 
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
		if(!bIsShiftKeyDown)
		{
			int nSelectionStart=0;
			int nSelectionEnd=0;
			GetSel(nSelectionStart, nSelectionEnd);
			UpdateInsertionPointForward(nSelectionStart);
		}
	}
	else if(nChar==VK_INSERT)
	{

		if (!bIsShiftKeyDown && !bIsCtrlKeyDown)
		{
			// The standard CEdit control does not support over-typing. 
			// This flag is used to manage over-typing internally. 
			SetInsertMode(!GetInsertMode());
		}
		else
			CEdit::OnKeyDown(nChar, nRepCnt, nFlags);//copy or paste
	}
	else
	{
		CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
	}
}


void COXEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if((GetStyle()&ES_READONLY)==ES_READONLY)
	{
		CEdit::OnChar(nChar, nRepCnt, nFlags);
		return;
	}

	int nSelectionStart=0;
	int nSelectionEnd=0;
	GetSel(nSelectionStart, nSelectionEnd);

	BOOL bAcceptReturnKey=(GetStyle()&ES_MULTILINE) && (GetStyle()&ES_WANTRETURN);
	if(nChar==10)
	{
		nChar=VK_RETURN;
		bAcceptReturnKey=(GetStyle()&ES_MULTILINE);
	}

	// If character value is above 32, then it is ANSI or Extended. 
	// Below 32 are control and navigation characters. 
	if(nChar>=32 || (nChar==VK_RETURN && bAcceptReturnKey))
	{
		if(nSelectionStart==nSelectionEnd)
		{
			if(GetStyle()&ES_NUMBER && nChar!=VK_RETURN && 
				(nChar<_T('0') || nChar>_T('9')))
			{
				CEdit::OnChar(nChar, nRepCnt, nFlags);
				return;
			}

			if(IsInputPosition(nSelectionStart))
			{
				int nActualInsertionPoint=nSelectionStart;
				if(m_bInsertMode)
				{
					if(nChar==VK_RETURN)
					{
						// InsertAt will return -1 if the character cannot be inserted here. 
						nActualInsertionPoint=InsertAt(nSelectionStart,_T('\r'));
						if(nActualInsertionPoint>=0)
						{
							nSelectionStart=nActualInsertionPoint+1;
							nActualInsertionPoint=InsertAt(nSelectionStart,_T('\n'));
						}
					}
					else
					{
						nActualInsertionPoint=InsertAt(nSelectionStart,(TCHAR)nChar);
					}
				}
				else
				{
					if(nChar==VK_RETURN)
					{
						nActualInsertionPoint=SetAt(nSelectionStart,_T('\r'));
						// SetAt will return -1 if the character cannot be set here. 
						if(nActualInsertionPoint>=0)
						{
							nSelectionStart=nActualInsertionPoint+1;
							nActualInsertionPoint=SetAt(nSelectionStart,_T('\n'));
						}
					}
					else
					{
						nActualInsertionPoint=SetAt(nSelectionStart,(TCHAR)nChar);
					}
				}
				
				// InsertAt will return -1 if the character cannot be inserted here. 
				if(nActualInsertionPoint>=0)
				{
					nSelectionStart=nActualInsertionPoint+1;
				}
				else
				{
					ValidationError();
				}
				
				Update(nSelectionStart);
			}
			else
			{
				// Beep if trying to type over a literal. 
				ValidationError();
				UpdateInsertionPointForward(nSelectionStart);
			}
		}
		else
		{
			// First delete the remaining selection. 
			// The function will return a valid count if 
			// some input characters were deleted. We use 
			// this value to determine if it makes sense to insert. 
			if(DeleteRange(nSelectionStart,nSelectionEnd))
			{
				// InsertAt will place the character at the next available position, 
				// then return that positition
				int nActualInsertionPoint=nSelectionStart;
				if(nChar==VK_RETURN)
				{
					// InsertAt will return -1 if the character cannot be inserted here. 
					nActualInsertionPoint=InsertAt(nSelectionStart,_T('\r'));
					if(nActualInsertionPoint>=0)
					{
						nSelectionStart=nActualInsertionPoint+1;
						nActualInsertionPoint=InsertAt(nSelectionStart,_T('\n'));
					}
				}
				else
				{
					nActualInsertionPoint=InsertAt(nSelectionStart,(TCHAR)nChar);
				}
				
				// InsertAt will return -1 if the character cannot be inserted here. 
				if(nActualInsertionPoint>=0)
				{
					nSelectionStart=nActualInsertionPoint+1;
				}
				else
				{
					ValidationError();
				}
				
				Update(nSelectionStart);
			}
			else  // Must be on a literal, so beep and move to a valid location. 
			{
				ValidationError();
				UpdateInsertionPointForward(nSelectionStart);
			}
		}
	}
	else
	{
		if(nChar==VK_BACK)
		{
			// Backspace performs two functions, if there is a selection,
			// then the backspace is the same as deleting the selection.
			// If there is no selection, then the backspace deletes the
			// first non-literal character to the left.
			if(nSelectionStart==nSelectionEnd)
			{
				while( nSelectionStart > 0 && nSelectionStart == nSelectionEnd )
				{
					nSelectionStart--; // Do the equivalent of a backspace.

					if(DeleteRange(nSelectionStart,nSelectionEnd))
					{
						Update(nSelectionStart);
						break;
					}

					nSelectionEnd--;
				}
			}
			else if(DeleteRange(nSelectionStart,nSelectionEnd))
			{
				Update(nSelectionStart);
			}
			else	// Must be on a literal, so continue moving to left
					// and re-attempting the delete until we either delete
					// a character or run out of characters.
			{
				if(nSelectionStart>=1)
				{
					// v9.3 update 01 change - user mina 
					// while(nSelectionStart>0)
					while(nSelectionStart>0 && nSelectionStart <= nSelectionEnd )
					{
						nSelectionStart--; // Do the equivalent of a backspace.

						if(DeleteRange(nSelectionStart,nSelectionEnd))
						{
							Update(nSelectionStart);
							break;
						}
						
						nSelectionEnd--;
					}
				}
			}
		}
		else
		{
			// let edit control to do its job 
			CEdit::OnChar(nChar, nRepCnt, nFlags);
		}
	}
}

void COXEdit::OnSetFocus(CWnd* pOldWnd)
{
	CEdit::OnSetFocus(pOldWnd);

	if(!GetHighlightOnSetFocus())
	{
		// The default behavior is to highlight the entire string. 
		// If this is the case, then move the insertion to the first input position. 
		int nSelectionStart=0;
		int nSelectionEnd=0;
		GetSel(nSelectionStart, nSelectionEnd);
		if((nSelectionStart==0) && (nSelectionEnd==GetWindowTextLength()))
		{
			// Only update the insertion point if the entire string is selected. 
			// This will allow the mouse to be used to set the cursor without 
			// our interfering. 
			UpdateInsertionPointForward(0);
		}
	}

	m_bNoSaveFont=TRUE;
	SetFont(&m_font);
	m_bNoSaveFont=FALSE;
}


LONG COXEdit::OnCut(UINT wParam, LONG lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	int nSelectionStart=0;
	int nSelectionEnd=0;
	GetSel(nSelectionStart,nSelectionEnd);

	// Before updating, let the control do its normal thing. 
	// This will save us the effort of filling the clipboard. 
	CEdit::Default();

	// First do our version of the cut. 
	int nDeleteCount=DeleteRange(nSelectionStart,nSelectionEnd);

	// Now we update with our standard mask. 
	Update(nSelectionStart);
	if(nDeleteCount==0)
	{
		// I don't think we want to beep if no input characters were cut. 
		//ValidationError();
	}

	return 0;
}


LONG COXEdit::OnCopy(UINT wParam, LONG lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	// Just let copy do its thing and copy the selected text. 
	CEdit::Default();

	return 0;
}


LONG COXEdit::OnPaste(UINT wParam, LONG lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	int nSelectionStart=0;
	int nSelectionEnd=0;
	GetSel(nSelectionStart, nSelectionEnd);

	CEdit::Default();

	// This is a real dump paste routine that expects SetInputData do 
	// do the filtering.  There is probably no easy solution to this 
	// task because anything can be pasted.  We could try and match 
	// the literals, but maybe we will get to that later. 
	CString sNewString;
	GetWindowText(sNewString);

	if(nSelectionStart!=nSelectionEnd)
	{
		CString sLeftLiterals=GetLeftLiterals();
		CString sRightLiterals=GetRightLiterals();

		if(sNewString.Find(sLeftLiterals)==0)
			sNewString=sNewString.Mid(sLeftLiterals.GetLength());
		int nPosition=sNewString.Find(sRightLiterals);
		if(nPosition!=-1 && 
			nPosition+sRightLiterals.GetLength()==sNewString.GetLength())
			sNewString=sNewString.Left(nPosition);
	}

	EmptyData(TRUE);
	SetInputData(sNewString,0);
	// Setting the insertion point after a paste is tricky because the 
	// expected location is after the last valid pasted character. 
	// Try and determine this location by setting the insertion point 
	// to the first empty location after the specified starting point. 
	int nNewInsertionPoint=GetNextInputLocation(nSelectionStart);
	SetSel(nNewInsertionPoint,nNewInsertionPoint);

	return 0;
}


LONG COXEdit::OnClear(UINT wParam, LONG lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	TRACE(_T("COXEdit::OnClear\n"));

	int nSelectionStart=0;
	int nSelectionEnd=0;
	GetSel(nSelectionStart,nSelectionEnd);

	// Before updating, let the control do its normal thing. 
	CEdit::Default();

	// First do our version of the cut. 
	int nDeleteCount=DeleteRange(nSelectionStart,nSelectionEnd);
	UNREFERENCED_PARAMETER(nDeleteCount);

	// Now we update with our standard mask. 
	Update(nSelectionStart);

	return 0;
}


void COXEdit::OnLButtonDown(UINT nFlags, CPoint point)
{
	CEdit::OnLButtonDown(nFlags,point);

	int nSelectionStart=0;
	int nSelectionEnd=0;
	GetSel(nSelectionStart,nSelectionEnd);
	if(nSelectionStart==nSelectionEnd && !IsInputPosition(nSelectionStart))
		UpdateInsertionPointForward(0);
}


LRESULT COXEdit::OnSetText(UINT wParam, LONG lParam)
{
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	if(m_nSetTextSemaphor>0)
	{
		LRESULT result=CEdit::Default();
		NotifyParent(EN_UPDATE);
		if(m_bNotifyParent)
			NotifyParent(EN_CHANGE);
		return result;
	}
	else
	{
		ASSERT(m_nSetTextSemaphor==0);
		CString sText=(LPCTSTR)(INT_PTR)lParam;
	//	EmptyData(TRUE);
		m_arrInputData.RemoveAll();
		m_bNotifyParent=FALSE;
		SetInputData(sText,0);
		m_bNotifyParent=TRUE;
		return TRUE;
	}
}

BOOL COXEdit::SetMask(LPCTSTR lpszMask)
{
	CString sMask=lpszMask;
	if(sMask.IsEmpty() || sMask.Find(OXEDITABLETEXT_SYMBOL)==-1)
		return FALSE;

	DeleteContents();

	BOOL bLeftLiteral=TRUE;
	for(int nIndex=0; nIndex<sMask.GetLength(); nIndex++)
	{
		BOOL bSaveLiteral=TRUE;
		TCHAR chSymbol=sMask[nIndex];
		TCHAR chSpecialSymbol=OXEDITABLETEXT_SYMBOL;
		if(chSymbol==chSpecialSymbol && (nIndex==sMask.GetLength()-1 || 
			sMask[nIndex+1]!=chSpecialSymbol))
		{
			bSaveLiteral=FALSE;
			bLeftLiteral=FALSE;
		}
		else if(chSymbol==chSpecialSymbol && nIndex<sMask.GetLength()-1 && 
			sMask[nIndex+1]==chSpecialSymbol)
		{
			nIndex++;
		}

		if(bSaveLiteral)
		{
			if(bLeftLiteral)
				m_arrLeftLiterals.Add(chSymbol);
			else
				m_arrRightLiterals.Add(chSymbol);
		}
	}

	if(bLeftLiteral)
	{
		VERIFY(SetMask(GetMask()));
		return FALSE;
	}

	m_sMask=sMask;
//	UpdateMask();

	//ValidateInput();
	Validate();

	Update();

	return TRUE;
}


void COXEdit::DeleteContents()
{
	m_arrLeftLiterals.RemoveAll();
	m_arrRightLiterals.RemoveAll();
	m_arrInputData.RemoveAll();
}


CString COXEdit::GetInputData()const
{
	CString sInputData;
	for(int nIndex=0; nIndex<m_arrInputData.GetSize(); nIndex++)
	{
		sInputData+=m_arrInputData[nIndex];
	}
	return sInputData;
}


BOOL COXEdit::SetInputData(LPCTSTR pszInputData, int nBeginPos/*=0*/)
{
	if(nBeginPos<0 || nBeginPos>m_arrInputData.GetSize())
		return FALSE;

	CString sInputData=pszInputData;
	for(int nIndex=0; nIndex<sInputData.GetLength(); nIndex++)
	{
		if(GetInsertMode())
			m_arrInputData.InsertAt(nBeginPos+nIndex,sInputData[nIndex]);
		else
			m_arrInputData.SetAtGrow(nBeginPos+nIndex,sInputData[nIndex]);
	}
	
//	ValidateInput();
	Validate();

	Update(-1);

	return TRUE;
}


void COXEdit::EmptyData(BOOL bOnlyInput/*=FALSE*/)
{
	if(bOnlyInput)
		m_arrInputData.RemoveAll();
	else
		DeleteContents();
	
	Update();
}

BOOL COXEdit::GetInsertMode() const
{
	// The standard CEdit control does not support over-typing. 
	// This flag is used to manage over-typing internally. 
	return m_bInsertMode;
}

void COXEdit::SetInsertMode(BOOL bInsertMode)
{
	// The standard CEdit control does not support over-typing. 
	// This flag is used to manage over-typing internally. 
	m_bInsertMode=bInsertMode;
}


CString COXEdit::GetFormattedText() const
{
	CString sFormattedText;
	int nIndex=0;
	for(nIndex=0; nIndex<m_arrLeftLiterals.GetSize(); nIndex++)
		sFormattedText+=m_arrLeftLiterals[nIndex];
	for(nIndex=0; nIndex<m_arrInputData.GetSize(); nIndex++)
		sFormattedText+=m_arrInputData[nIndex];
	for(nIndex=0; nIndex<m_arrRightLiterals.GetSize(); nIndex++)
		sFormattedText+=m_arrRightLiterals[nIndex];	
	return sFormattedText;
}


BOOL COXEdit::IsInputData(int nPosition) const
{
	// We frequently need to know if a position refers to 
	// input data or to a literal. 
	return((nPosition>=m_arrLeftLiterals.GetSize()&& 
		nPosition<m_arrLeftLiterals.GetSize()+
		m_arrInputData.GetSize())? TRUE : FALSE);
}


BOOL COXEdit::IsInputPosition(int nPosition) const
{
	// We frequently need to know if a position refers to 
	// input data or to a literal. 
	return((nPosition>=m_arrLeftLiterals.GetSize() && 
		nPosition<=m_arrLeftLiterals.GetSize()+
		m_arrInputData.GetSize())? TRUE : FALSE);
}


int COXEdit::DeleteRange(int& nSelectionStart, int& nSelectionEnd)
{
	int nDeleteCount=0;
	TCHAR chLastDeleted=_T('\0');
	TCHAR chFirstDeleted=_T('\0');
	int nIndex=0;
	for(nIndex=nSelectionEnd-1; nIndex>=nSelectionStart; nIndex--)
	{
		if(IsInputData(nIndex))
		{
			if(chFirstDeleted==_T('\0'))
			{
				chFirstDeleted=m_arrInputData[RPtoLP(nIndex)];
			}
			chLastDeleted=m_arrInputData[RPtoLP(nIndex)];
			m_arrInputData.RemoveAt(RPtoLP(nIndex));
			nDeleteCount++;
		}
	}
	if(chLastDeleted==_T('\r') && RPtoLP(nIndex+1)!=-1 && 
		m_arrInputData[RPtoLP(nIndex+1)]==_T('\n'))
	{
		m_arrInputData.RemoveAt(RPtoLP(nIndex+1));
		nDeleteCount++;
	}
	if(chFirstDeleted==_T('\n') && RPtoLP(nSelectionStart-1)!=-1 && 
		m_arrInputData[RPtoLP(nSelectionStart-1)]==_T('\r'))
	{
		m_arrInputData.RemoveAt(RPtoLP(nSelectionStart-1));
		nDeleteCount++;
		nSelectionStart--;
	}

	if(nDeleteCount)
	{
		Update(-1);
	}

	// return the deleted count so that an error can be generated 
	// if none were deleted. 
	return nDeleteCount;
}

int COXEdit::InsertAt(int nSelectionStart, TCHAR chNewChar)
{
	int nInsertionPoint=nSelectionStart;
	if(!IsInputPosition(nInsertionPoint))
	{
		nInsertionPoint=GetNextInputLocation(nInsertionPoint+1);
		ASSERT(IsInputPosition(nInsertionPoint));
	}

	m_arrInputData.InsertAt(RPtoLP(nInsertionPoint),chNewChar);

	//nInsertionPoint+=ValidateInput();
	nInsertionPoint+=Validate();

	Update(-1);

	return nInsertionPoint;
}


int COXEdit::SetAt(int nSelectionStart, TCHAR chNewChar)
{
	int nInsertionPoint=nSelectionStart;
	if(!IsInputData(nInsertionPoint))
	{
		nInsertionPoint=GetNextInputLocation(nInsertionPoint+1);
		if(!IsInputData(nInsertionPoint))
			return -1;
	}

	m_arrInputData.SetAt(RPtoLP(nInsertionPoint),chNewChar);

	nInsertionPoint+=Validate();

	Update(-1);

	return nInsertionPoint;
}


int COXEdit::GetNextInputLocation(int nSelectionStart)
{
	int nNextInputLocation=nSelectionStart;
	if(!IsInputPosition(nNextInputLocation))
	{
		nNextInputLocation= PtrToInt(m_arrLeftLiterals.GetSize()+
			m_arrInputData.GetSize());
		for(int nIndex=nSelectionStart; 
			nIndex<m_arrInputData.GetSize()+m_arrLeftLiterals.GetSize(); 
			nIndex++)
		{
			if(IsInputPosition(nIndex))
			{
				nNextInputLocation=nIndex;
				break;
			}
		}
	}

	ASSERT(IsInputPosition(nNextInputLocation));
	return nNextInputLocation;
}


int COXEdit::GetPreviousInputLocation(int nSelectionStart)
{
	int nPreviousInputLocation=nSelectionStart;
	if(!IsInputPosition(nPreviousInputLocation))
	{
		nPreviousInputLocation=PtrToInt(m_arrLeftLiterals.GetSize());
		for(int nIndex=nSelectionStart; 
			nIndex>=m_arrLeftLiterals.GetSize(); 
			nIndex--)
		{
			if(IsInputPosition(nIndex))
			{
				nPreviousInputLocation=nIndex;
				break;
			}
		}
	}

	ASSERT(IsInputPosition(nPreviousInputLocation));
	return nPreviousInputLocation;
}


void COXEdit::Update(int nSelectionStart/*=0*/)
{
	// Update the edit control if it exists. 
	if(::IsWindow(GetSafeHwnd()))
	{
		m_nSetTextSemaphor++;
		SetWindowText(GetFormattedText());
		m_nSetTextSemaphor--;
		SetModify(TRUE);
		// We usually need to update the insertion point. 
		if(nSelectionStart>=0)
			UpdateInsertionPointForward(nSelectionStart);
	}
}


void COXEdit::UpdateInsertionPointForward(int nSelectionStart)
{
	int nNewInsertionPoint=GetNextInputLocation(nSelectionStart);
	SetSel(nNewInsertionPoint,nNewInsertionPoint);
}


void COXEdit::UpdateInsertionPointBackward(int nSelectionStart)
{
	int nNewInsertionPoint=GetPreviousInputLocation(nSelectionStart);
	SetSel(nNewInsertionPoint,nNewInsertionPoint);
}


int COXEdit::RPtoLP(int nRealPos)const 
{
	// All COXEdit functions that take cusor position as argument interpret it
	// as real position within edit control(taking into account all symbols including 
	// literals). But sometimes we want to know which non-literal symbol is at  
	// particular real position. In that case this function is really useful

	if(nRealPos<m_arrLeftLiterals.GetSize() || 
		nRealPos>m_arrLeftLiterals.GetSize()+m_arrInputData.GetSize())
	{
		return -1;
	}
	else 
	{
		return PtrToInt(nRealPos-m_arrLeftLiterals.GetSize());
	}
}

		
int COXEdit::LPtoRP(int nLogicalPos)const 
{
	if(nLogicalPos<0 || nLogicalPos>m_arrInputData.GetSize())
	{
		return -1;
	}
	else
	{
		return PtrToInt(nLogicalPos+m_arrLeftLiterals.GetSize());
	}
}


void COXEdit::ValidationError()
{
	::MessageBeep(MB_ICONEXCLAMATION);
}


CString COXEdit::GetLeftLiterals() const
{
	CString sLeftLiterals;
	for(int nIndex=0; nIndex<m_arrLeftLiterals.GetSize(); nIndex++)
	{
		sLeftLiterals+=m_arrLeftLiterals[nIndex];
	}
	return sLeftLiterals;
}


CString COXEdit::GetRightLiterals() const
{
	CString sRightLiterals;
	for(int nIndex=0; nIndex<m_arrRightLiterals.GetSize(); nIndex++)
	{
		sRightLiterals+=m_arrRightLiterals[nIndex];
	}
	return sRightLiterals;
}


BOOL COXEdit::NotifyParent(UINT nNotificationID)
{
	CWnd* pParentWnd=GetParent();
	if(pParentWnd==NULL)
		return FALSE;

	pParentWnd->SendMessage(WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(),nNotificationID),
		(LPARAM)GetSafeHwnd());
	return TRUE;
}


/////////////////////////////////////////////////////////////////////////////



////////////////
// COXNumericEdit implements a edit control which numeric values.
//
//////////////////////////////////////////////////	
//////////////////////////////////////////////////	
	
IMPLEMENT_DYNCREATE(COXNumericEdit, COXEdit)

BEGIN_MESSAGE_MAP(COXNumericEdit, COXEdit)
	//{{AFX_MSG_MAP(COXNumericEdit)
	ON_WM_CTLCOLOR_REFLECT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////
// Constructor
COXNumericEdit::COXNumericEdit()
{
	m_bNegativeValue=FALSE;

	m_clrTextNegative=m_clrText;

	m_bForceToUsePeriod=TRUE;

	// default values
	//
	m_nDecimalDigitCount=-1;
	m_chGroupSeparator=_T(',');
	m_nGroupLength=3;	
	m_chDecimalSeparator=_T('.');	
	m_bShowLeadingZero=TRUE;	
	m_nFractionalDigitCount=2;
	m_sFormatNegative=_T("-1.1");
	m_sFormatPositive=_T("1.1");

	m_nDecimalDigitCountDefault=-1;
	m_chGroupSeparatorDefault=_T(',');
	m_nGroupLengthDefault=3;	
	m_chDecimalSeparatorDefault=_T('.');	
	m_bShowLeadingZeroDefault=TRUE;	
	m_nFractionalDigitCountDefault=2;
	m_sFormatNegativeDefault=_T("-1.1");
	m_sFormatPositiveDefault=_T("1.1");

	//
	//////////////////////
}


BOOL COXNumericEdit::InitControl()
{
	if(!COXEdit::InitControl())
		return FALSE;
	
	return TRUE;
}


void COXNumericEdit::DeleteContents()
{
	COXEdit::DeleteContents();
	m_dValue=0.0;
}


int COXNumericEdit::InsertAt(int nSelectionStart, TCHAR chNewChar)
{
	return TranslateSymbol(nSelectionStart,chNewChar,FALSE);
}


int COXNumericEdit::SetAt(int nSelectionStart, TCHAR chNewChar)
{
	return TranslateSymbol(nSelectionStart,chNewChar,TRUE);
}


int COXNumericEdit::DeleteRange(int& nSelectionStart, int& nSelectionEnd)
{
	ASSERT(nSelectionStart >= 0);
	ASSERT(nSelectionStart <= nSelectionEnd);

	// v9.3 update 01 TD select '-' sign and hit delete for infinite loop... (potential fix)
	if(nSelectionEnd == -1) return 0;

	nSelectionStart = GetNextInputLocation( nSelectionStart );

	// remember the number of group delimeter symbols before deleting range
	int nGroupDelimiterSymbolCount = GetGroupSeparatorSymbolsInserted();
	int nGroupDelimiterSymbolCountBeforeRangeStartPos = 
		GetGroupSeparatorSymbolsInserted(0,RPtoLP(nSelectionStart));

	int nDeletedCount = 0;
	int nDecimalDelimiterPos = FindSymbol(GetDecimalSeparator());
	if(nDecimalDelimiterPos == -1 || 
		nDecimalDelimiterPos < nSelectionStart ||
		nDecimalDelimiterPos > nSelectionEnd)
	{
		nDeletedCount = COXEdit::DeleteRange(nSelectionStart,nSelectionEnd);
		if(nDeletedCount > 0)
		{
			//ValidateInput();
			Validate();
		}
	}
	else
	{
		if(IsNegativeValue() && nSelectionStart <= m_arrLeftLiterals.GetSize() && nSelectionEnd >= GetWindowTextLength() - m_arrRightLiterals.GetSize())
		{	// Clear the negative value flag when all contents of the edit conrol is deleted
			m_bNegativeValue = FALSE;
		}
		if(nDecimalDelimiterPos < nSelectionEnd)
		{
			int nPosition = nDecimalDelimiterPos+1;
			nDeletedCount = COXEdit::DeleteRange(nPosition,nSelectionEnd);
		}
		if(nDecimalDelimiterPos > nSelectionStart)
		{
			nDeletedCount += COXEdit::DeleteRange(
				nSelectionStart,nDecimalDelimiterPos);
		}
		if(nDeletedCount>0)
		{
			//ValidateInput();
			Validate();
		}
	}

	// take into account the fact that due to deleting of the range
	// some of group delimeter symbols might get deleted 
	nDeletedCount += nGroupDelimiterSymbolCount - GetGroupSeparatorSymbolsInserted();
	// adjust selection range based on the fact that number of
	// group delimeter symbols before the original start of the range
	// might have changed
	int nShiftRange = nGroupDelimiterSymbolCountBeforeRangeStartPos - 
		GetGroupSeparatorSymbolsInserted(0,RPtoLP(nSelectionStart));

	nSelectionStart -= nShiftRange;
	nSelectionStart = (nSelectionStart < 0 ? 0 : nSelectionStart);
	nSelectionEnd -= nShiftRange;
	nSelectionEnd = (nSelectionEnd < 0 ? 0 : nSelectionEnd);

	return nDeletedCount;
}


CString COXNumericEdit::GetInputData()const
{
	CString sInputData=COXEdit::GetInputData();
	if(GetGroupLength()>0)
	{
#if _MFC_VER>0x0421
		sInputData.Remove(GetGroupSeparator());
#else
		RemoveCharFromString(sInputData,GetGroupSeparator());
#endif
	}
	return sInputData;
}


BOOL COXNumericEdit::IsInputData(int nPosition) const
{
	if(!COXEdit::IsInputData(nPosition))
	{
		return FALSE;
	}

	if(m_arrInputData[RPtoLP(nPosition)]==GetDecimalSeparator() ||
		m_arrInputData[RPtoLP(nPosition)]==GetGroupSeparator())
	{
		return FALSE;
	}

	return TRUE;
}


BOOL COXNumericEdit::GetLocaleSettings()
{
	int nLength;
	BOOL bSetNewValue;

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,NULL,0);
	if(nLength)
	{
		CString sExisting(m_chDecimalSeparator);
		CString sDefault(m_chDecimalSeparatorDefault);
		bSetNewValue=(sExisting.CompareNoCase(sDefault)==0);
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,
			&m_chDecimalSeparatorDefault,nLength);
		if(bSetNewValue)
		{
			m_chDecimalSeparator=m_chDecimalSeparatorDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,NULL,0);
	if(nLength)
	{
		CString sExisting(m_chGroupSeparator);
		CString sDefault(m_chGroupSeparatorDefault);
		bSetNewValue=(sExisting.CompareNoCase(sDefault)==0);
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,
			&m_chGroupSeparatorDefault,nLength);
		if(bSetNewValue)
		{
			m_chGroupSeparator=m_chGroupSeparatorDefault;
		}
	}

	TCHAR* buffer;
	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ILZERO,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_bShowLeadingZero==m_bShowLeadingZeroDefault);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ILZERO,(LPTSTR)buffer,nLength);
		m_bShowLeadingZeroDefault=(BOOL)_ttoi(buffer);
		delete[] buffer;
		if(bSetNewValue)
		{
			m_bShowLeadingZero=m_bShowLeadingZeroDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SGROUPING,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_nGroupLength==m_nGroupLengthDefault);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SGROUPING,(LPTSTR)buffer,nLength);
		CString sGrouping=buffer;
		int nPosition=sGrouping.Find(_T(";0"));
		if(nPosition!=-1)
		{
			ASSERT(nPosition>0);
			sGrouping=sGrouping.Left(nPosition);
			m_nGroupLengthDefault=_ttoi(sGrouping);
		}
		delete[] buffer;
		if(bSetNewValue)
		{
			m_nGroupLength=m_nGroupLengthDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_IDIGITS,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_nFractionalDigitCount==m_nFractionalDigitCountDefault);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_IDIGITS,(LPTSTR)buffer,nLength);
		m_nFractionalDigitCountDefault=_ttoi(buffer);
		delete[] buffer;
		if(bSetNewValue)
		{
			m_nFractionalDigitCount=m_nFractionalDigitCountDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_INEGNUMBER,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_sFormatNegative.CompareNoCase(m_sFormatNegativeDefault)==0);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_INEGNUMBER,(LPTSTR)buffer,nLength);
		int nNegativeNumberFormatType=_ttoi(buffer);
		switch(nNegativeNumberFormatType)
		{
		case 0:
			m_sFormatNegativeDefault=_T("(1.1)");
			break;
		case 1:
			m_sFormatNegativeDefault=_T("-1.1");
			break;
		case 2:
			m_sFormatNegativeDefault=_T("- 1.1");
			break;
		case 3:
			m_sFormatNegativeDefault=_T("1.1-");
			break;
		case 4:
			m_sFormatNegativeDefault=_T("1.1 -");
			break;
		}
		delete[] buffer;
		if(bSetNewValue)
		{
			m_sFormatNegative=m_sFormatNegativeDefault;
		}
	}

	return TRUE;
}


int COXNumericEdit::FindSymbol(TCHAR chSymbol) const
{
	int nPosition=-1;
	for(int nIndex=0; nIndex<m_arrInputData.GetSize(); nIndex++)
	{
		if(m_arrInputData[nIndex]==chSymbol)
		{
			nPosition=LPtoRP(nIndex);
			break;
		}
	}

	return nPosition;
}


int COXNumericEdit::ValidateInput()
{
	int nSymbolsBefore=PtrToInt(m_arrInputData.GetSize());

	BOOL bSetToNegative=FALSE;
	BOOL bDecimalSeparatorFound=FALSE;
	for(int nIndex=PtrToInt(m_arrInputData.GetSize())-1; nIndex>=0; nIndex--)
	{
		if(!(m_arrInputData[nIndex]>=_T('0') && m_arrInputData[nIndex]<=_T('9')))
		{
			if(nIndex==0 && m_arrInputData[nIndex]==_T('-'))
				bSetToNegative=TRUE;

			if(m_arrInputData[nIndex]==m_chDecimalSeparator)
			{
				if(!bDecimalSeparatorFound)
					bDecimalSeparatorFound=TRUE;
				else
					m_arrInputData.RemoveAt(nIndex);
			}
			else
				m_arrInputData.RemoveAt(nIndex);
		}
	}

	int nDecimalDelimiterPos=FindSymbol(GetDecimalSeparator());
	int nDecimalDigitsInserted=GetDecimalDigitsInserted();
	int nFractionalDigitsInserted=GetFractionalDigitsInserted();

	for(;m_arrInputData.GetSize()>0;)
	{
		if(m_arrInputData[0]==_T('0') && nDecimalDigitsInserted>1)
		{
			m_arrInputData.RemoveAt(0);
			nDecimalDigitsInserted--;
			if(nDecimalDelimiterPos!=-1)
				nDecimalDelimiterPos--;
		}
		else
			break;
	}

	BOOL bUpdate=FALSE;

	int nFractionalOvertypedCount=
		nFractionalDigitsInserted-GetFractionalDigitCount();
	if(nFractionalOvertypedCount>0)
	{
		for(int nIndex=0; nIndex<nFractionalOvertypedCount; nIndex++)
			m_arrInputData.RemoveAt(m_arrInputData.GetSize()-1);
		bUpdate=TRUE;
	}

	if(m_arrInputData.GetSize()>0 && GetDecimalDigitCount()!=-1)
	{
		if(m_arrInputData[0]!=_T('0'))
		{
			int nDecimalOvertypedCount=nDecimalDigitsInserted-GetDecimalDigitCount();
			if(nDecimalOvertypedCount>0)
			{
				for(int nIndex=0; nIndex<nDecimalOvertypedCount; nIndex++)
				{
					m_arrInputData.RemoveAt(0);
					nDecimalDigitsInserted--;
					if(nDecimalDelimiterPos!=-1)
						nDecimalDelimiterPos--;
				}
				bUpdate=TRUE;
			}
		}
		else
		{
			ASSERT(nDecimalDigitsInserted==1);
		}
	}

	if(GetFractionalDigitCount()!=0)
	{
		if(nDecimalDelimiterPos==-1)
			m_arrInputData.InsertAt(m_arrInputData.GetSize(),GetDecimalSeparator());

		if(GetFractionalDigitCount()>nFractionalDigitsInserted)
		{
			for(int nIndex=nFractionalDigitsInserted; 
				nIndex<GetFractionalDigitCount(); 
				nIndex++)
			{
				m_arrInputData.InsertAt(m_arrInputData.GetSize(),_T('0'));
			}
		}

		if(nDecimalDigitsInserted==0 && GetShowLeadingZero())
			m_arrInputData.InsertAt(0,_T('0'));

		if(nDecimalDigitsInserted==1 && !GetShowLeadingZero() 
			&& m_arrInputData[0]==_T('0'))
		{
			m_arrInputData.RemoveAt(0);
			nDecimalDigitsInserted--;
			if(nDecimalDelimiterPos!=-1)
				nDecimalDelimiterPos--;
		}
	}
	else
	{
		if(nDecimalDelimiterPos!=-1)
		{
			m_arrInputData.RemoveAt(RPtoLP(nDecimalDelimiterPos));
			nDecimalDelimiterPos=-1;
		}

		if(nDecimalDigitsInserted==0 && GetShowLeadingZero())
			m_arrInputData.InsertAt(0,_T('0'));
	}

	if(GetGroupLength()>0)
	{
		int nDecimalDelimiterPos=RPtoLP(FindSymbol(m_chDecimalSeparator));
		nDecimalDelimiterPos=(nDecimalDelimiterPos==-1) ?
			PtrToInt(m_arrInputData.GetSize()) : nDecimalDelimiterPos;

		int nDigitsInRow=0;
		for(int nIndex=nDecimalDelimiterPos-1; nIndex>=0; nIndex--)
		{
			if(nDigitsInRow==GetGroupLength())
			{
				if(m_arrInputData[nIndex]!=GetGroupSeparator())
				{
					m_arrInputData.InsertAt(nIndex+1,GetGroupSeparator());
					bUpdate=TRUE;
				}
				nDigitsInRow=1;
			}
			else
			{
				if(m_arrInputData[nIndex]!=GetGroupSeparator())
					nDigitsInRow++;
			}
		}
	}


	if(bSetToNegative)
		ChangeSign(bSetToNegative);
	else
		ChangeSign(IsNegativeValue());

	if(bUpdate)
		Update(-1);

	CString sInputData=GetInputData();
	if(GetGroupLength()>0)
	{
#if _MFC_VER>0x0421
		sInputData.Remove(GetGroupSeparator());
		sInputData.Replace(GetDecimalSeparator(),_T('.'));
#else
		RemoveCharFromString(sInputData,GetGroupSeparator());
		ReplaceCharInString(sInputData,GetDecimalSeparator(),_T('.'));
#endif
	}
	TCHAR* pchStop;
	m_dValue=_tcstod(sInputData,&pchStop);
	if(IsNegativeValue())
		m_dValue*=-1;

	int nSymbolsAfter=PtrToInt(m_arrInputData.GetSize());

	return nSymbolsAfter-nSymbolsBefore;
}


int COXNumericEdit::ChangeSign(BOOL bNegative)
{
	m_bNegativeValue=bNegative;

	CString sMask=GetMask();
	int nPosInputData=sMask.Find(OXEDITABLETEXT_SYMBOL);
	CString sLeftLiterals=(nPosInputData>0 ? sMask.Left(nPosInputData) : _T(""));
	CString sRightLiterals=(nPosInputData==-1 ? 
		_T("") : sMask.Mid(nPosInputData+1));

	int nLeftLiteralsBefore=PtrToInt(m_arrLeftLiterals.GetSize());

	m_arrLeftLiterals.RemoveAll();
	m_arrRightLiterals.RemoveAll();

	int nIndex=0;
	for(nIndex=0; nIndex<sLeftLiterals.GetLength(); nIndex++)
	{
		m_arrLeftLiterals.InsertAt(m_arrLeftLiterals.GetSize(),
			sLeftLiterals[nIndex]);
	}

	if(m_bNegativeValue)
	{
		int nPosInNegative=m_sFormatNegative.Find(OXNUMERICVALUE_PLACEHOLDER);
		CString sLeftNegativeLiterals=(nPosInNegative>0 ? 
			m_sFormatNegative.Left(nPosInNegative) : _T(""));
		CString sRightNegativeLiterals=(nPosInNegative==-1 ? 
			_T("") : m_sFormatNegative.Mid(nPosInNegative+
			OXNUMERICVALUE_PLACEHOLDER_LENGTH));

		for(nIndex=0; nIndex<sLeftNegativeLiterals.GetLength(); nIndex++)
		{
			m_arrLeftLiterals.InsertAt(m_arrLeftLiterals.GetSize(),
				sLeftNegativeLiterals[nIndex]);
		}
		for(nIndex=0; nIndex<sRightNegativeLiterals.GetLength(); nIndex++)
		{
			m_arrRightLiterals.InsertAt(m_arrRightLiterals.GetSize(),
				sRightNegativeLiterals[nIndex]);
		}
	}
	else
	{
		int nPosInPositive=m_sFormatPositive.Find(OXNUMERICVALUE_PLACEHOLDER);
		CString sLeftPositiveLiterals=(nPosInPositive>0 ? 
			m_sFormatPositive.Left(nPosInPositive) : _T(""));
		CString sRightPositiveLiterals=(nPosInPositive==-1 ? 
			_T("") : m_sFormatPositive.Mid(nPosInPositive+
			OXNUMERICVALUE_PLACEHOLDER_LENGTH));

		for(nIndex=0; nIndex<sLeftPositiveLiterals.GetLength(); nIndex++)
		{
			m_arrLeftLiterals.InsertAt(m_arrLeftLiterals.GetSize(),
				sLeftPositiveLiterals[nIndex]);
		}
		for(nIndex=0; nIndex<sRightPositiveLiterals.GetLength(); nIndex++)
		{
			m_arrRightLiterals.InsertAt(m_arrRightLiterals.GetSize(),
				sRightPositiveLiterals[nIndex]);
		}
	}

	for(nIndex=0; nIndex<sRightLiterals.GetLength(); nIndex++)
	{
		m_arrRightLiterals.InsertAt(m_arrRightLiterals.GetSize(),
			sRightLiterals[nIndex]);
	}

	UpdateMask();

	Update(-1);

	if((m_bNegativeValue && m_dValue>=0) || (!m_bNegativeValue && m_dValue<0))
		m_dValue*=-1;

	return PtrToInt(m_arrLeftLiterals.GetSize())-nLeftLiteralsBefore;
}


BOOL COXNumericEdit::SetDecimalDigitCount(int nDecimalDigitCount)
{
	if(nDecimalDigitCount<-1)
	{
		TRACE(_T("COXNumericEdit::SetDecimalDigitCount: invalid decimal digit count has been specified\n"));
		return FALSE;
	}

	if(m_nDecimalDigitCount==nDecimalDigitCount)
		return TRUE;

	CString sInputData=GetInputData();
	m_nDecimalDigitCount=nDecimalDigitCount;
	EmptyData(TRUE);
	VERIFY(SetInputData(sInputData));

	return TRUE;
}


BOOL COXNumericEdit::SetDecimalSeparator(TCHAR chDecimalSeparator)
{
	if((chDecimalSeparator>=_T('0') && chDecimalSeparator<=_T('9')) ||
		chDecimalSeparator==_T('-') || chDecimalSeparator==GetGroupSeparator() ||
		chDecimalSeparator==NULL)
	{
		TRACE(_T("COXNumericEdit::SetDecimalSeparator: invalid decimal separator has been specified\n"));
		return FALSE;
	}

	int nDecimalDelimiterPos=FindSymbol(GetDecimalSeparator());
	m_chDecimalSeparator=chDecimalSeparator;
	if(nDecimalDelimiterPos!=-1)
	{
		m_arrInputData.SetAt(RPtoLP(nDecimalDelimiterPos),chDecimalSeparator);
		Update(-1);
	}

	return TRUE;
}


BOOL COXNumericEdit::SetFractionalDigitCount(int nFractionalDigitCount)
{
	if(nFractionalDigitCount<0)
	{
		TRACE(_T("COXNumericEdit::SetFractionalDigitCount: invalid fractional digit count has been specified\n"));
		return FALSE;
	}

	if(m_nFractionalDigitCount==nFractionalDigitCount)
		return TRUE;

	CString sInputData=GetInputData();
	m_nFractionalDigitCount=nFractionalDigitCount;
	EmptyData(TRUE);
	VERIFY(SetInputData(sInputData));

	return TRUE;
}

BOOL COXNumericEdit::SetGroupLength(int nGroupLength)
{
	if(nGroupLength<0)
	{
		TRACE(_T("COXNumericEdit::SetGroupLength: invalid group length has been specified\n"));
		return FALSE;
	}

	if(m_nGroupLength==nGroupLength)
	{
		return TRUE;
	}

	CString sInputData=GetInputData();
	m_nGroupLength=nGroupLength;
	EmptyData(TRUE);
	VERIFY(SetInputData(sInputData));

	return TRUE;
}


BOOL COXNumericEdit::SetGroupSeparator(TCHAR chGroupSeparator)
{
	if((chGroupSeparator>=_T('0') && chGroupSeparator<=_T('9')) ||
		chGroupSeparator==_T('-') || chGroupSeparator==GetDecimalSeparator() ||
		chGroupSeparator==NULL)
	{
		TRACE(_T("COXNumericEdit::SetGroupSeparator: invalid group separator has been specified\n"));
		return FALSE;
	}

	if(m_chGroupSeparator==chGroupSeparator)
		return TRUE;

	CString sInputData=GetInputData();
	m_chGroupSeparator=chGroupSeparator;
	EmptyData(TRUE);
	VERIFY(SetInputData(sInputData));

	return TRUE;
}


BOOL COXNumericEdit::SetNegativeFormat(LPCTSTR lpszFormatNegative)
{
	ASSERT(lpszFormatNegative!=NULL);

	CString sNegativeFormat=lpszFormatNegative;
	if(sNegativeFormat.Find(OXNUMERICVALUE_PLACEHOLDER)==-1)
	{
		TRACE(_T("COXNumericEdit::SetNegativeFormat: invalid format for displaying negative value has been specified\n"));
		return FALSE;
	}

	m_sFormatNegative=sNegativeFormat;
	ChangeSign(IsNegativeValue());

	return TRUE;
}


BOOL COXNumericEdit::SetPositiveFormat(LPCTSTR lpszFormatPositive)
{
	ASSERT(lpszFormatPositive!=NULL);

	CString sPositiveFormat=lpszFormatPositive;
	if(sPositiveFormat.Find(OXNUMERICVALUE_PLACEHOLDER)==-1)
	{
		TRACE(_T("COXNumericEdit::SetPositiveFormat: invalid format for displaying positive value has been specified\n"));
		return FALSE;
	}

	m_sFormatPositive=sPositiveFormat;
	ChangeSign(IsNegativeValue());

	return TRUE;
}


BOOL COXNumericEdit::SetValue(double dValue)
{
	m_dValue=dValue;
	m_bNegativeValue=(m_dValue<0 ? TRUE : FALSE);
	BOOL bSetNegative=m_bNegativeValue;

	int decimal, sign;

#if _MSC_VER >= 1400
			int count = GetFractionalDigitCount();
			char buffer[MAX_PATH];	
			_fcvt_s(buffer, MAX_PATH, dValue, count, &decimal,&sign);
#else
			char *buffer= UTBStr::fcvt(m_dValue,GetFractionalDigitCount(),&decimal,&sign);
#endif
	int nSymbolCount=PtrToInt(strlen(buffer));
	TCHAR* chInputData=new TCHAR[nSymbolCount+1];
#ifdef _UNICODE
	UTBStr::mbstowcs(chInputData,nSymbolCount+1,buffer,nSymbolCount);
#else
	UTBStr::strncpy(chInputData,nSymbolCount+1, buffer,nSymbolCount);
#endif
	chInputData[nSymbolCount]=_T('\0');

	CString sInputData=chInputData;
	CString sDecimalSeparator(GetDecimalSeparator());
	if(!sInputData.IsEmpty())
	{
		for(int nIndex=decimal; nIndex<1; nIndex++)
		{
			sInputData=_T("0")+sInputData;
			decimal++;
			nSymbolCount++;
		}
		sInputData=sInputData.Left(decimal)+sDecimalSeparator+sInputData.Mid(decimal);
		if(sign!=0)
		{
			sInputData=_T("-")+sInputData;
		}
	}
	EmptyData(TRUE);
	VERIFY(COXEdit::SetInputData(sInputData));

	delete[] chInputData;

	ChangeSign(bSetNegative);

	return TRUE;
}


void COXNumericEdit::SetShowLeadingZero(BOOL bShowLeadingZero)
{
	if(m_bShowLeadingZero==bShowLeadingZero)
	{
		return;
	}

	CString sInputData=GetInputData();
	m_bShowLeadingZero=bShowLeadingZero;
	EmptyData(TRUE);
	VERIFY(SetInputData(sInputData));

	return;
}


void COXNumericEdit::SetNegativeTextColor(COLORREF clrTextNegative, 
										  BOOL bRedraw/*=TRUE*/)
{
	if(m_clrTextNegative!=clrTextNegative)
	{
		m_clrTextNegative=clrTextNegative;
		if(bRedraw && IsNegativeValue())
			RedrawWindow();
	}
}


int COXNumericEdit::TranslateSymbol(int nSelectionStart, 
									TCHAR chNewChar, BOOL bReplaceExisting)
{
	if((chNewChar>=_T('0') && chNewChar<=_T('9')) || 
		chNewChar==m_chDecimalSeparator || chNewChar==_T('-') ||
		(m_bForceToUsePeriod && chNewChar==_T('.')))
	{
		if(chNewChar==_T('-'))
		{
			nSelectionStart+=ChangeSign(!IsNegativeValue())-1;
			return nSelectionStart;
		}

		if(chNewChar==m_chDecimalSeparator || 
			(m_bForceToUsePeriod && chNewChar==_T('.')))
		{
			if(GetFractionalDigitCount()==0)
				return -1;
			int nPos=FindSymbol(m_chDecimalSeparator);
			if(nPos==-1)
			{
				nSelectionStart=PtrToInt(m_arrLeftLiterals.GetSize()+m_arrInputData.GetSize());
				return COXEdit::InsertAt(nSelectionStart,chNewChar);
			}
			else
				return GetNextInputLocation(nPos);
		}

		if(m_arrInputData.GetSize()>0 && chNewChar==_T('0') && 
			RPtoLP(nSelectionStart)==0 && IsInputData(nSelectionStart))
		{
			if(bReplaceExisting)
			{
				m_arrInputData.RemoveAt(0);
				//ValidateInput();
				Validate();
				if(m_arrInputData[0]==_T('0') || 
					m_arrInputData[0]==GetDecimalSeparator())
					return nSelectionStart;
				else
					return -1;
			}
			else
				return -1;
		}

		if(m_arrInputData.GetSize()>0 && RPtoLP(nSelectionStart)==0)
		{
			if(bReplaceExisting && m_arrInputData[0]==GetDecimalSeparator())
				return COXEdit::InsertAt(nSelectionStart,chNewChar);
			else if(!bReplaceExisting && m_arrInputData[0]==_T('0'))
				return COXEdit::SetAt(nSelectionStart,chNewChar);
		}

		int nInsertedAtPos=-1;
		if(bReplaceExisting)
		{
			nInsertedAtPos=COXEdit::SetAt(nSelectionStart,chNewChar);
		}
		else
		{
			int nDecimalDelimiterPos=FindSymbol(GetDecimalSeparator());
			if(nDecimalDelimiterPos==-1 || nDecimalDelimiterPos>=nSelectionStart)
			{
				if(nDecimalDelimiterPos==-1 && GetDecimalDigitsInserted()==1 &&
					m_arrInputData[0]==_T('0'))
					nInsertedAtPos=COXEdit::SetAt(nSelectionStart-1,chNewChar);
				else if(GetDecimalDigitsInserted()<GetDecimalDigitCount() ||
					GetDecimalDigitCount()==-1)
					nInsertedAtPos=COXEdit::InsertAt(nSelectionStart,chNewChar);
				else
					nInsertedAtPos=COXEdit::SetAt(nSelectionStart,chNewChar);
			}
			else
			{
				if(GetFractionalDigitsInserted()<GetFractionalDigitCount())
					nInsertedAtPos=COXEdit::InsertAt(nSelectionStart,chNewChar);
				else
					nInsertedAtPos=COXEdit::SetAt(nSelectionStart,chNewChar);
			}
		}

		return nInsertedAtPos;
	}

	return -1;
}


int COXNumericEdit::GetDecimalDigitsInserted() const
{
	int nCount=0;
	int nDecimalDelimiterPos=FindSymbol(GetDecimalSeparator());
	nDecimalDelimiterPos=(nDecimalDelimiterPos==-1 ? 
		PtrToInt(m_arrInputData.GetSize()) : RPtoLP(nDecimalDelimiterPos));
	for(int nIndex=0; nIndex<nDecimalDelimiterPos; nIndex++)
	{
		if(m_arrInputData[nIndex]>=_T('0') && m_arrInputData[nIndex]<=_T('9'))
		{
			nCount++;
		}
	}

	return nCount;
}


int COXNumericEdit::GetFractionalDigitsInserted() const
{
	int nCount=0;
	int nDecimalDelimiterPos=FindSymbol(GetDecimalSeparator());
	nDecimalDelimiterPos=(nDecimalDelimiterPos==-1 ? 
		PtrToInt(m_arrInputData.GetSize()) : RPtoLP(nDecimalDelimiterPos));
	for(int nIndex=nDecimalDelimiterPos+1; nIndex<m_arrInputData.GetSize(); nIndex++)
	{
		if(m_arrInputData[nIndex]>=_T('0') && m_arrInputData[nIndex]<=_T('9'))
		{
			nCount++;
		}
	}

	return nCount;
}


int COXNumericEdit::GetGroupSeparatorSymbolsInserted(int nStartPos/* = 0*/,
													 int nEndPos/* = -1*/) const
{
	if(nEndPos == -1)
	{
		nEndPos = PtrToInt(m_arrInputData.GetSize()) - 1;
	}
	// v9.3 update 01 TD - asserts with no leading 0, no decimal group etc and single digit/sign 
	// selected for delete - hangs in release if Backspace selected on highlight '-'
	if(nEndPos == nStartPos || (nEndPos == -1 && nStartPos == 0)) {
		return 0;
	}
	ASSERT(nEndPos >= nStartPos);
	ASSERT(nStartPos >= 0 && nStartPos <= m_arrInputData.GetSize());
	ASSERT(nEndPos >= 0 && nEndPos <= m_arrInputData.GetSize());

	int nCount = 0;
	int nDecimalDelimiterPos = FindSymbol(GetDecimalSeparator());
	nDecimalDelimiterPos = (nDecimalDelimiterPos == -1 ? 
		PtrToInt(m_arrInputData.GetSize()) : RPtoLP(nDecimalDelimiterPos));
	for(int nIndex = nStartPos; nIndex < min(nDecimalDelimiterPos,nEndPos); nIndex++)
	{
		if(m_arrInputData[nIndex] == GetGroupSeparator())
		{
			nCount++;
		}
	}

	return nCount;
}


//////////////////
// Handle reflected WM_CTLCOLOR to set custom control color and font.
// 
HBRUSH COXNumericEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
	HBRUSH hBrush=COXEdit::CtlColor(pDC,nCtlColor);
	if(IsNegativeValue())
		pDC->SetTextColor(GetNegativeTextColor());
	return hBrush;
}



// global:
void AFXAPI DDX_OXNumericEdit(CDataExchange* pDX, int nIDC, double& dValue)
{
	// be sure that the control is a COXNumericEdit
	COXNumericEdit* pEdit = (COXNumericEdit*)pDX->m_pDlgWnd->GetDlgItem(nIDC);
	ASSERT(pEdit->IsKindOf(RUNTIME_CLASS(COXNumericEdit)));
	ASSERT_VALID(pEdit);
	if (pDX->m_bSaveAndValidate)
	{
		// retreive data
		dValue = pEdit->GetValue();
	}
	else
	{
		// set data
		pEdit->SetValue(dValue);
	}
}

void AFXAPI DDX_OXNumericEdit(CDataExchange* pDX, int nIDC, float& fValue)
{
	// be sure that the control is a COXNumericEdit
	COXNumericEdit* pEdit = (COXNumericEdit*)pDX->m_pDlgWnd->GetDlgItem(nIDC);
	ASSERT(pEdit->IsKindOf(RUNTIME_CLASS(COXNumericEdit)));
	ASSERT_VALID(pEdit);
	if (pDX->m_bSaveAndValidate)
	{
		// retreive data
		fValue = (float)pEdit->GetValue();
	}
	else
	{
		// set data
		pEdit->SetValue((double)fValue);
	}
}

void AFXAPI DDX_OXNumericEdit(CDataExchange* pDX, int nIDC, int& nValue)
{
	// be sure that the control is a COXNumericEdit
	COXNumericEdit* pEdit = (COXNumericEdit*)pDX->m_pDlgWnd->GetDlgItem(nIDC);
	ASSERT(pEdit->IsKindOf(RUNTIME_CLASS(COXNumericEdit)));
	ASSERT_VALID(pEdit);
	if (pDX->m_bSaveAndValidate)
	{
		// retreive data
		nValue = (int)pEdit->GetValue();
	}
	else
	{
		// set data
		pEdit->SetValue((double)nValue);
	}
}

void AFXAPI DDX_OXNumericEdit(CDataExchange* pDX, int nIDC, long& nValue)
{
	// be sure that the control is a COXNumericEdit
	COXNumericEdit* pEdit = (COXNumericEdit*)pDX->m_pDlgWnd->GetDlgItem(nIDC);
	ASSERT(pEdit->IsKindOf(RUNTIME_CLASS(COXNumericEdit)));
	ASSERT_VALID(pEdit);
	if (pDX->m_bSaveAndValidate)
	{
		// retreive data
		nValue = (long)pEdit->GetValue();
	}
	else
	{
		// set data
		pEdit->SetValue((double)nValue);
	}
}


/////////////////////////////////////////////////////////////////////////////



////////////////
// COXCurrencyEdit implements a edit control which numeric values.
//
//////////////////////////////////////////////////	
//////////////////////////////////////////////////	
	
IMPLEMENT_DYNCREATE(COXCurrencyEdit, COXNumericEdit)

BEGIN_MESSAGE_MAP(COXCurrencyEdit, COXNumericEdit)
	//{{AFX_MSG_MAP(COXCurrencyEdit)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////
// Constructor
COXCurrencyEdit::COXCurrencyEdit()
{
	m_nDecimalDigitCount=-1;
	m_chGroupSeparator=_T(',');
	m_nGroupLength=3;	
	m_chDecimalSeparator=_T('.');	
	m_bShowLeadingZero=TRUE;	
	m_nFractionalDigitCount=2;
	m_sFormatNegative=_T("($1.1)");
	m_sFormatPositive=_T("$1.1");
	m_sCurrency=_T("$");

	m_nDecimalDigitCountDefault=-1;
	m_chGroupSeparatorDefault=_T(',');
	m_nGroupLengthDefault=3;	
	m_chDecimalSeparatorDefault=_T('.');	
	m_bShowLeadingZeroDefault=TRUE;	
	m_nFractionalDigitCountDefault=2;
	m_sFormatNegativeDefault=_T("($1.1)");
	m_sFormatPositiveDefault=_T("$1.1");
	m_sCurrencyDefault=_T("$");
}


BOOL COXCurrencyEdit::GetLocaleSettings()
{
	int nLength;
	BOOL bSetNewValue;


	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SMONDECIMALSEP,NULL,0);
	if(nLength)
	{
		CString sExisting(m_chDecimalSeparator);
		CString sDefault(m_chDecimalSeparatorDefault);
		bSetNewValue=(sExisting.CompareNoCase(sDefault)==0);
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SMONDECIMALSEP,
			&m_chDecimalSeparatorDefault,nLength);
		if(bSetNewValue)
		{
			m_chDecimalSeparator=m_chDecimalSeparatorDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SMONTHOUSANDSEP,NULL,0);
	if(nLength)
	{
		CString sExisting(m_chGroupSeparator);
		CString sDefault(m_chGroupSeparatorDefault);
		bSetNewValue=(sExisting.CompareNoCase(sDefault)==0);
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SMONTHOUSANDSEP,
			&m_chGroupSeparatorDefault,nLength);
		if(bSetNewValue)
		{
			m_chGroupSeparator=m_chGroupSeparatorDefault;
		}
	}

	TCHAR* buffer;
	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ILZERO,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_bShowLeadingZero==m_bShowLeadingZeroDefault);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ILZERO,(LPTSTR)buffer,nLength);
		m_bShowLeadingZeroDefault=(BOOL)_ttoi(buffer);
		delete[] buffer;
		if(bSetNewValue)
		{
			m_bShowLeadingZero=m_bShowLeadingZeroDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SMONGROUPING,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_nGroupLength==m_nGroupLengthDefault);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SMONGROUPING,(LPTSTR)buffer,nLength);
		CString sGrouping=buffer;
		int nPosition=sGrouping.Find(_T(";0"));
		if(nPosition!=-1)
		{
			ASSERT(nPosition>0);
			sGrouping=sGrouping.Left(nPosition);
			m_nGroupLengthDefault=_ttoi(sGrouping);
		}
		delete[] buffer;
		if(bSetNewValue)
		{
			m_nGroupLength=m_nGroupLengthDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ICURRDIGITS,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_nFractionalDigitCount==m_nFractionalDigitCountDefault);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ICURRDIGITS,(LPTSTR)buffer,nLength);
		m_nFractionalDigitCountDefault=_ttoi(buffer);
		delete[] buffer;
		if(bSetNewValue)
		{
			m_nFractionalDigitCount=m_nFractionalDigitCountDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SCURRENCY,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_sCurrency.CompareNoCase(m_sCurrencyDefault)==0);
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SCURRENCY,
			m_sCurrencyDefault.GetBuffer(nLength-1),nLength);
		m_sCurrencyDefault.ReleaseBuffer();
		if(bSetNewValue)
		{
			m_sCurrency=m_sCurrencyDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ICURRENCY,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_sFormatPositive.CompareNoCase(m_sFormatPositiveDefault)==0);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ICURRENCY,(LPTSTR)buffer,nLength);
		int nPositiveNumberFormatType=_ttoi(buffer);
		switch(nPositiveNumberFormatType)
		{
		case 0:
			m_sFormatPositiveDefault=_T("$1.1");
			break;
		case 1:
			m_sFormatPositiveDefault=_T("1.1$");
			break;
		case 2:
			m_sFormatPositiveDefault=_T("$ 1.1");
			break;
		case 3:
			m_sFormatPositiveDefault=_T("1.1 $");
			break;
		}
		delete[] buffer;
		if(bSetNewValue)
		{
			m_sFormatPositive=m_sFormatPositiveDefault;
		}
	}

	nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_INEGCURR,NULL,0);
	if(nLength)
	{
		bSetNewValue=(m_sFormatNegative.CompareNoCase(m_sFormatNegativeDefault)==0);
		buffer=new TCHAR[nLength];
		::GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_INEGCURR,(LPTSTR)buffer,nLength);
		int nNegativeNumberFormatType=_ttoi(buffer);
		switch(nNegativeNumberFormatType)
		{
		case 0:
			m_sFormatNegativeDefault=_T("($1.1)");
			break;
		case 1:
			m_sFormatNegativeDefault=_T("-$1.1");
			break;
		case 2:
			m_sFormatNegativeDefault=_T("$-1.1");
			break;
		case 3:
			m_sFormatNegativeDefault=_T("$1.1-");
			break;
		case 4:
			m_sFormatNegativeDefault=_T("(1.1$)");
			break;
		case 5:
			m_sFormatNegativeDefault=_T("-1.1$");
			break;
		case 6:
			m_sFormatNegativeDefault=_T("1.1-$");
			break;
		case 7:
			m_sFormatNegativeDefault=_T("1.1$-");
			break;
		case 8:
			m_sFormatNegativeDefault=_T("-1.1 $");
			break;
		case 9:
			m_sFormatNegativeDefault=_T("-$ 1.1");
			break;
		case 10:
			m_sFormatNegativeDefault=_T("1.1 $-");
			break;
		case 11:
			m_sFormatNegativeDefault=_T("$ 1.1-");
			break;
		case 12:
			m_sFormatNegativeDefault=_T("$ -1.1");
			break;
		case 13:
			m_sFormatNegativeDefault=_T("1.1- $");
			break;
		case 14:
			m_sFormatNegativeDefault=_T("($ 1.1)");
			break;
		case 15:
			m_sFormatNegativeDefault=_T("(1.1 $)");
			break;
		}
		delete[] buffer;
		if(bSetNewValue)
		{
			m_sFormatNegative=m_sFormatNegativeDefault;
		}
	}

	return TRUE;
}


BOOL COXCurrencyEdit::SetCurrencyName(LPCTSTR lpszCurrency)
{
	if(lpszCurrency==NULL)
		return FALSE;

	CString sCurrency=lpszCurrency;
	if(sCurrency.GetLength()==0)
		return FALSE;

	CString sInputData=GetInputData();
	m_sCurrency=sCurrency;
	EmptyData(TRUE);
	VERIFY(SetInputData(sInputData));

	return TRUE;
}


BOOL COXCurrencyEdit::SetNegativeFormat(LPCTSTR lpszFormatNegative)
{
	ASSERT(lpszFormatNegative!=NULL);

	CString sNegativeFormat=lpszFormatNegative;
	if(sNegativeFormat.Find(OXNUMERICVALUE_PLACEHOLDER)==-1 || 
		sNegativeFormat.Find(_T("$"))==-1)
	{
		TRACE(_T("COXCurrencyEdit::SetNegativeFormat: invalid format for displaying negative value has been specified\n"));
		return FALSE;
	}

	return COXNumericEdit::SetNegativeFormat(lpszFormatNegative);
}


BOOL COXCurrencyEdit::SetPositiveFormat(LPCTSTR lpszFormatPositive)
{
	ASSERT(lpszFormatPositive!=NULL);

	CString sPositiveFormat=lpszFormatPositive;
	if(sPositiveFormat.Find(OXNUMERICVALUE_PLACEHOLDER)==-1 || 
		sPositiveFormat.Find(_T("$"))==-1)
	{
		TRACE(_T("COXCurrencyEdit::SetPositiveFormat: invalid format for displaying positive value has been specified\n"));
		return FALSE;
	}

	return COXNumericEdit::SetPositiveFormat(lpszFormatPositive);
}


void COXCurrencyEdit::UpdateMask()
{
	COXNumericEdit::UpdateMask();

	CString sCurrency=GetCurrencyName();
	ASSERT(sCurrency.GetLength()>0);

	CString sFormat=(IsNegativeValue() ? GetNegativeFormat() : GetPositiveFormat());
	int nPosCurrency=sFormat.Find(OXCURRENCY_SYMBOL);
	ASSERT(nPosCurrency!=-1);
	int nPosValue=sFormat.Find(OXNUMERICVALUE_PLACEHOLDER);
	ASSERT(nPosValue!=-1);

	CString sMask=GetMask();
	int nPosInputData=sMask.Find(OXEDITABLETEXT_SYMBOL);

	if(nPosCurrency<nPosValue)
	{
		CString sLeftLiterals=(nPosInputData==-1 ? _T("") : sMask.Left(nPosInputData));
		ASSERT(m_arrLeftLiterals[sLeftLiterals.GetLength()+nPosCurrency]==
			OXCURRENCY_SYMBOL);
		m_arrLeftLiterals.RemoveAt(sLeftLiterals.GetLength()+nPosCurrency);
		for(int nIndex=0; nIndex<sCurrency.GetLength(); nIndex++)
		{
			m_arrLeftLiterals.InsertAt(sLeftLiterals.GetLength()+
				nPosCurrency+nIndex,sCurrency[nIndex]);
		}
	}
	else
	{
		nPosCurrency-=nPosValue+OXNUMERICVALUE_PLACEHOLDER_LENGTH;
		CString sRightLiterals=(nPosInputData==-1 ? _T("") : sMask.Mid(nPosInputData+1));
		ASSERT(m_arrRightLiterals[nPosCurrency]==OXCURRENCY_SYMBOL);
		m_arrRightLiterals.RemoveAt(nPosCurrency);
		for(int nIndex=0; nIndex<sCurrency.GetLength(); nIndex++)
		{
			m_arrRightLiterals.InsertAt(nPosCurrency+nIndex,sCurrency[nIndex]);
		}
	}
}


// global:
void AFXAPI DDX_OXCurrencyEdit(CDataExchange* pDX, int nIDC, double& dValue)
{
	// be sure that the control is a COXNumericEdit
	COXCurrencyEdit* pEdit = (COXCurrencyEdit*)pDX->m_pDlgWnd->GetDlgItem(nIDC);
	ASSERT(pEdit->IsKindOf(RUNTIME_CLASS(COXCurrencyEdit)));
	ASSERT_VALID(pEdit);
	if (pDX->m_bSaveAndValidate)
	{
		// retreive data
		dValue = pEdit->GetValue();
	}
	else
	{
		// set data
		pEdit->SetValue(dValue);
	}
}

void AFXAPI DDX_OXCurrencyEdit(CDataExchange* pDX, int nIDC, float& fValue)
{
	// be sure that the control is a COXNumericEdit
	COXCurrencyEdit* pEdit = (COXCurrencyEdit*)pDX->m_pDlgWnd->GetDlgItem(nIDC);
	ASSERT(pEdit->IsKindOf(RUNTIME_CLASS(COXCurrencyEdit)));
	ASSERT_VALID(pEdit);
	if (pDX->m_bSaveAndValidate)
	{
		// retreive data
		fValue = (float)pEdit->GetValue();
	}
	else
	{
		// set data
		pEdit->SetValue((double)fValue);
	}
}

void AFXAPI DDX_OXCurrencyEdit(CDataExchange* pDX, int nIDC, int& nValue)
{
	// be sure that the control is a COXNumericEdit
	COXCurrencyEdit* pEdit = (COXCurrencyEdit*)pDX->m_pDlgWnd->GetDlgItem(nIDC);
	ASSERT(pEdit->IsKindOf(RUNTIME_CLASS(COXCurrencyEdit)));
	ASSERT_VALID(pEdit);
	if (pDX->m_bSaveAndValidate)
	{
		// retreive data
		nValue = (int)pEdit->GetValue();
	}
	else
	{
		// set data
		pEdit->SetValue((double)nValue);
	}
}
///////////////////////////////////////////////////////////////////////////////

int COXEdit::Validate()
{
	static CString sFullText;
	int nRet=ValidateInput();

	CString sNewText=GetInputData();
	if ((::IsWindow(m_hWnd)) && (UINT) sNewText.GetLength()>GetLimitText())
	{
		int n=0;
		for (n=0; n<sFullText.GetLength(); n++)
			m_arrInputData.SetAtGrow(n,sFullText.GetAt(n));
		m_arrInputData.SetAtGrow(n,NULL);
	}
	sFullText=COXEdit::GetInputData();
	return nRet;

}	

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions