//==========================================================================
// 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;
}
// v9.3 - update 03 - 64-bit - changed these to LRESULT, WPARAM, LPARAM from LONG, UINT, LONG
LRESULT COXEdit::OnCut(WPARAM wParam, LPARAM 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;
}
// v9.3 - update 03 - 64-bit - changed these to LRESULT, WPARAM, LPARAM from LONG, UINT, LONG
LRESULT COXEdit::OnCopy(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
// Just let copy do its thing and copy the selected text.
CEdit::Default();
return 0;
}
// v9.3 - update 03 - 64-bit - changed these to LRESULT, WPARAM, LPARAM from LONG, UINT, LONG
LRESULT COXEdit::OnPaste(WPARAM wParam, LPARAM 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;
}
// v9.3 - update 03 - 64-bit - changed these to LRESULT, WPARAM, LPARAM from LONG, UINT, LONG
LRESULT COXEdit::OnClear(WPARAM wParam, LPARAM 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);
}
// v9.3 - update 03 - 64-bit - changed these to WPARAM, LPARAM from UINT, LONG
LRESULT COXEdit::OnSetText(WPARAM wParam, LPARAM 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;
}