From bd4650767fe52f8dd2955b802d2dfb3262cc8dd0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 24 Aug 2023 17:11:51 +0200 Subject: [PATCH] Prevent pasting invalid characters when using wxTextValidator It shouldn't be possible to put characters that can't be entered into the associated control directly by pasting them, so intercept paste events too and filter out any invalid characters. Closes #10281. Closes #23812. --- include/wx/valtext.h | 2 ++ src/common/valtext.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/wx/valtext.h b/include/wx/valtext.h index edf643ec35..b292f31447 100644 --- a/include/wx/valtext.h +++ b/include/wx/valtext.h @@ -155,6 +155,8 @@ protected: wxArrayString m_excludes; private: + void OnPaste(wxClipboardTextEvent& event); + wxDECLARE_NO_ASSIGN_CLASS(wxTextValidator); wxDECLARE_DYNAMIC_CLASS(wxTextValidator); wxDECLARE_EVENT_TABLE(); diff --git a/src/common/valtext.cpp b/src/common/valtext.cpp index 7a94a1fc96..7aada5d604 100644 --- a/src/common/valtext.cpp +++ b/src/common/valtext.cpp @@ -30,6 +30,7 @@ #include #include +#include "wx/clipbrd.h" #include "wx/combo.h" // ---------------------------------------------------------------------------- @@ -57,6 +58,7 @@ static bool wxIsNumeric(const wxString& val) wxIMPLEMENT_DYNAMIC_CLASS(wxTextValidator, wxValidator); wxBEGIN_EVENT_TABLE(wxTextValidator, wxValidator) EVT_CHAR(wxTextValidator::OnChar) + EVT_TEXT_PASTE(wxID_ANY, wxTextValidator::OnPaste) wxEND_EVENT_TABLE() wxTextValidator::wxTextValidator(long style, wxString *val) @@ -298,6 +300,69 @@ void wxTextValidator::OnChar(wxKeyEvent& event) event.Skip(false); } +void wxTextValidator::OnPaste(wxClipboardTextEvent& event) +{ +#if wxUSE_CLIPBOARD + // Filter out invalid characters from the clipboard contents as it + // shouldn't be possible to sneak them into the control in such a way. + // + // This seems better than not allowing to paste anything at all if there is + // anything invalid on the clipboard, e.g. it is more user-friendly to omit + // any trailing spaces in a control not allowing them than to refuse to + // paste a string with some spaces into it completely. + // + // Out of abundance of caution also prefer to let the control do its own + // thing if there are no invalid characters at all, as we can be sure it + // does the right thing in all cases, while our code might not deal with + // some edge cases correctly. + wxClipboardLocker lock; + wxTextDataObject data; + wxTheClipboard->GetData(data); + const wxString& text = data.GetText(); + + wxString valid; + valid.reserve(text.length()); + + bool hasInvalid = false; + + // Examine all characters one by one. + for ( wxString::const_iterator i = text.begin(), end = text.end(); + i != end; ++i ) + { + const wxUniChar ch = *i; + + if ( IsValidChar(ch) ) + { + valid += ch; + } + else // Invalid character. + { + // Only beep once per paste, not for every invalid character. + if ( !hasInvalid && !wxValidator::IsSilent() ) + wxBell(); + + hasInvalid = true; + } + } + + // If we can't let the control paste everything, do it ourselves. + if ( hasInvalid ) + { + wxTextEntry * const entry = GetTextEntry(); + if ( entry ) + { + entry->WriteText(valid); + + // Skip the call to wxEvent::Skip() below, preventing the normal + // paste from happening. + return; + } + } +#endif // wxUSE_CLIPBOARD + + event.Skip(); +} + bool wxTextValidator::IsValidChar(const wxUniChar& c) const { if ( !m_validatorStyle ) // no filtering if HasFlag(wxFILTER_NONE)