📄 amsedit.cpp
字号:
{
if (_ShouldEnter(c))
{
if (strText[nStart] == m_cGroupSeparator)
nStart++;
nEnd = (nEnd == nLen ? nEnd : (nStart + 1));
m_pEdit->SetSel(nStart, nEnd);
m_pEdit->ReplaceSel(CString(c), TRUE);
}
return;
}
bNeedAdjustment = true;
}
}
// Check if it's a non-printable character, such as Backspace or Ctrl+C
else if (!_istprint(c))
bNeedAdjustment = true;
else
return;
// Check if the character should be entered
if (!_ShouldEnter(c))
return;
Behavior::_OnChar(uChar, nRepCnt, nFlags);
// If the decimal point was added/removed or a separator needs adding/removing, adjust the text
if (bNeedAdjustment)
AdjustSeparators(nSepCount);
}
// Handles the WM_KEYDOWN message, which is called when the user enters a special character such as Delete or the arrow keys.
void CAMSEdit::NumericBehavior::_OnKeyDown(UINT uChar, UINT nRepCnt, UINT nFlags)
{
switch (uChar)
{
case VK_DELETE:
{
int nStart = 0, nEnd = 0;
m_pEdit->GetSel(nStart, nEnd);
CString strText = m_pEdit->GetText();
int nLen = strText.GetLength();
// If deleting the prefix, don't allow it if there's a number after it.
int nPrefixLen = m_strPrefix.GetLength();
if (nStart < nPrefixLen && nLen > nPrefixLen)
{
if (nEnd == nLen)
break;
return;
}
if (nStart < nLen && strText[nStart] == m_cGroupSeparator && nStart == nEnd)
Behavior::_OnKeyDown(VK_RIGHT, nRepCnt, nFlags);
// Allow the deletion and then adjust the value
int nSepCount = GetGroupSeparatorCount(strText);
Behavior::_OnKeyDown(uChar, nRepCnt, nFlags);
AdjustSeparators(nSepCount);
// If everything on the right was deleted, put the selection on the right
if (nEnd == nLen)
m_pEdit->SetSel(nStart, nStart);
return;
}
}
Behavior::_OnKeyDown(uChar, nRepCnt, nFlags);
}
// Handles the WM_KILLFOCUS message, which is called when the user leaves the control.
// It's used here to check if zeros need to be added to the value.
void CAMSEdit::NumericBehavior::_OnKillFocus(CWnd* pNewWnd)
{
Behavior::_OnKillFocus(pNewWnd);
// Check if the value is empty and we don't want to touch it
CString strOriginalText = GetNumericText(m_pEdit->GetText());
CString strText = strOriginalText;
int nLen = strText.GetLength();
// If desired, remove any extra leading zeros but always leave one in front of the decimal point
if (m_uFlags & OnKillFocus_RemoveExtraLeadingZeros && nLen > 0)
{
bool bIsNegative = (strText[0] == m_cNegativeSign);
if (bIsNegative)
strText.Delete(0);
strText.TrimLeft('0');
if (strText.IsEmpty() || strText[0] == m_cDecimalPoint)
strText.Insert(0, '0');
if (bIsNegative)
strText.Insert(0, m_cNegativeSign);
}
else if (!(m_uFlags & OnKillFocus_Max) || (nLen == 0 && m_uFlags & OnKillFocus_DontPadWithZerosIfEmpty))
return;
int nDecimalPos = strText.Find(m_cDecimalPoint);
// Check if we need to pad the number with zeros after the decimal point
if (m_uFlags & OnKillFocus_PadWithZerosAfterDecimal && m_nMaxDecimalPlaces > 0)
{
if (nDecimalPos < 0)
{
if (nLen == 0 || strText == '-')
{
strText = '0';
nLen = 1;
}
strText += m_cDecimalPoint;
nDecimalPos = nLen++;
}
InsertZeros(&strText, -1, m_nMaxDecimalPlaces - (nLen - nDecimalPos - 1));
}
// Check if we need to pad the number with zeros before the decimal point
if (m_uFlags & OnKillFocus_PadWithZerosBeforeDecimal && m_nMaxWholeDigits > 0)
{
if (nDecimalPos < 0)
nDecimalPos = nLen;
if (nLen && strText[0] == '-')
nDecimalPos--;
InsertZeros(&strText, (nLen ? strText[0] == '-' : -1), m_nMaxWholeDigits - nDecimalPos);
}
if (strText != strOriginalText)
{
SelectionSaver selection = m_pEdit; // remember the current selection
m_pEdit->SetWindowText(strText);
}
// If it's empty, take action based on the flag
if (strText.IsEmpty())
{
if (m_uFlags & OnKillFocus_Beep_IfEmpty)
MessageBeep(MB_ICONEXCLAMATION);
if (m_uFlags & OnKillFocus_SetValid_IfEmpty)
AdjustWithinRange();
if ((m_uFlags & OnKillFocus_ShowMessage_IfEmpty) == OnKillFocus_ShowMessage_IfEmpty)
ShowErrorMessage();
if (m_uFlags & OnKillFocus_SetFocus_IfEmpty)
m_pEdit->SetFocus();
return;
}
if (!IsValid())
{
if (m_uFlags & OnKillFocus_Beep_IfInvalid)
MessageBeep(MB_ICONEXCLAMATION);
if (m_uFlags & OnKillFocus_SetValid_IfInvalid)
AdjustWithinRange();
if ((m_uFlags & OnKillFocus_ShowMessage_IfInvalid) == OnKillFocus_ShowMessage_IfInvalid)
ShowErrorMessage();
if (m_uFlags & OnKillFocus_SetFocus_IfInvalid)
m_pEdit->SetFocus();
}
}
// Returns true if the control's value is valid and falls within the allowed range.
bool CAMSEdit::NumericBehavior::IsValid() const
{
double dValue = GetDouble();
return (dValue >= m_dMin && dValue <= m_dMax);
}
// Returns true if the control's value is valid and falls within the allowed range.
// If bShowErrorIfNotValid is true, an error message box is shown and the control gets the focus.
bool CAMSEdit::NumericBehavior::CheckIfValid(bool bShowErrorIfNotValid /*= true*/)
{
if (!m_pEdit->IsWindowEnabled())
return true;
bool bValid = IsValid();
if (!bValid && bShowErrorIfNotValid)
{
ShowErrorMessage();
m_pEdit->SetFocus();
}
return bValid;
}
// Shows a message box informing the user to enter a valid value within the allowed range.
void CAMSEdit::NumericBehavior::ShowErrorMessage() const
{
CString strMessage = _T("Please specify a valid numeric value.");
if (m_dMin > AMS_MIN_NUMBER && m_dMax < AMS_MAX_NUMBER)
strMessage = _T("Please specify a numeric value between ") + GetDoubleText(m_dMin) + _T(" and ") + GetDoubleText(m_dMax) + _T(".");
else if (m_dMin > AMS_MIN_NUMBER)
strMessage = _T("Please specify a numeric value greater than or equal to ") + GetDoubleText(m_dMin) + _T(".");
else if (m_dMax < AMS_MAX_NUMBER)
strMessage = _T("Please specify a numeric value less than or equal to ") + GetDoubleText(m_dMax) + _T(".");
AfxMessageBox(strMessage, MB_ICONEXCLAMATION);
}
// Adjusts the control's value to be within the range of allowed numeric values.
void CAMSEdit::NumericBehavior::AdjustWithinRange()
{
// Check if it's already within the range
if (IsValid())
return;
// If it's empty, set it a valid number
if (m_pEdit->GetText().IsEmpty())
m_pEdit->SetWindowText(_T(" "));
else
_Redraw();
// Make it fall within the range
double dValue = GetDouble();
if (dValue < m_dMin)
SetDouble(m_dMin);
else if (dValue > m_dMax)
SetDouble(m_dMax);
}
#endif // (AMSEDIT_COMPILED_CLASSES & AMSEDIT_NUMERIC_CLASS)
#if (AMSEDIT_COMPILED_CLASSES & AMSEDIT_DATE_CLASS)
/////////////////////////////////////////////////////////////////////////////
// CAMSEdit::DateBehavior
// Constructs the object with the given control.
CAMSEdit::DateBehavior::DateBehavior(CAMSEdit* pEdit) :
Behavior(pEdit),
m_dateMin(AMS_MIN_OLEDATETIME),
m_dateMax(AMS_MAX_OLEDATETIME),
m_cSep('/')
{
// Get the system's date separator
if (::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, NULL, 0))
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, &m_cSep, sizeof(m_cSep));
// Determine if the day should go before the month
TCHAR szShortDate[MAX_PATH];
if (::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, NULL, 0))
{
::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, szShortDate, sizeof(szShortDate)/sizeof(TCHAR));
for (int iPos = 0; szShortDate[iPos]; iPos++)
{
TCHAR c = static_cast<TCHAR>(_totupper(szShortDate[iPos]));
if (c == 'M') // see if the month is first
break;
if (c == 'D') // see if the day is first, and then set the flag
{
m_uFlags |= DayBeforeMonth;
break;
}
}
}
}
// Handles the WM_CHAR message, which is called when the user enters a regular character or Backspace
void CAMSEdit::DateBehavior::_OnChar(UINT uChar, UINT nRepCnt, UINT nFlags)
{
// Check to see if it's read only
if (m_pEdit->IsReadOnly())
return;
TCHAR c = static_cast<TCHAR>(uChar);
int nStart, nEnd;
m_pEdit->GetSel(nStart, nEnd);
CString strText = m_pEdit->GetText();
int nLen = strText.GetLength();
// Check for a non-printable character (such as Ctrl+C)
if (!_istprint(c))
{
if (c == VK_BACK && nStart != nLen)
{
m_pEdit->SendMessage(WM_KEYDOWN, VK_LEFT); // move the cursor left
return;
}
// Allow backspace only if the cursor is all the way to the right
if (_ShouldEnter(c))
Behavior::_OnChar(uChar, nRepCnt, nFlags);
return;
}
// Add the digit depending on its location
switch (nStart)
{
case 0: // FIRST DIGIT
{
if (m_uFlags & DayBeforeMonth)
{
if (IsValidDayDigit(c, 0) && _ShouldEnter(c))
{
if (nLen > nStart)
{
m_pEdit->SetSel(nStart, nStart + 1);
m_pEdit->ReplaceSel(CString(c), TRUE);
if (nLen > nStart + 1)
{
if (!IsValidDay(GetDay()))
{
m_pEdit->SetSel(nStart + 1, nStart + 2);
m_pEdit->ReplaceSel(CString(GetMinDayDigit(1)), TRUE);
m_pEdit->SetSel(nStart + 1, nStart + 2);
}
}
}
else
Behavior::_OnChar(uChar, nRepCnt, nFlags);
}
// Check if we can insert the digit with a leading zero
else if (nLen == nStart && GetMinDayDigit(0) == '0' && IsValidDayDigit(c, 1) && _ShouldEnter(c))
{
m_pEdit->SetSel(nStart, nStart + 2);
m_pEdit->ReplaceSel(CString('0') + c, TRUE);
}
}
else
{
if (IsValidMonthDigit(c, 0) && _ShouldEnter(c))
{
if (nLen > nStart)
{
m_pEdit->SetSel(nStart, nStart + 1);
m_pEdit->ReplaceSel(CString(c), TRUE);
if (nLen > nStart + 1)
{
if (!IsValidMonth(GetMonth()))
{
m_pEdit->SetSel(nStart + 1, nStart + 2);
m_pEdit->ReplaceSel(CString(GetMinMonthDigit(1)), TRUE);
m_pEdit->SetSel(nStart + 1, nStart + 2);
}
}
AdjustMaxDay();
}
else
Behavior::_OnChar(uChar, nRepCnt, nFlags);
}
// Check if we can insert the digit with a leading zero
else if (nLen == nStart && GetMinMonthDigit(0) == '0' && IsValidMonthDigit(c, 1) && _ShouldEnter(c))
{
m_pEdit->SetSel(nStart, nStart + 2);
m_pEdit->ReplaceSel(CString('0') + c, TRUE);
}
}
break;
}
case 1: // SECOND DIGIT
{
if (m_uFlags & DayBeforeMonth)
{
if (IsValidDayDigit(c, 1) && _ShouldEnter(c))
{
if (nLen > nStart)
{
m_pEdit->SetSel(nStart, nStart + 1);
m_pEdit->ReplaceSel(CString(c), TRUE);
}
else
Behavior::_OnChar(uChar, nRepCnt, nFlags);
}
// Check if it's a slash and the first digit (preceded by a zero) is a valid month
else if (c == m_cSep && nLen == nStart && GetMinDayDigit(0) == '0' && IsValidDay(_ttoi(CString('0') + strText[0])) && _ShouldEnter(c))
{
m_pEdit->SetSel(0, nStart);
m_pEdit->ReplaceSel(CString('0') + strText[0] + c, TRUE);
}
}
else
{
if (IsValidMonthDigit(c, 1) && _ShouldEnter(c))
{
if (nLen > nStart)
{
m_pEdit->SetSel(nStart, nStart + 1);
m_pEdit->ReplaceSel(CString(c), TRUE);
if (GetDay() > 0 && AdjustMaxDay())
m_pEdit->SetSel(GetDayStartPosition(), GetDayStartPosition() + 2);
}
else
Behavior::_OnChar(uChar, nRepCnt, nFlags);
}
// Check if it's a slash and the first digit (preceded by a zero) is a valid month
else if (c == m_cSep && nLen == nStart && GetMinMonthDigit(0) == '0' && IsValidMonth(_ttoi(CString('0') + strText[0])) && _ShouldEnter(c))
{
m_pEdit->SetSel(0, nStart);
m_pEdit->ReplaceSel(CString('0') + strText[0] + c, TRUE);
}
}
break;
}
case 2: // FIRST SLASH
{
int nSlash = 0;
if (c == m_cSep)
nSlash = 1;
else
{
if (m_uFlags & DayBeforeMonth)
nSlash = (IsValidMonthDigit(c, 0) || (nLen == nStart && GetMinMonthDigit(0) == '0' && IsValidMonthDigit(c, 1))) ? 2 : 0;
else
nSlash = (IsValidDayDigit(c, 0) || (nLen == nStart && GetMinDayDigit(0) == '0' && IsValidDayDigit(c, 1))) ? 2 : 0;
}
// If we need the slash, enter it
if (nSlash && _ShouldEnter(c))
{
m_pEdit->SetSel(nStart, nStart + 1, FALSE);
m_pEdit->ReplaceSel(CString(m_cSep), TRUE);
}
// If the slash is to be preceded by a valid digit, "type" it in.
if (nSlash == 2)
keybd_event((BYTE)c, 0, 0, 0);
break;
}
case 3: // THIRD DIGIT
{
if (m_uFlags & DayBeforeMonth)
{
if (IsValidMonthDigit(c, 0) && _ShouldEnter(c))
{
if (nLen > nStart)
{
m_pEdit->SetSel(nStart, nStart + 1);
m_pEdit->ReplaceSel(CString(c), TRUE);
if (nLen > nStart + 1)
{
if (!IsValidMonth(GetMonth()))
{
m_pEdit->SetSel(nStart + 1, nStart + 2);
m_pEdit->ReplaceSel(CString(GetMinMonthDigit(1)), TRUE);
m_pEdit->SetSel(nStart + 1, nStart + 2);
}
}
}
else
Behavior::_OnChar(uChar, nRepCnt, nFlags);
AdjustMaxDay();
}
// Check if we can insert the digit with a leading zero
else if (nLen == nStart && GetMinMonthDigit(0) == '0' && IsValidMonthDigit(c, 1) && _ShouldEnter(c))
{
m_pEdit->SetSel(nStart, nStart + 2);
m_pEdit->ReplaceSel(CString('0') + c, TRUE);
AdjustMaxDay();
}
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -