1466 lines
No EOL
80 KiB
C++
1466 lines
No EOL
80 KiB
C++
// 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.
|
||
|
||
// OXMaskedEdit.cpp : implementation file
|
||
//
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// Version: 9.3
|
||
|
||
|
||
#include "stdafx.h"
|
||
#include "OXMaskedEdit.h" // COXMaskedEdit
|
||
|
||
#ifdef _DEBUG
|
||
#define new DEBUG_NEW
|
||
#undef THIS_FILE
|
||
static char THIS_FILE[]=__FILE__;
|
||
#endif
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// CMaskData
|
||
|
||
IMPLEMENT_DYNCREATE(CMaskData, CObject)
|
||
|
||
CMaskData::CMaskData() :
|
||
m_eType (MaskDataTypeLITERAL),
|
||
m_chValue(chNULL)
|
||
{
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// CMaskData operations
|
||
|
||
void CMaskData::operator=(const CMaskData& src)
|
||
{
|
||
m_eType=src.m_eType ;
|
||
m_chValue=src.m_chValue;
|
||
}
|
||
|
||
BOOL CMaskData::IsInputData()
|
||
{
|
||
BOOL bIsInputData=FALSE;
|
||
switch(m_eType)
|
||
{
|
||
// These are the input types.
|
||
case MaskDataTypeDIGIT :
|
||
case MaskDataTypeALPHANUMERIC :
|
||
case MaskDataTypeALPHABETIC :
|
||
case MaskDataTypeALPHAETICUPPER :
|
||
case MaskDataTypeALPHAETICLOWER :
|
||
case MaskDataTypeCHARACTER :
|
||
bIsInputData=TRUE ;
|
||
break;
|
||
}
|
||
return bIsInputData;
|
||
}
|
||
|
||
BOOL CMaskData::IsValidInput(TCHAR chNewChar)
|
||
{
|
||
BOOL bIsValidInput=FALSE;
|
||
switch(m_eType)
|
||
{
|
||
// These are the input types.
|
||
case MaskDataTypeDIGIT :
|
||
bIsValidInput=_istdigit(chNewChar);
|
||
break;
|
||
case MaskDataTypeALPHANUMERIC :
|
||
bIsValidInput=_istalnum(chNewChar);
|
||
break;
|
||
case MaskDataTypeALPHABETIC :
|
||
case MaskDataTypeALPHAETICUPPER :
|
||
case MaskDataTypeALPHAETICLOWER :
|
||
bIsValidInput=_istalpha(chNewChar);
|
||
break;
|
||
case MaskDataTypeCHARACTER :
|
||
if((chNewChar >= 32) && (chNewChar <= 126))
|
||
bIsValidInput=TRUE ;
|
||
if((chNewChar >= 128) && (chNewChar <= 255))
|
||
bIsValidInput=TRUE ;
|
||
break;
|
||
}
|
||
return bIsValidInput;
|
||
}
|
||
|
||
TCHAR CMaskData::PreProcessChar(TCHAR chNewChar)
|
||
{
|
||
TCHAR chProcessedChar=chNewChar;
|
||
switch(m_eType)
|
||
{
|
||
case MaskDataTypeALPHAETICUPPER :
|
||
chProcessedChar=(TCHAR)_totupper(chNewChar);
|
||
break;
|
||
case MaskDataTypeALPHAETICLOWER :
|
||
chProcessedChar=(TCHAR)_totlower(chNewChar);
|
||
break;
|
||
}
|
||
return chProcessedChar;
|
||
}
|
||
|
||
#ifdef _DEBUG
|
||
void CMaskData::AssertValid() const
|
||
{
|
||
CObject::AssertValid();
|
||
ASSERT( (m_eType >= 0) && (m_eType < MASKDATATYPECOUNT));
|
||
ASSERT( m_chValue != chNULL);
|
||
}
|
||
|
||
void CMaskData::Dump(CDumpContext& dc) const
|
||
{
|
||
CObject::Dump(dc);
|
||
}
|
||
#endif
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// COXMaskedEdit
|
||
|
||
IMPLEMENT_DYNAMIC(COXMaskedEdit, CEdit)
|
||
|
||
COXMaskedEdit::COXMaskedEdit(LPCTSTR pszMask/*=_T("")*/) :
|
||
m_bInsertMode(TRUE),
|
||
m_chPromptSymbol(chSPACE),
|
||
m_chIntlDecimal(chPERIOD),
|
||
m_chIntlThousands(chCOMMA),
|
||
m_chIntlTime(chCOLON),
|
||
m_chIntlDate(chSLASH),
|
||
m_bAutoTab(FALSE),
|
||
m_nSetTextSemaphor(0),
|
||
m_bNotifyParent(TRUE)
|
||
{
|
||
int nLength;
|
||
|
||
nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
|
||
&m_chIntlDecimal, 0);
|
||
if(nLength)
|
||
{
|
||
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL ,
|
||
&m_chIntlDecimal, nLength);
|
||
}
|
||
nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
|
||
&m_chIntlThousands, 0);
|
||
if(nLength)
|
||
{
|
||
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
|
||
&m_chIntlThousands , nLength);
|
||
}
|
||
nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIME, &m_chIntlTime, 0);
|
||
if(nLength)
|
||
{
|
||
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIME, &m_chIntlTime, nLength);
|
||
}
|
||
nLength=::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, &m_chIntlDate, 0);
|
||
if(nLength)
|
||
{
|
||
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, &m_chIntlDate, nLength);
|
||
}
|
||
|
||
SetMask(pszMask);
|
||
}
|
||
|
||
COXMaskedEdit::~COXMaskedEdit()
|
||
{
|
||
DeleteContents();
|
||
}
|
||
|
||
|
||
BEGIN_MESSAGE_MAP(COXMaskedEdit, CEdit)
|
||
//{{AFX_MSG_MAP(COXMaskedEdit)
|
||
ON_WM_KEYDOWN()
|
||
ON_WM_CHAR()
|
||
ON_WM_SETFOCUS()
|
||
ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillfocus)
|
||
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()
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// COXMaskedEdit operations
|
||
|
||
void COXMaskedEdit::DeleteContents()
|
||
{
|
||
if(m_listData.GetCount()==0)
|
||
{
|
||
if(::IsWindow(GetSafeHwnd()))
|
||
SetWindowText(_T(""));
|
||
return;
|
||
}
|
||
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
delete pobjData;
|
||
pobjData=NULL;
|
||
}
|
||
m_listData.RemoveAll();
|
||
}
|
||
|
||
CString COXMaskedEdit::GetMask() const
|
||
{
|
||
CString csMask;
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
switch(pobjData->m_eType)
|
||
{
|
||
case MaskDataTypeDECIMALSEPARATOR : csMask += chMaskPlaceholderDECIMALSEPARATOR ; break;
|
||
case MaskDataTypeTHOUSANDSSEPARATOR: csMask += chMaskPlaceholderTHOUSANDSSEPARATOR; break;
|
||
case MaskDataTypeTIMESEPARATOR : csMask += chMaskPlaceholderTIMESEPARATOR ; break;
|
||
case MaskDataTypeDATESEPARATOR : csMask += chMaskPlaceholderDATESEPARATOR ; break;
|
||
case MaskDataTypeDIGIT : csMask += chMaskPlaceholderDIGIT ; break;
|
||
case MaskDataTypeALPHANUMERIC : csMask += chMaskPlaceholderALPHANUMERIC ; break;
|
||
case MaskDataTypeALPHABETIC : csMask += chMaskPlaceholderALPHABETIC ; break;
|
||
case MaskDataTypeALPHAETICUPPER : csMask += chMaskPlaceholderALPHABETICUPPER ; break;
|
||
case MaskDataTypeALPHAETICLOWER : csMask += chMaskPlaceholderALPHABETICLOWER ; break;
|
||
case MaskDataTypeCHARACTER : csMask += chMaskPlaceholderCHARACTER ; break;
|
||
case MaskDataTypeLITERALESCAPE :
|
||
// Need to add the escape to things that were escaped.
|
||
csMask += chMaskPlaceholderLITERALESCAPE;
|
||
csMask += pobjData->m_chValue ;
|
||
break;
|
||
default:
|
||
// Literals and everything else is kept the same.
|
||
csMask += pobjData->m_chValue;
|
||
break;
|
||
}
|
||
}
|
||
return csMask;
|
||
}
|
||
|
||
void COXMaskedEdit::SetMask(LPCTSTR pszMask)
|
||
{
|
||
if(pszMask==NULL)
|
||
{
|
||
pszMask=_T("");
|
||
}
|
||
DeleteContents();
|
||
|
||
CMaskData* pobjData=NULL;
|
||
for(LPCTSTR pszInsertionPoint=pszMask; *pszInsertionPoint; pszInsertionPoint++)
|
||
{
|
||
TCHAR chNew=*pszInsertionPoint;
|
||
pobjData=new CMaskData();
|
||
m_listData.AddTail(pobjData);
|
||
switch(chNew)
|
||
{
|
||
case chMaskPlaceholderDECIMALSEPARATOR :
|
||
pobjData->m_eType =MaskDataTypeDECIMALSEPARATOR ;
|
||
pobjData->m_chValue=m_chIntlDecimal ;
|
||
break;
|
||
case chMaskPlaceholderTHOUSANDSSEPARATOR:
|
||
pobjData->m_eType =MaskDataTypeTHOUSANDSSEPARATOR;
|
||
pobjData->m_chValue=m_chIntlThousands ;
|
||
break;
|
||
case chMaskPlaceholderTIMESEPARATOR :
|
||
pobjData->m_eType =MaskDataTypeTIMESEPARATOR ;
|
||
pobjData->m_chValue=m_chIntlTime ;
|
||
break;
|
||
case chMaskPlaceholderDATESEPARATOR :
|
||
pobjData->m_eType =MaskDataTypeDATESEPARATOR ;
|
||
pobjData->m_chValue=m_chIntlDate ;
|
||
break;
|
||
case chMaskPlaceholderDIGIT :
|
||
pobjData->m_eType =MaskDataTypeDIGIT ;
|
||
pobjData->m_chValue=m_chPromptSymbol ;
|
||
break;
|
||
case chMaskPlaceholderALPHANUMERIC :
|
||
pobjData->m_eType =MaskDataTypeALPHANUMERIC ;
|
||
pobjData->m_chValue=m_chPromptSymbol ;
|
||
break;
|
||
case chMaskPlaceholderALPHABETIC :
|
||
pobjData->m_eType =MaskDataTypeALPHABETIC ;
|
||
pobjData->m_chValue=m_chPromptSymbol ;
|
||
break;
|
||
case chMaskPlaceholderALPHABETICUPPER :
|
||
pobjData->m_eType =MaskDataTypeALPHAETICUPPER ;
|
||
pobjData->m_chValue=m_chPromptSymbol ;
|
||
break;
|
||
case chMaskPlaceholderALPHABETICLOWER :
|
||
pobjData->m_eType =MaskDataTypeALPHAETICLOWER ;
|
||
pobjData->m_chValue=m_chPromptSymbol ;
|
||
break;
|
||
case chMaskPlaceholderCHARACTER :
|
||
pobjData->m_eType =MaskDataTypeCHARACTER ;
|
||
pobjData->m_chValue=m_chPromptSymbol ;
|
||
break;
|
||
case chMaskPlaceholderLITERALESCAPE :
|
||
// It is the next character that is inserted.
|
||
pszInsertionPoint++;
|
||
chNew=*pszInsertionPoint;
|
||
if(chNew)
|
||
{
|
||
pobjData->m_eType =MaskDataTypeLITERALESCAPE ;
|
||
pobjData->m_chValue=chNew ;
|
||
break;
|
||
}
|
||
// If there is no character following the escape,
|
||
// just treat the escape as a literal so that the user
|
||
// will see the problem.
|
||
default:
|
||
// Everything else is just a literal.
|
||
pobjData->m_eType =MaskDataTypeLITERAL ;
|
||
pobjData->m_chValue=chNew ;
|
||
break;
|
||
}
|
||
}
|
||
ASSERT(GetMask()==pszMask);
|
||
|
||
Update();
|
||
|
||
if(::IsWindow(GetSafeHwnd()))
|
||
SetModify(FALSE);
|
||
}
|
||
|
||
CString COXMaskedEdit::GetInputData() const
|
||
{
|
||
CString csInputData;
|
||
|
||
if(m_listData.GetCount()==0)
|
||
{
|
||
GetWindowText(csInputData);
|
||
return csInputData;
|
||
}
|
||
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
// Ignore everything that is not data.
|
||
if(pobjData->IsInputData())
|
||
csInputData += pobjData->m_chValue;
|
||
}
|
||
return csInputData;
|
||
}
|
||
|
||
CString COXMaskedEdit::GetInputData(LPCTSTR lpszText) const
|
||
{
|
||
CString csInputData=lpszText;
|
||
int nSymbolCount=csInputData.GetLength();
|
||
CMaskData* pobjData=NULL;
|
||
CString sToExclude;
|
||
int nStartPos=-1;
|
||
int nEndPos=-1;
|
||
int nIndex=0;
|
||
int nRemovedCount=0;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
if(!pobjData->IsInputData())
|
||
{
|
||
if(nStartPos==-1)
|
||
{
|
||
nStartPos=nIndex;
|
||
sToExclude.Empty();
|
||
}
|
||
sToExclude+=pobjData->m_chValue;
|
||
}
|
||
else
|
||
{
|
||
if(nStartPos!=-1)
|
||
{
|
||
nEndPos=nIndex-1;
|
||
if(csInputData.Mid(nStartPos-nRemovedCount,
|
||
nEndPos-nStartPos+1)==sToExclude)
|
||
{
|
||
csInputData=csInputData.Left(nStartPos-nRemovedCount)+
|
||
csInputData.Mid(nEndPos-nRemovedCount+1);
|
||
nRemovedCount+=nEndPos-nStartPos+1;
|
||
}
|
||
nStartPos=-1;
|
||
}
|
||
}
|
||
|
||
nIndex++;
|
||
if(nIndex>=nSymbolCount)
|
||
break;
|
||
}
|
||
|
||
return csInputData;
|
||
}
|
||
|
||
BOOL COXMaskedEdit::SetInputData(LPCTSTR pszInputData, int nBeginPos/*=0*/,
|
||
BOOL bAllowPrompt/*=TRUE*/)
|
||
{
|
||
CString csFullInput;
|
||
// Start with existing data and append the new data.
|
||
csFullInput=GetInputData();
|
||
csFullInput=csFullInput.Left(nBeginPos);
|
||
if(bAllowPrompt)
|
||
{
|
||
csFullInput+=pszInputData;
|
||
}
|
||
else
|
||
{
|
||
// If the prompt symbol is not valid, then
|
||
// add the data one-by-one ignoring any prompt symbols.
|
||
for(; *pszInputData; pszInputData++)
|
||
{
|
||
if(*pszInputData!=m_chPromptSymbol)
|
||
csFullInput+=*pszInputData;
|
||
}
|
||
}
|
||
|
||
BOOL bCompleteSuccess=TRUE;
|
||
LPCTSTR pszReplaceData=csFullInput;
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
// Ignore everything that is not data.
|
||
if(pobjData->IsInputData())
|
||
{
|
||
// If we run out of replacement data, then use the prompt symbol.
|
||
// Make sure we iterate through the entire list so that the
|
||
// prompt symbol is applied to any empty areas.
|
||
if(*pszReplaceData)
|
||
{
|
||
// This inner while loop is so that we can re-apply input data
|
||
// after an error. This will allow us to skip over invalid
|
||
// input data and try the next character.
|
||
while(*pszReplaceData)
|
||
{
|
||
TCHAR chReplace=*pszReplaceData;
|
||
pszReplaceData++;
|
||
|
||
// Make sure to follow the input validation.
|
||
// The prompt symbol is always valid at this level.
|
||
// This allows the user to erase a string by overtyping a space.
|
||
// On error, just skip the character being inserted.
|
||
// This will allow the DeleteRange() function to have the remaining
|
||
// characters validated.
|
||
if((chReplace==m_chPromptSymbol) || pobjData->IsValidInput(chReplace))
|
||
{
|
||
pobjData->m_chValue=pobjData->PreProcessChar(chReplace);
|
||
break;
|
||
}
|
||
else
|
||
bCompleteSuccess=FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pobjData->m_chValue=m_chPromptSymbol;
|
||
}
|
||
}
|
||
}
|
||
|
||
Update();
|
||
|
||
return bCompleteSuccess;
|
||
}
|
||
|
||
TCHAR COXMaskedEdit::GetPromptSymbol()
|
||
{
|
||
return m_chPromptSymbol;
|
||
}
|
||
|
||
void COXMaskedEdit::SetPromptSymbol(TCHAR chNewPromptSymbol)
|
||
{
|
||
// The prompt symbol must be a valid edit box symbol.
|
||
ASSERT( (chNewPromptSymbol != chNULL) && (chNewPromptSymbol != chCR) &&
|
||
(chNewPromptSymbol != chLF) && (chNewPromptSymbol != 127));
|
||
|
||
if((chNewPromptSymbol != chNULL) && (chNewPromptSymbol != chCR) &&
|
||
(chNewPromptSymbol != chLF) && (chNewPromptSymbol != 127))
|
||
{
|
||
// If the prompt symbol changes,
|
||
// go through and replace the existing prompts with the new prompt.
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
if(pobjData->IsInputData())
|
||
{
|
||
if(pobjData->m_chValue==m_chPromptSymbol)
|
||
pobjData->m_chValue=chNewPromptSymbol;
|
||
}
|
||
}
|
||
m_chPromptSymbol=chNewPromptSymbol;
|
||
}
|
||
|
||
// Don't update the insertion point if we are just setting the prompt symbol.
|
||
Update(-1);
|
||
|
||
if(::IsWindow(GetSafeHwnd()))
|
||
SetModify(FALSE);
|
||
}
|
||
|
||
void COXMaskedEdit::EmptyData(BOOL bOnlyInput/*=FALSE*/)
|
||
{
|
||
if(m_listData.GetCount()==0)
|
||
{
|
||
DeleteContents();
|
||
return;
|
||
}
|
||
|
||
if(bOnlyInput)
|
||
{
|
||
// If emptying only the data, then iterate through the list
|
||
// of data and replace input data with the prompt symbol.
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
if(pobjData->IsInputData())
|
||
pobjData->m_chValue=m_chPromptSymbol;
|
||
}
|
||
}
|
||
else
|
||
DeleteContents();
|
||
|
||
Update();
|
||
}
|
||
|
||
BOOL COXMaskedEdit::IsInputEmpty()
|
||
{
|
||
if(m_listData.GetCount()==0)
|
||
{
|
||
CString csInputData;
|
||
GetWindowText(csInputData);
|
||
return csInputData.IsEmpty();
|
||
}
|
||
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
if (pobjData->IsInputData() && pobjData->m_chValue!=m_chPromptSymbol)
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL COXMaskedEdit::GetInsertMode() const
|
||
{
|
||
// The standard CEdit control does not support over-typing.
|
||
// This flag is used to manage over-typing internally.
|
||
return m_bInsertMode;
|
||
}
|
||
|
||
void COXMaskedEdit::SetInsertMode(BOOL bInsertMode)
|
||
{
|
||
// The standard CEdit control does not support over-typing.
|
||
// This flag is used to manage over-typing internally.
|
||
m_bInsertMode=bInsertMode;
|
||
}
|
||
|
||
BOOL COXMaskedEdit::GetAutoTab() const
|
||
{
|
||
// The standard CEdit control does not support AutoTab mode.
|
||
// This flag is used to manage the AutoTab mode internally.
|
||
return m_bAutoTab;
|
||
}
|
||
|
||
void COXMaskedEdit::SetAutoTab(BOOL bAutoTab)
|
||
{
|
||
// The standard CEdit control does not support AutoTab mode.
|
||
// This flag is used to manage AutoTab mode internally.
|
||
m_bAutoTab=bAutoTab;
|
||
}
|
||
|
||
CString COXMaskedEdit::ShowMask() const
|
||
{
|
||
CString csShow;
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos;)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
ASSERT_VALID( pobjData);
|
||
|
||
// There is no need to do any fancy string building because
|
||
// all validation is done when characters are inserted into the list.
|
||
// Literals and placeholders are converted properly at that time
|
||
// so all we have to do here is get the value.
|
||
csShow += pobjData->m_chValue;
|
||
}
|
||
return csShow;
|
||
}
|
||
|
||
BOOL COXMaskedEdit::IsInputData(int nPosition) const
|
||
{
|
||
if(m_listData.GetCount()==0)
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
// We frequently need to know if a position refers to
|
||
// input data or to a literal.
|
||
BOOL bIsInputData=FALSE;
|
||
if(nPosition >= 0)
|
||
{
|
||
POSITION pos=m_listData.FindIndex(nPosition);
|
||
if(pos)
|
||
{
|
||
CMaskData* pobjData=m_listData.GetAt(pos);
|
||
if(pobjData)
|
||
{
|
||
bIsInputData=pobjData->IsInputData();
|
||
}
|
||
}
|
||
}
|
||
return bIsInputData;
|
||
}
|
||
|
||
int COXMaskedEdit::DeleteRange(int nSelectionStart, int nSelectionEnd)
|
||
{
|
||
// In order to delete properly, we must count the number of
|
||
// input characters that are selected and only delete that many.
|
||
// This is because the selection can include literals.
|
||
int nCharIndex =0;
|
||
int nDeleteCount=0;
|
||
CString csInputData;
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos; nCharIndex++)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
// Ignore everything that is not data.
|
||
// This is the same as GetInputData except that we
|
||
// will ignore the input data within the selection range.
|
||
if(pobjData->IsInputData())
|
||
{
|
||
if((nCharIndex < nSelectionStart) || (nCharIndex >= nSelectionEnd))
|
||
{
|
||
// The SetInputData() function will take care of validating
|
||
// the shifted characters.
|
||
csInputData += pobjData->m_chValue;
|
||
}
|
||
else
|
||
nDeleteCount++;
|
||
}
|
||
}
|
||
// Now apply the filtered data stream.
|
||
SetInputData(csInputData);
|
||
// return the deleted count so that an error can be generated
|
||
// if nothing was deleted.
|
||
return nDeleteCount;
|
||
}
|
||
|
||
int COXMaskedEdit::InsertAt(int nSelectionStart, TCHAR chNewChar)
|
||
{
|
||
// We could have some complex, yet efficient, routine
|
||
// that would raise an error if inserting pushed an existing character
|
||
// into an invalid region. Instead, just save the current
|
||
// state and restore it on error.
|
||
CString csPreviousInput=GetInputData();
|
||
|
||
int nCharIndex=0;
|
||
int nInsertionPoint=-1;
|
||
CString csInputData;
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.GetHeadPosition(); pos; nCharIndex++)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
// Ignore everything that is not data.
|
||
// This is just like we do in GetInputData except that we
|
||
// will ignore the input data within the selection range.
|
||
if(pobjData->IsInputData())
|
||
{
|
||
// Wait until a valid insertion point and
|
||
// only make sure to insert once.
|
||
if((nInsertionPoint < 0) && (nCharIndex >= nSelectionStart))
|
||
{
|
||
csInputData += chNewChar;
|
||
nInsertionPoint=nCharIndex;
|
||
}
|
||
csInputData += pobjData->m_chValue;
|
||
}
|
||
}
|
||
// Now apply the filtered data stream and check if it was successful.
|
||
if(!SetInputData(csInputData))
|
||
{
|
||
// If not successful, then restore the previous input and return -1.
|
||
SetInputData(csPreviousInput);
|
||
return -1;
|
||
}
|
||
return nInsertionPoint;
|
||
}
|
||
|
||
int COXMaskedEdit::SetAt(int nSelectionStart, TCHAR chNewChar)
|
||
{
|
||
if(nSelectionStart >= 0)
|
||
{
|
||
POSITION pos=m_listData.FindIndex(nSelectionStart);
|
||
if(pos)
|
||
{
|
||
CMaskData* pobjData=m_listData.GetAt(pos);
|
||
if(pobjData)
|
||
{
|
||
if(pobjData->IsInputData())
|
||
{
|
||
if((chNewChar==m_chPromptSymbol) || pobjData->IsValidInput(chNewChar))
|
||
pobjData->m_chValue=pobjData->PreProcessChar(chNewChar);
|
||
else
|
||
return -1; // Input value is invalid or not allowed.
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return nSelectionStart;
|
||
}
|
||
|
||
int COXMaskedEdit::GetNextInputLocation(int nSelectionStart)
|
||
{
|
||
// One of the functions of this edit control is that it skips over literals.
|
||
// We need a function to help skip to the next position.
|
||
int nNextInputLocation=nSelectionStart;
|
||
if(nNextInputLocation < 0)
|
||
nNextInputLocation=0;
|
||
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.FindIndex(nNextInputLocation); pos; nNextInputLocation++)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
if(pobjData->IsInputData())
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
return nNextInputLocation;
|
||
}
|
||
|
||
int COXMaskedEdit::GetPreviousInputLocation(int nSelectionStart)
|
||
{
|
||
// One of the functions of this edit control is that it skips over literals.
|
||
// We need a function to help skip to the next position.
|
||
int nNextInputLocation=nSelectionStart;
|
||
if(nNextInputLocation < 0)
|
||
nNextInputLocation=0;
|
||
// Need to determine if we moved to a previous location.
|
||
// There will need to be some correction.
|
||
int nInitialInputLocation=nNextInputLocation;
|
||
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.FindIndex(nNextInputLocation); pos; nNextInputLocation--)
|
||
{
|
||
pobjData=m_listData.GetPrev(pos);
|
||
if(pobjData->IsInputData())
|
||
{
|
||
if(nInitialInputLocation != nNextInputLocation)
|
||
{
|
||
// If we find a valid previous location, then move to the right of it.
|
||
// This backup and then move forward is typical when seeking in a backwards direction.
|
||
nNextInputLocation++;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
// If there is no input data to the left of the selection,
|
||
// then seek forward to the next location.
|
||
if(nNextInputLocation < 0)
|
||
return GetNextInputLocation(nSelectionStart);
|
||
return nNextInputLocation;
|
||
}
|
||
|
||
int COXMaskedEdit::GetEmptyInputLocation(int nSelectionStart)
|
||
{
|
||
int nEmptyInputLocation=nSelectionStart;
|
||
if(nEmptyInputLocation < 0)
|
||
nEmptyInputLocation=0;
|
||
|
||
CMaskData* pobjData=NULL;
|
||
for(POSITION pos=m_listData.FindIndex(nEmptyInputLocation); pos; nEmptyInputLocation++)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
if(pobjData->IsInputData())
|
||
{
|
||
if(pobjData->m_chValue==m_chPromptSymbol)
|
||
break;
|
||
}
|
||
}
|
||
return nEmptyInputLocation;
|
||
}
|
||
|
||
void COXMaskedEdit::Update(int nSelectionStart/*=0*/)
|
||
{
|
||
// Update the edit control if it exists.
|
||
if(::IsWindow(m_hWnd))
|
||
{
|
||
m_nSetTextSemaphor++;
|
||
CString sText=ShowMask();
|
||
SetWindowText(sText);
|
||
m_nSetTextSemaphor--;
|
||
SetModify(TRUE);
|
||
// We usually need to update the insertion point.
|
||
if(nSelectionStart>=0)
|
||
UpdateInsertionPointForward(nSelectionStart);
|
||
}
|
||
}
|
||
|
||
void COXMaskedEdit::UpdateInsertionPointForward(int nSelectionStart)
|
||
{
|
||
int nNewInsertionPoint=GetNextInputLocation(nSelectionStart);
|
||
|
||
if(m_bAutoTab && nNewInsertionPoint==m_listData.GetCount())
|
||
{
|
||
CWnd* pParentWnd=GetParent();
|
||
ASSERT(pParentWnd);
|
||
CWnd* pNextTabCtrl=pParentWnd->GetNextDlgTabItem(this);
|
||
if(pNextTabCtrl && pNextTabCtrl!=this)
|
||
{
|
||
pNextTabCtrl->SetFocus();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SetSel(nNewInsertionPoint, nNewInsertionPoint);
|
||
}
|
||
}
|
||
|
||
void COXMaskedEdit::UpdateInsertionPointBackward(int nSelectionStart)
|
||
{
|
||
int nNewInsertionPoint=GetPreviousInputLocation(nSelectionStart);
|
||
SetSel(nNewInsertionPoint, nNewInsertionPoint);
|
||
}
|
||
|
||
void COXMaskedEdit::ValidationError()
|
||
{
|
||
::MessageBeep(MB_ICONEXCLAMATION);
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// COXMaskedEdit overrides
|
||
|
||
void COXMaskedEdit::PreSubclassWindow()
|
||
{
|
||
CEdit::PreSubclassWindow();
|
||
// As of 01/07/98, this masked edit control was only designed
|
||
// to handle single lines. At some point, the code can be reviewed
|
||
// to see if it can handle multiple lines.
|
||
ASSERT( !(GetStyle() & ES_MULTILINE));
|
||
|
||
_AFX_THREAD_STATE* pThreadState=AfxGetThreadState();
|
||
// hook not already in progress
|
||
if(pThreadState->m_pWndInit==NULL)
|
||
{
|
||
// This is a great place to update the control as it is
|
||
// the first function called after a successful subclass.
|
||
// Don't update if there is no data.
|
||
if(m_listData.GetCount() != 0)
|
||
Update();
|
||
}
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// COXMaskedEdit message handlers
|
||
|
||
BOOL COXMaskedEdit::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
|
||
{
|
||
// We override the create function so that we can update the text
|
||
// if a mask was provided in the constructor.
|
||
BOOL bReturn=CEdit::Create(dwStyle, rect, pParentWnd, nID);
|
||
if(bReturn)
|
||
{
|
||
// Don't update if there is no data.
|
||
if(m_listData.GetCount() != 0)
|
||
Update();
|
||
}
|
||
return bReturn;
|
||
}
|
||
|
||
void COXMaskedEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
{
|
||
// If there is no mask, then exit quickly performing the default operation.
|
||
if(m_listData.GetCount()==0 || GetStyle()&ES_READONLY)
|
||
{
|
||
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
|
||
return;
|
||
}
|
||
|
||
// 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.
|
||
BOOL bIsShiftKeyDown=::GetAsyncKeyState(VK_SHIFT)< 0;
|
||
if(nChar==VK_DELETE)
|
||
{
|
||
int nSelectionStart=0;
|
||
int nSelectionEnd =0;
|
||
GetSel(nSelectionStart, nSelectionEnd);
|
||
|
||
if(nSelectionStart<m_listData.GetCount())
|
||
{
|
||
// 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-attempt the delete until we either delete
|
||
// a character or run out of characters.
|
||
{
|
||
while (nSelectionEnd != m_listData.GetCount())
|
||
{
|
||
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 the right
|
||
// and reattempt the delete until we either delete
|
||
// a character or run out of characters.
|
||
{
|
||
while (nSelectionEnd != m_listData.GetCount())
|
||
{
|
||
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);
|
||
|
||
UpdateInsertionPointForward(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)
|
||
{
|
||
// The standard CEdit control does not support over-typing.
|
||
// This flag is used to manage over-typing internally.
|
||
BOOL bOldInsertMode=GetInsertMode();
|
||
BOOL bNewInsertMode=bOldInsertMode ? FALSE : TRUE;
|
||
|
||
SetInsertMode(bNewInsertMode);
|
||
}
|
||
else
|
||
{
|
||
CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
|
||
}
|
||
}
|
||
|
||
void COXMaskedEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
{
|
||
// If there is no mask, then exit quickly performing the default operation.
|
||
if(m_listData.GetCount()==0 || GetStyle()&ES_READONLY)
|
||
{
|
||
CEdit::OnChar(nChar, nRepCnt, nFlags);
|
||
return;
|
||
}
|
||
|
||
int nSelectionStart=0;
|
||
int nSelectionEnd =0;
|
||
GetSel(nSelectionStart, nSelectionEnd);
|
||
|
||
// If character value is above 32, then it is ANSI or Extended.
|
||
// Below 32 are control and navigation characters.
|
||
if(nChar >= 32)
|
||
{
|
||
if(nSelectionStart==nSelectionEnd)
|
||
{
|
||
if(IsInputData(nSelectionStart))
|
||
{
|
||
int nActualInsertionPoint=nSelectionStart;
|
||
if(m_bInsertMode)
|
||
nActualInsertionPoint=InsertAt(nSelectionStart, (TCHAR)nChar);
|
||
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();
|
||
|
||
UpdateInsertionPointForward(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 position
|
||
int nActualInsertionPoint=nSelectionStart;
|
||
nActualInsertionPoint=InsertAt(nSelectionStart, (TCHAR)nChar);
|
||
|
||
// InsertAt will return -1 if the character cannot be inserted here.
|
||
if(nActualInsertionPoint >= 0)
|
||
nSelectionStart=nActualInsertionPoint + 1;
|
||
else
|
||
ValidationError();
|
||
|
||
UpdateInsertionPointForward(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)
|
||
{
|
||
if (nSelectionStart >= 1)
|
||
{
|
||
while (nSelectionStart>=0)
|
||
{
|
||
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-attempt the delete until we either delete
|
||
// a character or run out of characters.
|
||
{
|
||
if (nSelectionStart >= 1)
|
||
{
|
||
while (nSelectionStart>=0)
|
||
{
|
||
nSelectionStart--; // Do the equivalent of a backspace.
|
||
|
||
if (DeleteRange(nSelectionStart, nSelectionEnd))
|
||
{
|
||
Update(nSelectionStart);
|
||
break;
|
||
}
|
||
|
||
nSelectionEnd--;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
// let edit control do its job
|
||
CEdit::OnChar(nChar, nRepCnt, nFlags);
|
||
}
|
||
}
|
||
|
||
void COXMaskedEdit::OnSetFocus(CWnd* pOldWnd)
|
||
{
|
||
CEdit::OnSetFocus(pOldWnd);
|
||
// 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==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 interference.
|
||
UpdateInsertionPointForward(0);
|
||
}
|
||
}
|
||
|
||
// v9.3 - update 03 - 64-bit - these were declared using UINT and LONG - now WPARAM and LPARAM
|
||
LRESULT COXMaskedEdit::OnCut(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
if(m_listData.GetCount()==0 || GetStyle()&ES_READONLY)
|
||
{
|
||
return CEdit::Default();
|
||
}
|
||
|
||
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 - these were declared using UINT and LONG - now WPARAM and LPARAM
|
||
LRESULT COXMaskedEdit::OnCopy(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
// Let copy do its thing and copy the selected text.
|
||
return CEdit::Default();
|
||
}
|
||
|
||
// v9.3 - update 03 - 64-bit - these were declared using UINT and LONG - now WPARAM and LPARAM
|
||
LRESULT COXMaskedEdit::OnPaste(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
if(m_listData.GetCount()==0 || GetStyle()&ES_READONLY)
|
||
{
|
||
return CEdit::Default();
|
||
}
|
||
|
||
int nSelectionStart=0;
|
||
int nSelectionEnd =0;
|
||
GetSel(nSelectionStart, nSelectionEnd);
|
||
|
||
CEdit::Default();
|
||
|
||
// This is a real dump paste routine that expects SetInputData to
|
||
// 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 csNewString;
|
||
GetWindowText(csNewString);
|
||
// It is very important that we do not allow the prompt character
|
||
// in this scenario. This is because we expect the pasted text
|
||
// to contain lots of literals and spaces.
|
||
SetInputData(csNewString, 0, FALSE);
|
||
Update(-1);
|
||
// 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=GetEmptyInputLocation(nSelectionStart);
|
||
SetSel(nNewInsertionPoint, nNewInsertionPoint);
|
||
|
||
return 0;
|
||
}
|
||
|
||
// v9.3 - update 03 - 64-bit - these were declared using UINT and LONG - now WPARAM and LPARAM
|
||
LRESULT COXMaskedEdit::OnClear(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
if(m_listData.GetCount()==0 || GetStyle()&ES_READONLY)
|
||
{
|
||
return CEdit::Default();
|
||
}
|
||
|
||
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);
|
||
|
||
// 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;
|
||
}
|
||
|
||
|
||
void COXMaskedEdit::OnKillfocus()
|
||
{
|
||
// TODO: Add your control notification handler code here
|
||
|
||
// Send OXMEN_VALIDATE notification to parent to validate typed information
|
||
// if the notification was handled. The return value have to be one of these:
|
||
//
|
||
// -1 - if typed info is invalid
|
||
// 0 - typed info is valid but virtual OnValidate function will be
|
||
// called to verify typed info
|
||
// 1 - typed info is valid and OnValidate function won't be called
|
||
|
||
CWnd* pParentWnd=GetParent();
|
||
ASSERT(pParentWnd);
|
||
|
||
MENMHDR MENMHdr;
|
||
memset(&MENMHdr,0,sizeof(MENMHdr));
|
||
MENMHdr.hdr.hwndFrom=GetSafeHwnd();
|
||
MENMHdr.hdr.idFrom=GetDlgCtrlID();
|
||
MENMHdr.hdr.code=OXMEN_VALIDATE;
|
||
MENMHdr.bValid=TRUE;
|
||
MENMHdr.bDefaultValidation=TRUE;
|
||
MENMHdr.nPosition=0;
|
||
|
||
pParentWnd->SendMessage(WM_NOTIFY,MENMHdr.hdr.idFrom,(LPARAM)&MENMHdr);
|
||
|
||
if(!MENMHdr.bValid || !(MENMHdr.bDefaultValidation ? OnValidate() : TRUE))
|
||
{
|
||
SetFocus();
|
||
ValidationError();
|
||
// set insertion point at the first input location
|
||
UpdateInsertionPointForward(MENMHdr.nPosition);
|
||
}
|
||
}
|
||
|
||
BOOL COXMaskedEdit::OnValidate()
|
||
{
|
||
// by default return TRUE
|
||
// one can overwrite this function to provide validation capability
|
||
// in COXMaskedEdit derived class
|
||
return TRUE;
|
||
}
|
||
|
||
void COXMaskedEdit::OnLButtonDown(UINT nFlags, CPoint point)
|
||
{
|
||
if(::GetFocus()!=GetSafeHwnd() && IsInputEmpty())
|
||
{
|
||
SetFocus();
|
||
UpdateInsertionPointForward(0);
|
||
}
|
||
else
|
||
{
|
||
CEdit::OnLButtonDown(nFlags,point);
|
||
}
|
||
}
|
||
|
||
// v9.3 - update 03 - 64-bit - these were declared using UINT and LONG - now WPARAM and LPARAM
|
||
LRESULT COXMaskedEdit::OnSetText(WPARAM wParam, LPARAM lParam)
|
||
{
|
||
UNREFERENCED_PARAMETER(wParam);
|
||
UNREFERENCED_PARAMETER(lParam);
|
||
|
||
if(m_listData.GetCount()==0 || GetStyle()&ES_READONLY)
|
||
{
|
||
return CEdit::Default();
|
||
}
|
||
|
||
CString csNewString=(LPCTSTR)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);
|
||
|
||
csNewString=GetInputData(csNewString);
|
||
// check if secified text is the same as input data
|
||
CString sInputData=GetInputData();
|
||
if(csNewString.Compare(sInputData)==0)
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
m_bNotifyParent=FALSE;
|
||
SetInputData(csNewString,0,TRUE);
|
||
m_bNotifyParent=TRUE;
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
int COXMaskedEdit::RPtoLP(int nRealPos) const
|
||
{
|
||
// All COXMaskedEdit functions that take a cursor position as argument interpret it
|
||
// as a real position within the edit control (taking into account all symbols including
|
||
// literals). But sometimes we want to know which non-literal symbol is at a
|
||
// particular real position. In that case this function is really useful.
|
||
|
||
if(nRealPos<0 || nRealPos>=m_listData.GetCount())
|
||
return -1;
|
||
|
||
int nLogicalPos=-1;
|
||
CMaskData* pobjData=NULL;
|
||
int nNextInputLocation=0;
|
||
for(POSITION pos=m_listData.FindIndex(nNextInputLocation); pos; nNextInputLocation++)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
if(pobjData->IsInputData())
|
||
{
|
||
nLogicalPos++;
|
||
}
|
||
if(nNextInputLocation==nRealPos)
|
||
{
|
||
return pobjData->IsInputData() ? nLogicalPos : -1;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
int COXMaskedEdit::LPtoRP(int nLogicalPos) const
|
||
{
|
||
// All COXMaskedEdit functions that take a cursor position as an argument interpret it
|
||
// as a real position within the edit control (taking into account all symbols including
|
||
// literals). But sometimes we want to set the cursor at a position before or after a
|
||
// particular non-literal symbol. In that case this function is really useful.
|
||
|
||
if(nLogicalPos<0 || nLogicalPos>=m_listData.GetCount())
|
||
return -1;
|
||
|
||
int nRealPos=-1;
|
||
int nNonLiterals=-1;
|
||
CMaskData* pobjData=NULL;
|
||
int nNextInputLocation=0;
|
||
for(POSITION pos=m_listData.FindIndex(nNextInputLocation); pos; nNextInputLocation++)
|
||
{
|
||
pobjData=m_listData.GetNext(pos);
|
||
nRealPos++;
|
||
if(pobjData->IsInputData())
|
||
{
|
||
nNonLiterals++;
|
||
if(nNonLiterals==nLogicalPos)
|
||
{
|
||
return nRealPos;
|
||
}
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
|
||
BOOL COXMaskedEdit::NotifyParent(UINT nNotificationID)
|
||
{
|
||
CWnd* pParentWnd=GetParent();
|
||
if(pParentWnd==NULL)
|
||
return FALSE;
|
||
|
||
pParentWnd->SendMessage(WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(),nNotificationID),
|
||
(LPARAM)GetSafeHwnd());
|
||
return TRUE;
|
||
}
|
||
|
||
CString COXMaskedEdit::GetText()
|
||
{
|
||
if (!::IsWindow(m_hWnd))
|
||
return _T("");
|
||
|
||
CString strText;
|
||
GetWindowText(strText);
|
||
return strText;
|
||
}
|
||
|
||
|
||
void COXMaskedEdit::SetText(LPCTSTR lpszText)
|
||
{
|
||
if (!::IsWindow(m_hWnd))
|
||
return;
|
||
|
||
SetWindowText(lpszText);
|
||
} |