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.
This commit is contained in:
Vadim Zeitlin 2023-08-24 17:11:51 +02:00
parent cd8a854dce
commit bd4650767f
2 changed files with 67 additions and 0 deletions

View file

@ -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();

View file

@ -30,6 +30,7 @@
#include <string.h>
#include <stdlib.h>
#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)