/** * @file XTPMaskEditT.h * * @copyright * (c) 1998-2025 Codejock Software, All Rights Reserved. * * This source file is the property of Codejock Software and must not be * redistributed by any means without the explicit written permission of * Codejock Software. * * The use of this source code is governed by the terms and conditions specified * in the Toolkit Pro license agreement. Codejock Software grants you, as a * single software developer, the limited right to use this software on one * computer only. * * Contact Information: * support@codejock.com * http://www.codejock.com * */ /** @cond */ #if !defined(__XTMASKEDITEX_H__) # define __XTMASKEDITEX_H__ /** @endcond */ # if _MSC_VER >= 1000 # pragma once # endif // _MSC_VER >= 1000 # include "Common/XTPCasting.h" # include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" // deprecated # define CXTMaskEditT CXTPMaskEditT /** * @brief * CXTPMaskEditT is a template class. It allows text masking to be * applied to the control to format it for special editing restrictions. */ template class CXTPMaskEditT : public TBase { public: /** * @brief * Constructs a CXTPMaskEditT object */ CXTPMaskEditT() : m_nStartChar(0) , m_nEndChar(0) , m_bUseMask(TRUE) , m_bOverType(FALSE) , m_bRedo(FALSE) , m_bModified(FALSE) , m_chPrompt(_T('_')) , m_strMask(_T("")) , m_strDefault(_T("")) , m_strUndoBuffer(_T("")) , m_strWindowText(_T("")) , m_strLiteral(_T("")) , m_bUpdateUndo(TRUE) { } public: /** * @brief * Call this member function to enable or disable the mask for the mask * edit control. * @param bUseMask TRUE to enable the mask. FALSE to disable the mask. */ void SetUseMask(BOOL bUseMask) { m_bUseMask = bUseMask; } /** * @brief * This member function is called to determine if the mask for the edit * control can be used. * @return * TRUE if the mask can be used, otherwise returns FALSE. */ BOOL CanUseMask() const { return m_bUseMask && TBase::m_hWnd && ((TBase::GetStyle() & ES_READONLY) == 0) && !m_strMask.IsEmpty(); } /** * @brief * This member function is called to enable or disable type over, also * known as insert mode. * @param bOverType TRUE to enable type over. */ void SetOverType(BOOL bOverType) { m_bOverType = bOverType; } /** * @brief * This member function is called to determine if type over has been enabled. * @return * TRUE if type over is enabled, otherwise returns FALSE. */ BOOL CanOverType() const { return m_bOverType; } /** * @brief * This member function is called to determine if the index specified * by 'iPos' is a valid index for the currently displayed edit text. * @param iPos Index of the character to check. * @return * TRUE if the index is valid, otherwise returns FALSE. */ BOOL PosInRange(int iPos) const { return ((iPos >= 0) && (iPos < m_strLiteral.GetLength())); } /** * @brief * This member function retrieves the character that is currently used as * the mask prompt. The mask prompt indicates that the field is editable. * @return * A TCHAR data type. */ TCHAR GetPromptChar() const { return m_chPrompt; } /** * @brief * This helper member function retrieves the string contains nLength prompt characters. * @param nLength Length of string to create. * @return * CString contains nLength prompt characters */ CString GetPromptString(int nLength) const { CString strPrompt; while (nLength--) strPrompt += m_chPrompt; return strPrompt; } /** * @brief * This member function is called to set the prompt character that is * displayed to the user that indicates the field can be edited. * @param ch A TCHAR data type. * @param bAutoReplace TRUE to auto replace */ void SetPromptChar(TCHAR ch, BOOL bAutoReplace = TRUE) { if (m_chPrompt == ch) return; if (bAutoReplace) { GetMaskState(); for (int i = 0; i < m_strLiteral.GetLength(); i++) if (m_strLiteral[i] == m_chPrompt) m_strLiteral.SetAt(i, ch); for (int j = 0; j < m_strWindowText.GetLength(); j++) if (m_strWindowText[j] == m_chPrompt) m_strWindowText.SetAt(j, ch); SetMaskState(); } m_chPrompt = ch; } /** * @brief * This member function is called to perform a cut operation using the * currently selected text. * @return TRUE if successful, FALSE otherwise. */ afx_msg BOOL MaskCut() { if (!CanUseMask()) return static_cast(TBase::DefWindowProc(WM_CUT, 0, 0)); MaskCopy(); MaskClear(); return TRUE; } /** * @brief * This member function is called to perform a copy operation using the * currently selected text. * @return TRUE if successful, FALSE otherwise. */ afx_msg BOOL MaskCopy() { if (!CanUseMask()) return static_cast(TBase::DefWindowProc(WM_COPY, 0, 0)); GetMaskState(); CString strMaskedText = GetMaskedText(m_nStartChar, m_nEndChar); CopyToClipboard(strMaskedText); return TRUE; } /** * @brief * This member function is called to perform a replace operation using the * currently selected text. * @param lpszNewText Text to replace. */ void MaskReplaceSel(LPCTSTR lpszNewText) { _ASSERTE(CanUseMask()); if (m_nStartChar != m_nEndChar) MaskDeleteSel(); int x = m_nStartChar, nNewTextLen = (int)_tcslen(lpszNewText); int nWindowTextLen = m_strWindowText.GetLength(); if (x >= nWindowTextLen) return; for (int i = 0; i < nNewTextLen; ++i) { TCHAR ch = lpszNewText[i]; if (ch == m_chPrompt || CheckChar(ch, x)) { InsertCharAt(x, ch); x++; while (x < nWindowTextLen && !IsPromptPos(x)) x++; if (x >= m_strWindowText.GetLength()) break; } } CorrectPosition(x); m_nStartChar = m_nEndChar = x; } /** * @brief * This member function is called to perform a paste operation using the * current clipboard text. * @return TRUE if successful, FALSE otherwise. */ afx_msg BOOL MaskPaste() { if (!CanUseMask()) return static_cast(TBase::DefWindowProc(WM_PASTE, 0, 0)); GetMaskState(); if (!TBase::OpenClipboard()) return FALSE; # ifndef _UNICODE HGLOBAL hglbPaste = ::GetClipboardData(CF_TEXT); # else HGLOBAL hglbPaste = ::GetClipboardData(CF_UNICODETEXT); # endif if (hglbPaste != NULL) { TCHAR* lpszClipboard = (TCHAR*)GlobalLock(hglbPaste); MaskReplaceSel(lpszClipboard); GlobalUnlock(hglbPaste); SetMaskState(); } ::CloseClipboard(); return TRUE; } /** * @brief * This member function is called to delete the selection. */ void MaskDeleteSel() { if (m_nStartChar == m_nEndChar) return; CString strMaskedText = GetMaskedText(m_nEndChar); SetMaskedText(strMaskedText, m_nStartChar, FALSE); m_nEndChar = m_nStartChar; } /** * @brief * This member function is called to clear the current text selection. * @return TRUE if successful, FALSE otherwise. */ afx_msg BOOL MaskClear() { if (!CanUseMask()) return static_cast(TBase::DefWindowProc(WM_CLEAR, 0, 0)); GetMaskState(); MaskDeleteSel(); SetMaskState(); return TRUE; } /** * @brief * This member function is called to undo the previous action. * @return TRUE if successful, FALSE otherwise. */ afx_msg BOOL MaskUndo() { if (!CanUseMask()) { return static_cast(TBase::DefWindowProc(EM_UNDO, 0, 0)); } else { MaskGetSel(); if (m_bRedo) { TBase::SetWindowText(m_strRedoBuffer); } else { TBase::GetWindowText(m_strRedoBuffer); TBase::SetWindowText(m_strUndoBuffer); } m_bRedo = !m_bRedo; m_bModified = TRUE; m_nEndChar = m_nStartChar; TBase::SetSel(m_nStartChar, m_nEndChar); } return TRUE; } /** * @brief * This member function is called to select all text in the mask edit * control. */ afx_msg void MaskSelectAll() { if (!CanUseMask()) { TBase::SetSel(0, -1); } else { m_nStartChar = 0; CorrectPosition(m_nStartChar); TBase::SetSel(m_nStartChar, -1); } } /** * @brief * This member function is called to determine if the text has been modified. * @return * TRUE if the text has changed, otherwise returns FALSE. */ BOOL IsModified() const { return m_bModified; } /** * @brief * This method called to set masked text for the edit control. * @param lpszMaskedText Text string without mask. * @param iPos Start position from which current masked text * will be updated. * Default value 0 (the first position). * @param bUpdateWindow If TRUE edit control window text and selection * will be updated, otherwise only internal members * take changes. * @details * If a mask is used, then the mask will be applied to the text * in lpszMaskedText. */ void SetMaskedText(LPCTSTR lpszMaskedText, int iPos = 0, BOOL bUpdateWindow = TRUE) { int nMaskedTextLength = (int)_tcslen(lpszMaskedText); m_strWindowText = m_strWindowText.Left(iPos); int nIndex = 0; for (; (iPos < m_strLiteral.GetLength()) && (nIndex < nMaskedTextLength); iPos++) { TCHAR uChar = lpszMaskedText[nIndex]; if (IsPromptPos(iPos) && ((uChar == m_chPrompt) || ProcessMask(uChar, iPos))) { m_strWindowText += (TCHAR)uChar; nIndex++; } else { m_strWindowText += m_strLiteral[iPos]; } } if (bUpdateWindow) { SetMaskState(FALSE); } else { CorrectWindowText(); } } /** * @brief * This member function will set the mask for the edit control. * @param lpszMask The format for the mask field. For example, if you wanted to set * the mask for a phone number, and you only wanted digits to be entered, * your mask might look like this; _T("(000) 000-0000"). * @param lpszLiteral The literal format is entered here. Wherever you place an underscore * ('_') is where the user will be allowed to enter data only. Using * the phone number example; _T("(___) ___-____"). * @param lpszDefault Text that is to be displayed when the control is initialized. For * example; _T("(800) 555-1212"). If NULL, 'lpszLiteral' is used to initialize * the edit text. * @details * The values that can be set are: *
	 * Mask Character         Description
	 * ---------------------  ------------------------
	 * 0                      Numeric (0-9)
	 * 9                      Numeric (0-9) or space (' ')
	 * #                      Numeric (0-9) or space (' ') or ('+') or ('-')
	 * L                      Alpha (a-Z)
	 * ?                      Alpha (a-Z) or space (' ')
	 * A                      Alpha numeric (0-9 and a-Z)
	 * a                      Alpha numeric (0-9 and a-Z) or space (' ')
	 * &                      All print character only
	 * H                      Uppercase hex digit (0-9 and A-F)
	 * h                      Lowercase hex digit (0-9 and a-f)
	 * X                      Uppercase hex digit (0-9 and A-F) and space (' ')
	 * x                      Lowercase hex digit (0-9 and a-f) and space (' ')
	 * >                      Forces characters to upper case (A-Z) or any Unicode character
	 * <                      Forces characters to lower case (a-z) or any Unicode character
	 * C                      Uppercase ASCII character
	 * c                      Lowercase ASCII character
	 * 
* @return TRUE if successful, FALSE otherwise. */ virtual BOOL SetEditMask(LPCTSTR lpszMask, LPCTSTR lpszLiteral, LPCTSTR lpszDefault = NULL) { _ASSERTE(lpszMask); _ASSERTE(lpszLiteral); // initialize the mask for the control. m_strMask = lpszMask; m_strLiteral = lpszLiteral; _ASSERTE(m_strMask.GetLength() == m_strLiteral.GetLength()); if (m_strMask.GetLength() != m_strLiteral.GetLength()) return FALSE; if (lpszDefault == NULL) { m_strWindowText = m_strDefault = lpszLiteral; } else { m_strWindowText = m_strDefault = lpszDefault; if (m_strDefault.GetLength() != m_strLiteral.GetLength()) { SetMaskedText(m_strDefault, 0, FALSE); m_strDefault = m_strWindowText; } } _ASSERTE(m_strWindowText.GetLength() == m_strLiteral.GetLength()); // set the window text for the control. m_bRedo = FALSE; m_bModified = FALSE; TBase::SetWindowText(m_strWindowText); m_strUndoBuffer = m_strWindowText; return TRUE; } /** * @brief * Converts character to Upper/Lower case. * @param nChar Char to be converted * @param bUpperCase TRUE to convert to upper case * @return * Converted character. */ TCHAR ConvertUnicodeAlpha(TCHAR nChar, BOOL bUpperCase) const { CString strTemp(nChar); if (bUpperCase) strTemp.MakeUpper(); else strTemp.MakeLower(); return strTemp[0]; } /** * @brief * This member function is used internally to validate the character indicated * by 'nChar'. * @param nChar Contains the character code value of the key. * @param nPos Sting length. * @return * TRUE if successful, otherwise returns FALSE. */ virtual BOOL CheckChar(TCHAR& nChar, int nPos) { // do not use mask if (!CanUseMask()) return FALSE; // control character, OK if (!IsPrintChar(nChar)) return TRUE; // make sure the string is not longer than the mask if (nPos >= m_strMask.GetLength()) return FALSE; if (!IsPromptPos(nPos)) return FALSE; return ProcessMask(nChar, nPos); } /** * @brief * This member function is used internally to process the character passed * in by 'nChar' whose index is specified by 'nEndPos'. * @param nChar Contains the character code value of the key. * @param nEndPos Index of character in display string. * @return * TRUE if successful, otherwise returns FALSE. */ virtual BOOL ProcessMask(TCHAR& nChar, int nEndPos) { _ASSERTE(nEndPos < m_strMask.GetLength()); if (nEndPos < 0 || nEndPos >= m_strMask.GetLength()) return FALSE; // check the key against the mask wint_t tChar = static_cast(nChar); switch (m_strMask.GetAt(nEndPos)) { case '0': // digit only //completely changed this return ('0' <= tChar && tChar <= '9'); case '9': // digit or space return ('0' <= tChar && tChar <= '9') || _istspace(tChar); case '#': // digit or space or '+' or '-' return ('0' <= tChar && tChar <= '9') || _istspace(tChar) || tChar == _T('-') || tChar == _T('+'); case 'd': // decimal return ('0' <= tChar && tChar <= '9') || _istspace(tChar) || tChar == _T('-') || tChar == _T('+') || tChar == _T('.') || tChar == _T(','); case 'L': // alpha only return IsAlphaChar(nChar); case '?': // alpha or space return IsAlphaChar(nChar) || _istspace(tChar); case 'A': // alpha numeric only return _istalnum(tChar) || IsAlphaChar(nChar); case 'a': // alpha numeric or space return _istalnum(tChar) || IsAlphaChar(nChar) || _istspace(tChar); case '&': // all print character only return IsPrintChar(nChar); case 'H': // hex digit uppercase return ('0' <= tChar && tChar <= '9') || ('A' <= tChar && tChar <= 'F') || ('a' <= tChar && tChar <= 'f' ? (nChar ^= 0x20, TRUE) : FALSE); case 'h': // hex digit lowercase return ('0' <= tChar && tChar <= '9') || ('a' <= tChar && tChar <= 'f') || ('A' <= tChar && tChar <= 'F' ? (nChar ^= 0x20, TRUE) : FALSE); case 'X': // uppercase hex digit or space return ('0' <= tChar && tChar <= '9') || ('A' <= tChar && tChar <= 'F') || ('a' <= tChar && tChar <= 'f' ? (nChar ^= 0x20, TRUE) : FALSE) || _istspace(tChar); case 'x': // lowercase hex digit or space return ('0' <= tChar && tChar <= '9') || ('a' <= tChar && tChar <= 'f') || ('A' <= tChar && tChar <= 'F' ? (nChar ^= 0x20, TRUE) : FALSE) || _istspace(tChar); case 'C': // Uppercase ASCII character return ('a' <= tChar && tChar <= 'z' ? (nChar ^= 0x20, TRUE) : 'A' <= tChar && tChar <= 'Z'); case 'c': // Lowercase ASCII character return ('A' <= tChar && tChar <= 'Z' ? (nChar ^= 0x20, TRUE) : 'a' <= tChar && tChar <= 'z'); case '>': if (IsAlphaChar(nChar)) { nChar = ConvertUnicodeAlpha(nChar, TRUE); return TRUE; } return FALSE; case '<': if (IsAlphaChar(nChar)) { nChar = ConvertUnicodeAlpha(nChar, FALSE); return TRUE; } return FALSE; } return FALSE; } public: /** * @brief * Used by class CWinApp to translate window messages before they are dispatched to * theTranslateMessage andDispatchMessage Windows functions. * @param pMsg Points to a MSG structure that contains the message to process. * @return * Nonzero if the message was translated and should not be dispatched; 0 if the message was * not translated and should be dispatched. */ virtual BOOL PreTranslateMessage(MSG* pMsg) { if (!CanUseMask()) return TBase::PreTranslateMessage(pMsg); // intercept Ctrl+C (copy), Ctrl+V (paste), Ctrl+X (cut) and Ctrl+Z (undo) // before CEdit base class gets a hold of them. if (pMsg->message == WM_KEYDOWN) { if (::GetKeyState(VK_SUBTRACT) < 0) { OnChar('-', 1, 1); return TRUE; } if (::GetKeyState(VK_ADD) < 0) { OnChar('+', 1, 1); return TRUE; } if (::GetKeyState(VK_CONTROL) < 0) { switch (pMsg->wParam) { case 'X': case 'x': { MaskCut(); return TRUE; } case 'C': case 'c': { MaskCopy(); return TRUE; } case 'V': case 'v': { MaskPaste(); return TRUE; } case 'Z': case 'z': { MaskUndo(); return TRUE; } } } } return TBase::PreTranslateMessage(pMsg); } /** * @brief * Deletes character in specified position * @param iPos Position for character to be deleted. */ void DeleteCharAt(int iPos) { _ASSERTE(PosInRange(iPos)); if (!PosInRange(iPos)) return; CString strMaskedText = GetMaskedText(iPos + 1) + m_chPrompt; SetMaskedText(strMaskedText, iPos, FALSE); } /** * @brief * Inserts character to specified position. * @param iPos Position to insert * @param nChar Character to be inserted */ void InsertCharAt(int iPos, TCHAR nChar) { _ASSERTE(PosInRange(iPos)); if (!PosInRange(iPos)) return; CString strMaskedText = CString(nChar) + GetMaskedText(iPos); SetMaskedText(strMaskedText, iPos, FALSE); } /** * @brief * Copies text to system clipboard * @param strText Text to be copied * @return * TRUE if successful; otherwise returns FALSE */ BOOL CopyToClipboard(const CString& strText) { if (!TBase::OpenClipboard()) return FALSE; ::EmptyClipboard(); SIZE_T nLen = (strText.GetLength() + 1) * sizeof(TCHAR); HGLOBAL hglbCopy = ::GlobalAlloc(GMEM_MOVEABLE, nLen); if (hglbCopy == NULL) { ::CloseClipboard(); return FALSE; } LPTSTR lptstrCopy = (TCHAR*)GlobalLock(hglbCopy); STRCPY_S(lptstrCopy, XTPToSizeTChecked(strText.GetLength() + 1), (LPCTSTR)strText); GlobalUnlock(hglbCopy); # ifndef _UNICODE ::SetClipboardData(CF_TEXT, hglbCopy); # else ::SetClipboardData(CF_UNICODETEXT, hglbCopy); # endif if (!::CloseClipboard()) return FALSE; return TRUE; } /** * @brief * Retrieves masked text of the control * @param nStartPos Start position * @param nEndPos End position * @return * Masked text of the control. */ CString GetMaskedText(int nStartPos = 0, int nEndPos = -1) const { if (nEndPos == -1) nEndPos = m_strWindowText.GetLength(); else nEndPos = min(nEndPos, m_strWindowText.GetLength()); CString strBuffer; for (int i = nStartPos; i < nEndPos; ++i) { if (IsPromptPos(i)) { strBuffer += m_strWindowText[i]; } } return strBuffer; } protected: /** @cond */ afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if (!CanUseMask()) { TBase::OnKeyDown(nChar, nRepCnt, nFlags); // default processing. return; } BOOL bShift = (::GetKeyState(VK_SHIFT) < 0); BOOL bCtrl = (::GetKeyState(VK_CONTROL) < 0); switch (nChar) { case VK_UP: case VK_LEFT: case VK_HOME: { TBase::OnKeyDown(nChar, nRepCnt, nFlags); GetMaskState(FALSE); int iStartChar = m_nStartChar; CorrectPosition(iStartChar, FALSE); if (m_nStartChar < iStartChar) { m_nStartChar = iStartChar; if (!bShift) m_nEndChar = iStartChar; } SetMaskState(); } return; case VK_DOWN: case VK_RIGHT: case VK_END: { TBase::OnKeyDown(nChar, nRepCnt, nFlags); GetMaskState(FALSE); int iEndChar = m_nEndChar; CorrectPosition(iEndChar); if (m_nEndChar > iEndChar) { m_nEndChar = iEndChar; if (!bShift) m_nStartChar = iEndChar; } SetMaskState(); } return; case VK_INSERT: { if (bCtrl) { MaskCopy(); } else if (bShift) { MaskPaste(); } else { m_bOverType = !m_bOverType; // set the type-over flag } } return; case VK_DELETE: { GetMaskState(); if (m_nStartChar == m_nEndChar) { m_nEndChar = m_nStartChar + 1; } else if (bShift) { MaskCopy(); } MaskDeleteSel(); SetMaskState(); } return; case VK_SPACE: { GetMaskState(); if (!PosInRange(m_nStartChar) || !IsPromptPos(m_nStartChar)) { NotifyPosNotInRange(); return; } TCHAR chSpace = _T(' '); if (ProcessMask(chSpace, m_nStartChar)) { ProcessChar(chSpace); } SetMaskState(); } return; case VK_BACK: { GetMaskState(FALSE); if (((m_nStartChar > 0) || (m_nStartChar == 0 && m_nEndChar != 0)) && (m_nStartChar <= m_strLiteral.GetLength())) { if (m_nStartChar == m_nEndChar) { m_nStartChar--; CorrectPosition(m_nStartChar, FALSE); if (m_bOverType && PosInRange(m_nStartChar)) { m_strWindowText.SetAt(m_nStartChar, m_strDefault[m_nStartChar]); m_nEndChar = m_nStartChar; } } MaskDeleteSel(); SetMaskState(); } else { NotifyPosNotInRange(); } } return; } TBase::OnKeyDown(nChar, nRepCnt, nFlags); } void ProcessChar(TCHAR nChar) { int iLen = m_strLiteral.GetLength(); if (m_nStartChar >= iLen) { NotifyPosNotInRange(); return; } if (m_nStartChar != m_nEndChar) { MaskDeleteSel(); } _ASSERTE(m_nStartChar == m_nEndChar); CorrectPosition(m_nStartChar); if (CanOverType()) { m_strWindowText.SetAt(m_nStartChar, nChar); } else { InsertCharAt(m_nStartChar, nChar); } if (m_nStartChar < iLen) m_nStartChar++; CorrectPosition(m_nStartChar); m_nEndChar = m_nStartChar; } afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { if (!CanUseMask()) { TBase::OnChar(nChar, nRepCnt, nFlags); return; } switch (nChar) { case VK_SPACE: case VK_BACK: return; // handled in WM_KEYDOWN } GetMaskState(); if (!PosInRange(m_nStartChar) || !IsPromptPos(m_nStartChar)) { NotifyPosNotInRange(); return; } TCHAR ch = (TCHAR)nChar; if (!CheckChar(ch, m_nStartChar)) { NotifyInvalidCharacter(ch, m_strMask[m_nStartChar]); return; } if (IsPrintChar(ch)) { ProcessChar(ch); SetMaskState(FALSE); } else { if (nChar != 127) TBase::OnChar(nChar, nRepCnt, nFlags); } } afx_msg void OnSetFocus(CWnd* pOldWnd) { TBase::OnSetFocus(pOldWnd); if (!CanUseMask()) { return; } MaskGetSel(); CorrectPosition(m_nStartChar); m_nEndChar = m_nStartChar; TBase::SetSel(m_nStartChar, m_nEndChar); } afx_msg void OnUpdateEditUndo(CCmdUI* pCmdUI) { if (!CanUseMask()) { pCmdUI->Enable(TBase::CanUndo()); } else { pCmdUI->Enable(m_bModified); } } // Some goodies BOOL CorrectPosition(int& iPos, BOOL bForward = TRUE) // used internally { int iLen = m_strLiteral.GetLength(); if (IsPromptPos(iPos)) return TRUE; if (bForward) { for (; iPos < iLen; iPos++) { if (IsPromptPos(iPos)) return TRUE; } for (; iPos >= 0; iPos--) { if (IsPromptPos(iPos - 1)) return FALSE; } } else { for (; iPos >= 0; iPos--) { if (IsPromptPos(iPos)) return TRUE; } for (; iPos < iLen; iPos++) { if (IsPromptPos(iPos)) return FALSE; } } return FALSE; } virtual BOOL IsPrintChar(TCHAR nChar) { return _istprint(static_cast(nChar)) || IsAlphaChar(nChar); } /** * @brief * This method determines if nChar is alpha character * @param nChar Character need to test * @return * TRUE if nChar is alpha character. */ virtual BOOL IsAlphaChar(TCHAR nChar) { if (_istalpha(static_cast(nChar))) return TRUE; if (ConvertUnicodeAlpha(nChar, TRUE) != nChar) return TRUE; if (ConvertUnicodeAlpha(nChar, FALSE) != nChar) return TRUE; return FALSE; } virtual void NotifyPosNotInRange() { ::MessageBeep((UINT)-1); } virtual void NotifyInvalidCharacter(TCHAR /*nChar*/, TCHAR /*chMask*/) { ::MessageBeep((UINT)-1); } BOOL IsPromptPos(int nPos) const { return IsPromptPos(m_strLiteral, nPos); } BOOL IsPromptPos(const CString& strLiteral, int nPos) const { return (nPos >= 0 && nPos < strLiteral.GetLength()) && (strLiteral[nPos] == m_chPrompt); } void CorrectWindowText() { int nLiteralLength = m_strLiteral.GetLength(); int nWindowTextLength = m_strWindowText.GetLength(); if (nWindowTextLength > nLiteralLength) { m_strWindowText = m_strWindowText.Left(nLiteralLength); } else if (nWindowTextLength < nLiteralLength) { m_strWindowText += m_strLiteral.Mid(nWindowTextLength, nLiteralLength - nWindowTextLength); } } void GetMaskState(BOOL bCorrectSelection = TRUE) { if (!TBase::m_hWnd) return; _ASSERTE(m_bUseMask); MaskGetSel(); TBase::GetWindowText(m_strWindowText); _ASSERTE(m_strDefault.GetLength() == m_strLiteral.GetLength()); _ASSERTE(m_strMask.GetLength() == m_strLiteral.GetLength()); CorrectWindowText(); if (bCorrectSelection) { CorrectPosition(m_nStartChar); CorrectPosition(m_nEndChar); if (m_nEndChar < m_nStartChar) m_nEndChar = m_nStartChar; } } void MaskGetSel() // To allow CEdit and CRichEditCtrl { if (TBase::m_hWnd) { TBase::SendMessage(EM_GETSEL, (WPARAM)&m_nStartChar, (LPARAM)&m_nEndChar); } } void SetMaskState(BOOL bUpdateUndo = TRUE) { if (!TBase::m_hWnd) return; _ASSERTE(m_bUseMask); CString strWindowText; TBase::GetWindowText(strWindowText); CorrectWindowText(); TBase::HideCaret(); if (strWindowText != m_strWindowText) { TBase::SetWindowText(m_strWindowText); if (bUpdateUndo || m_bUpdateUndo) m_strUndoBuffer = strWindowText; m_bRedo = FALSE; m_bModified = TRUE; } m_bUpdateUndo = bUpdateUndo; TBase::SetSel(m_nStartChar, m_nEndChar); TBase::ShowCaret(); } /** @endcond */ /** * @brief * The framework calls this member function when the user selects an item from a menu * @param wParam The low-order word of wParam identifies the command ID of the menu item, * control, or accelerator. The high-order word of wParam specifies the * notification message if the message is from a control. If the message is from an accelerator, * the high-order word is 1. If the message is from a menu, the high-order word is 0 * @param lParam Identifies the control that sends the message if the message is from a * control. Otherwise, lParam is 0. * @return * An application returns nonzero if it processes this message; otherwise 0. */ virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam) { switch (LOWORD(wParam)) { case ID_EDIT_CUT: MaskCut(); return TRUE; case ID_EDIT_COPY: MaskCopy(); return TRUE; case ID_EDIT_PASTE: MaskPaste(); return TRUE; case ID_EDIT_CLEAR: MaskClear(); return TRUE; case ID_EDIT_UNDO: MaskUndo(); return TRUE; case ID_EDIT_SELECT_ALL: MaskSelectAll(); return TRUE; } return TBase::OnCommand(wParam, lParam); } protected: int m_nStartChar; /**< Current position of the first character in the current selection. */ int m_nEndChar; /**< Current position of the first non-selected character past the end of the current selection. */ BOOL m_bUseMask; /**< TRUE to use the edit mask. */ BOOL m_bOverType; /**< TRUE to over type the text, set with VK_INSERT key press. */ BOOL m_bRedo; /**< TRUE to redo, or FALSE to undo. */ BOOL m_bModified; /**< TRUE if mask edit has been modified. */ TCHAR m_chPrompt; /**< Prompt character used to identify the text entry. */ CString m_strMask; /**< Buffer that holds the actual edit mask value. */ CString m_strDefault; /**< Contains the edit controls default display text. */ CString m_strUndoBuffer; /**< Holds the contents of the undo buffer. */ CString m_strRedoBuffer; /**< Holds the contents of the redo buffer. */ CString m_strWindowText; /**< Buffer that holds the actual edit text. */ CString m_strLiteral; /**< Literal format that restricts where the user can enter text. */ BOOL m_bUpdateUndo; /**< TRUE to update undo */ }; /** @cond */ # define ON_MESSAGE_BOOL(message, memberFxn) \ { \ message, 0, 0, \ 0, AfxSig_bv, (AFX_PMSG)(AFX_PMSGW)(BOOL(AFX_MSG_CALL CWnd::*)(void)) & memberFxn \ }, # define ON_MASKEDIT_REFLECT \ ON_MESSAGE_BOOL(WM_CUT, MaskCut) \ ON_MESSAGE_BOOL(WM_PASTE, MaskPaste) \ ON_MESSAGE_BOOL(WM_CLEAR, MaskClear) \ ON_MESSAGE_BOOL(WM_UNDO, MaskUndo) \ ON_MESSAGE_BOOL(WM_COPY, MaskCopy) \ ON_WM_KEYDOWN() \ ON_WM_CHAR() \ ON_WM_SETFOCUS /** @endcond */ ////////////////////////////////////////////////////////////////////// # include "Common/Base/Diagnostic/XTPEnableNoisyWarnings.h" /** @cond */ #endif // #if !defined(__XTMASKEDITEX_H__) /** @endcond */