diff --git a/docs/changes.txt b/docs/changes.txt index 956ba8986c..44657da85c 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -104,6 +104,10 @@ Changes in behaviour which may result in build errors e.g. if the error is due to spelling an option name wrongly, fixing or removing its name. +- wxTextValidator::Get{In,Ex}cludes() now return a const reference to + wxArrayString. Please update your code to use the appropriate setter + Set[Char]{In,Ex}cludes(), instead of mutating the internal data directly. + 3.1.3: (released 2019-??-??) ---------------------------- diff --git a/include/wx/valtext.h b/include/wx/valtext.h index 7dbdec176f..61c339a22b 100644 --- a/include/wx/valtext.h +++ b/include/wx/valtext.h @@ -31,9 +31,20 @@ enum wxTextValidatorStyle wxFILTER_INCLUDE_LIST = 0x40, wxFILTER_INCLUDE_CHAR_LIST = 0x80, wxFILTER_EXCLUDE_LIST = 0x100, - wxFILTER_EXCLUDE_CHAR_LIST = 0x200 + wxFILTER_EXCLUDE_CHAR_LIST = 0x200, + wxFILTER_XDIGITS = 0x400, + wxFILTER_SPACE = 0x800, + + // filter char class (for internal use only) + wxFILTER_CC = wxFILTER_SPACE|wxFILTER_ASCII|wxFILTER_NUMERIC| + wxFILTER_ALPHANUMERIC|wxFILTER_ALPHA| + wxFILTER_DIGITS|wxFILTER_XDIGITS }; +// ---------------------------------------------------------------------------- +// wxTextValidator +// ---------------------------------------------------------------------------- + class WXDLLIMPEXP_CORE wxTextValidator: public wxValidator { public: @@ -68,31 +79,78 @@ public: wxTextEntry *GetTextEntry(); + // strings & chars inclusions: + // --------------------------- + void SetCharIncludes(const wxString& chars); - void SetIncludes(const wxArrayString& includes) { m_includes = includes; } - inline wxArrayString& GetIncludes() { return m_includes; } + void AddCharIncludes(const wxString& chars); + + void SetIncludes(const wxArrayString& includes); + void AddInclude(const wxString& include); + + const wxArrayString& GetIncludes() const { return m_includes; } + wxString GetCharIncludes() const { return m_charIncludes; } + + // strings & chars exclusions: + // --------------------------- void SetCharExcludes(const wxString& chars); - void SetExcludes(const wxArrayString& excludes) { m_excludes = excludes; } - inline wxArrayString& GetExcludes() { return m_excludes; } + void AddCharExcludes(const wxString& chars); + + void SetExcludes(const wxArrayString& excludes); + void AddExclude(const wxString& exclude); + + const wxArrayString& GetExcludes() const { return m_excludes; } + wxString GetCharExcludes() const { return m_charExcludes; } bool HasFlag(wxTextValidatorStyle style) const { return (m_validatorStyle & style) != 0; } + // implementation only + // -------------------- + + // returns the error message if the contents of 'str' are invalid + virtual wxString IsValid(const wxString& str) const; + protected: - // returns true if all characters of the given string are present in m_includes + bool IsCharIncluded(const wxUniChar& c) const + { + return m_charIncludes.find(c) != wxString::npos; + } + + bool IsCharExcluded(const wxUniChar& c) const + { + return m_charExcludes.find(c) != wxString::npos; + } + + bool IsIncluded(const wxString& str) const + { + if ( HasFlag(wxFILTER_INCLUDE_LIST) ) + return m_includes.Index(str) != wxNOT_FOUND; + + // m_includes should be ignored (i.e. return true) + // if the style is not set. + return true; + } + + bool IsExcluded(const wxString& str) const + { + return m_excludes.Index(str) != wxNOT_FOUND; + } + + // returns false if the character is invalid + bool IsValidChar(const wxUniChar& c) const; + + // These two functions (undocumented now) are kept for compatibility reasons. bool ContainsOnlyIncludedCharacters(const wxString& val) const; - - // returns true if at least one character of the given string is present in m_excludes bool ContainsExcludedCharacters(const wxString& val) const; - // returns the error message if the contents of 'val' are invalid - virtual wxString IsValid(const wxString& val) const; - protected: long m_validatorStyle; wxString* m_stringValue; + wxString m_charIncludes; + wxString m_charExcludes; wxArrayString m_includes; wxArrayString m_excludes; diff --git a/interface/wx/valtext.h b/interface/wx/valtext.h index 0102328ad4..de93979acc 100644 --- a/interface/wx/valtext.h +++ b/interface/wx/valtext.h @@ -9,8 +9,11 @@ /** Styles used by wxTextValidator. - Note that when you specify more styles in wxTextValidator the validation checks - are performed in the order in which the styles of this enumeration are defined. + Notice that wxFILTER_EXCLUDE[_CHAR]_LIST pair can be used to document the + purpose of the validator only and are not enforced in the implementation of + the wxTextValidator. Therefore, calling the corresponding member functions: + wxTextValidator::{SetExcludes,SetCharExcludes}(), is enough to create the + desired validator. */ enum wxTextValidatorStyle { @@ -33,9 +36,11 @@ enum wxTextValidatorStyle /// Non-alphanumeric characters are filtered out. /// Uses the wxWidgets wrapper for the standard CRT function @c isalnum /// (which is locale-dependent) on all characters of the string. + /// Equivalent to wxFILTER_ALPHA combined with wxFILTER_DIGITS or + /// wxFILTER_XDIGITS, or with both of them. wxFILTER_ALPHANUMERIC, - /// Non-numeric characters are filtered out. + /// Non-digit characters are filtered out. /// Uses the wxWidgets wrapper for the standard CRT function @c isdigit /// (which is locale-dependent) on all characters of the string. wxFILTER_DIGITS, @@ -50,19 +55,36 @@ enum wxTextValidatorStyle /// the list, complaining if not. See wxTextValidator::SetIncludes(). wxFILTER_INCLUDE_LIST, - /// Use an include list. The validator checks if each input character is - /// in the list (one character per list element), complaining if not. - /// See wxTextValidator::SetCharIncludes(). + /// Use an include char list. + /// Characters in the include char list will be allowed to be in the + /// user input. See wxTextValidator::SetCharIncludes(). + /// If this style is set with one or more of the following styles: + /// wxFILTER_ASCII, wxFILTER_ALPHA, wxFILTER_ALPHANUMERIC, wxFILTER_DIGITS, + /// wxFILTER_XDIGITS, wxFILTER_NUMERIC it just extends the character class + /// denoted by the aforementioned styles with those specified in the include + /// char list. If set alone, then the charactes allowed to be in the user input + /// are restricted to those, and only those, present in the include char list. wxFILTER_INCLUDE_CHAR_LIST, /// Use an exclude list. The validator checks if the user input is on /// the list, complaining if it is. See wxTextValidator::SetExcludes(). wxFILTER_EXCLUDE_LIST, - /// Use an exclude list. The validator checks if each input character is - /// in the list (one character per list element), complaining if it is. - /// See wxTextValidator::SetCharExcludes(). - wxFILTER_EXCLUDE_CHAR_LIST + /// Use an exclude char list. + /// Characters in the exclude char list won't be allowed to be in the + /// user input. See wxTextValidator::SetCharExcludes(). + wxFILTER_EXCLUDE_CHAR_LIST, + + /// Non-hexadecimal characters are filtered out. + /// Uses the wxWidgets wrapper for the standard CRT function @c isxdigit + /// (which is locale-dependent) on all characters of the string. + wxFILTER_XDIGITS, + + /// A convenience flag for use with the other flags. + /// The space character is more often used with alphanumeric characters + /// which makes setting a flag more easier than calling SetCharIncludes(" ") + /// for that matter. + wxFILTER_SPACE }; /** @@ -83,7 +105,7 @@ class wxTextValidator : public wxValidator { public: /** - Default constructor. + Copy constructor. */ wxTextValidator(const wxTextValidator& validator); @@ -106,14 +128,28 @@ public: virtual wxObject* Clone() const; /** - Returns a reference to the exclude list (the list of invalid values). + Returns a copy of the exclude char list (the list of invalid characters). + + @since 3.1.3 */ - wxArrayString& GetExcludes(); + wxString GetCharExcludes() const; /** - Returns a reference to the include list (the list of valid values). + Returns a copy of the include char list (the list of additional valid characters). + + @since 3.1.3 */ - wxArrayString& GetIncludes(); + wxString GetCharIncludes() const; + + /** + Returns a const reference to the exclude list (the list of invalid values). + */ + const wxArrayString& GetExcludes() const; + + /** + Returns a const reference to the include list (the list of valid values). + */ + const wxArrayString& GetIncludes() const; /** Returns the validator style. @@ -135,41 +171,81 @@ public: /** Sets the exclude list (invalid values for the user input). + + @note Beware that exclusion takes priority over inclusion. */ void SetExcludes(const wxArrayString& stringList); /** - Breaks the given @a chars strings in single characters and sets the - internal wxArrayString used to store the "excluded" characters - (see SetExcludes()). + Sets the exclude char list (invalid characters for the user input). - This function is mostly useful when @c wxFILTER_EXCLUDE_CHAR_LIST was used. + @note Beware that exclusion takes priority over inclusion. + @note This function may cancel the effect of @c wxFILTER_SPACE if the passed + in string @a chars contains the @b space character. */ void SetCharExcludes(const wxString& chars); /** Sets the include list (valid values for the user input). + + @see IsIncluded() */ void SetIncludes(const wxArrayString& stringList); /** - Breaks the given @a chars strings in single characters and sets the - internal wxArrayString used to store the "included" characters - (see SetIncludes()). + Sets the include char list (additional valid values for the user input). - This function is mostly useful when @c wxFILTER_INCLUDE_CHAR_LIST was used. + @note Any explicitly excluded characters will still be excluded even if + they're part of @a chars. */ void SetCharIncludes(const wxString& chars); + /** + Adds @a exclude to the list of excluded values. + + @note Beware that exclusion takes priority over inclusion. + + @since 3.1.3 + */ + void AddExclude(const wxString& exclude); + + /** + Adds @a include to the list of included values. + + @note Any explicitly excluded characters will still be excluded. + + @since 3.1.3 + */ + void AddInclude(const wxString& include); + + /** + Adds @a chars to the list of excluded characters. + + @note Beware that exclusion takes priority over inclusion. + + @since 3.1.3 + */ + void AddCharExcludes(const wxString& chars); + + /** + Adds @a chars to the list of included characters. + + @note Any explicitly excluded characters will still be excluded even if + they're part of @a chars. + + @since 3.1.3 + */ + void AddCharIncludes(const wxString& chars); + + /** Sets the validator style which must be a combination of one or more of the ::wxTextValidatorStyle values. - Note that not all possible combinations make sense! - Also note that the order in which the checks are performed is important, - in case you specify more than a single style. - wxTextValidator will perform the checks in the same definition order - used in the ::wxTextValidatorStyle enumeration. + Note that not all possible combinations make sense! Also, some + combinations have shorter and more idiomatic alternative, e.g. + @c wxFILTER_ALPHANUMERIC can be used instead of + @c wxFILTER_ALPHA|wxFILTER_DIGITS. */ void SetStyle(long style); @@ -189,24 +265,52 @@ public: */ virtual bool Validate(wxWindow* parent); -protected: - - /** - Returns @true if all the characters of the given @a val string - are present in the include list (set by SetIncludes() or SetCharIncludes()). - */ - bool ContainsOnlyIncludedCharacters(const wxString& val) const; - - /** - Returns true if at least one character of the given @a val string - is present in the exclude list (set by SetExcludes() or SetCharExcludes()). - */ - bool ContainsExcludedCharacters(const wxString& val) const; - /** Returns the error message if the contents of @a val are invalid or the empty string if @a val is valid. */ virtual wxString IsValid(const wxString& val) const; + +protected: + + /** + Returns @true if the char @a c is allowed to be in the user input string. + Additional characters, set by SetCharIncludes() or AddCharIncludes() are + also considered. + + @since 3.1.3 + */ + bool IsCharIncluded(const wxUniChar& c) const; + + /** + Returns @true if the char @a c is not allowed to be in the user input string. + (characters set by SetCharExcludes() or AddCharExcludes()). + + @since 3.1.3 + */ + bool IsCharExcluded(const wxUniChar& c) const; + + /** + Returns @true if the string @a str is one of the includes strings set by + SetIncludes() or AddInclude(). + + Notice that unless wxFILTER_INCLUDE_LIST is specified (in which case the + validator will complain if the user input is not on the list), the list + will be ignored and won't participate in the validation process. + + @since 3.1.3 + */ + bool IsIncluded(const wxString& str) const; + + /** + Returns @true if the string @a str is one of the excludes strings set by + SetExcludes() or AddExclude(). + + @since 3.1.3 + */ + bool IsExcluded(const wxString& str) const; + + /// Returns false if the character @a c is invalid. + bool IsValidChar(const wxUniChar& c) const; }; diff --git a/samples/propgrid/sampleprops.cpp b/samples/propgrid/sampleprops.cpp index 92ea1316ed..c32ec6e7c3 100644 --- a/samples/propgrid/sampleprops.cpp +++ b/samples/propgrid/sampleprops.cpp @@ -650,16 +650,12 @@ wxValidator* wxArrayDoubleProperty::DoGetValidator() const #if wxUSE_VALIDATORS WX_PG_DOGETVALIDATOR_ENTRY() - wxTextValidator* validator = new wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST); + wxTextValidator* validator = + new wxNumericPropertyValidator(wxNumericPropertyValidator::Float); - // Accept characters for numeric elements - wxNumericPropertyValidator numValidator(wxNumericPropertyValidator::Float); - wxArrayString incChars(numValidator.GetIncludes()); // Accept also a delimiter and space character - incChars.Add(m_delimiter); - incChars.Add(" "); - - validator->SetIncludes(incChars); + validator->AddCharIncludes(m_delimiter); + validator->AddCharIncludes(" "); WX_PG_DOGETVALIDATOR_EXIT(validator) #else diff --git a/samples/validate/validate.cpp b/samples/validate/validate.cpp index 3f8706ee07..55bf6a0169 100644 --- a/samples/validate/validate.cpp +++ b/samples/validate/validate.cpp @@ -40,8 +40,6 @@ // Global data // ---------------------------------------------------------------------------- -MyData g_data; - wxString g_listbox_choices[] = {"one", "two", "three"}; @@ -51,6 +49,8 @@ wxString g_combobox_choices[] = wxString g_radiobox_choices[] = {"green", "yellow", "red"}; +MyData g_data; + // ---------------------------------------------------------------------------- // MyData // ---------------------------------------------------------------------------- @@ -63,6 +63,7 @@ MyData::MyData() m_string = "Spaces are invalid here"; m_string2 = "Valid text"; m_listbox_choices.Add(0); + m_combobox_choice = g_combobox_choices[0]; m_intValue = 0; m_smallIntValue = 3; m_doubleValue = 12354.31; @@ -247,28 +248,19 @@ MyDialog::MyDialog( wxWindow *parent, const wxString& title, wxFlexGridSizer *flexgridsizer = new wxFlexGridSizer(3, 2, 5, 5); - // Create and add controls to sizers. Note that a member variable - // of g_data is bound to each control upon construction. There is - // currently no easy way to substitute a different validator or a - // different transfer variable after a control has been constructed. - + // Create and add controls to sizers. // Pointers to some of these controls are saved in member variables // so that we can use them elsewhere, like this one. - m_text = new wxTextCtrl(this, VALIDATE_TEXT, wxEmptyString, - wxDefaultPosition, wxDefaultSize, 0, - wxTextValidator(wxFILTER_ALPHA, &g_data.m_string)); - m_text->SetToolTip("uses wxTextValidator with wxFILTER_ALPHA"); + m_text = new wxTextCtrl(this, VALIDATE_TEXT); + m_text->SetToolTip("wxTextValidator not set"); + m_text->SetHint("Enter some text here, please..."); flexgridsizer->Add(m_text, 1, wxGROW); - - // Now set a wxTextValidator with an explicit list of characters NOT allowed: - wxTextValidator textVal(wxFILTER_EMPTY|wxFILTER_EXCLUDE_CHAR_LIST, &g_data.m_string2); - textVal.SetCharExcludes("bcwyz"); - wxTextCtrl* txt2 = - new wxTextCtrl(this, VALIDATE_TEXT2, wxEmptyString, - wxDefaultPosition, wxDefaultSize, 0, textVal); - txt2->SetToolTip("uses wxTextValidator with wxFILTER_EMPTY|wxFILTER_EXCLUDE_CHAR_LIST to exclude 'bcwyz'"); - flexgridsizer->Add(txt2, 1, wxGROW); + // Make it possible to change the wxTextValidator for m_text at runtime. + wxButton* const button = + new wxButton(this, wxID_ANY, "Set new wxTextValidator..."); + button->Bind(wxEVT_BUTTON, &MyDialog::OnChangeValidator, this); + flexgridsizer->Add(button, wxSizerFlags().Center()); flexgridsizer->Add(new wxListBox((wxWindow*)this, VALIDATE_LIST, wxDefaultPosition, wxDefaultSize, @@ -391,17 +383,298 @@ MyDialog::MyDialog( wxWindow *parent, const wxString& title, // make the dialog a bit bigger than its minimal size: SetSize(GetBestSize()*1.5); -} -bool MyDialog::TransferDataToWindow() -{ - bool r = wxDialog::TransferDataToWindow(); - - // These function calls have to be made here, after the - // dialog has been created. + // Now sets the focus to m_text m_text->SetFocus(); - m_combobox->SetSelection(0); - - return r; } +void MyDialog::OnChangeValidator(wxCommandEvent& WXUNUSED(event)) +{ + TextValidatorDialog dialog(this, m_text); + + if ( dialog.ShowModal() == wxID_OK ) + { + dialog.ApplyValidator(); + } +} + +// ---------------------------------------------------------------------------- +// TextValidatorDialog +// ---------------------------------------------------------------------------- + +TextValidatorDialog::TextValidatorDialog(wxWindow *parent, wxTextCtrl* txtCtrl) + : wxDialog(parent, wxID_ANY, "wxTextValidator Dialog"), + m_txtCtrl(txtCtrl), + m_noValidation(true), + m_validatorStyle(wxFILTER_NONE) +{ + if ( m_txtCtrl ) + { + wxTextValidator* txtValidator = + wxDynamicCast(m_txtCtrl->GetValidator(), wxTextValidator); + + if ( txtValidator ) + { + m_validatorStyle = txtValidator->GetStyle(); + + if ( m_validatorStyle != wxFILTER_NONE ) + m_noValidation = false; + + m_charIncludes = txtValidator->GetCharIncludes(); + m_charExcludes = txtValidator->GetCharExcludes(); + m_includes = txtValidator->GetIncludes(); + m_excludes = txtValidator->GetExcludes(); + } + } + + wxFlexGridSizer *fgSizer = new wxFlexGridSizer(2, FromDIP(wxSize(5, 5))); + const wxSizerFlags center = wxSizerFlags().CenterVertical(); + + const StyleValidator styleVal(&m_validatorStyle); + + wxCheckBox* filterNone = new wxCheckBox(this, Id_None, "wxFILTER_NONE"); + filterNone->SetValue(m_noValidation); + fgSizer->Add(filterNone); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "No filtering takes place.")); + + fgSizer->Add(new wxCheckBox(this, Id_Empty, "wxFILTER_EMPTY")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Empty strings are filtered out.")); + + fgSizer->Add(new wxCheckBox(this, Id_Ascii, "wxFILTER_ASCII")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Non-ASCII characters are filtered out.")); + + fgSizer->Add(new wxCheckBox(this, Id_Alpha, "wxFILTER_ALPHA")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Non-alpha characters are filtered out.")); + + fgSizer->Add(new wxCheckBox(this, Id_Alphanumeric, "wxFILTER_ALPHANUMERIC")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Non-alphanumeric characters are filtered out.")); + + fgSizer->Add(new wxCheckBox(this, Id_Digits, "wxFILTER_DIGITS")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Non-digit characters are filtered out.")); + + fgSizer->Add(new wxCheckBox(this, Id_Numeric, "wxFILTER_NUMERIC")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Non-numeric characters are filtered out.")); + + fgSizer->Add(new wxCheckBox(this, Id_IncludeList, "wxFILTER_INCLUDE_LIST"), center) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxTextCtrl(this, Id_IncludeListTxt), wxSizerFlags().Expand()) + ->GetWindow()->Bind(wxEVT_KILL_FOCUS, &TextValidatorDialog::OnKillFocus, this); + + fgSizer->Add(new wxCheckBox(this, Id_IncludeCharList, "wxFILTER_INCLUDE_CHAR_LIST"), center) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxTextCtrl(this, Id_IncludeCharListTxt, wxString(), wxDefaultPosition, + wxDefaultSize, 0, wxGenericValidator(&m_charIncludes)), + wxSizerFlags().Expand()) + ->GetWindow()->Bind(wxEVT_KILL_FOCUS, &TextValidatorDialog::OnKillFocus, this); + + fgSizer->Add(new wxCheckBox(this, Id_ExcludeList, "wxFILTER_EXCLUDE_LIST"), center) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxTextCtrl(this, Id_ExcludeListTxt), wxSizerFlags().Expand()) + ->GetWindow()->Bind(wxEVT_KILL_FOCUS, &TextValidatorDialog::OnKillFocus, this); + + fgSizer->Add(new wxCheckBox(this, Id_ExcludeCharList, "wxFILTER_EXCLUDE_CHAR_LIST"), center) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxTextCtrl(this, Id_ExcludeCharListTxt, wxString(), wxDefaultPosition, + wxDefaultSize, 0, wxGenericValidator(&m_charExcludes)), + wxSizerFlags().Expand()) + ->GetWindow()->Bind(wxEVT_KILL_FOCUS, &TextValidatorDialog::OnKillFocus, this); + + fgSizer->Add(new wxCheckBox(this, Id_Xdigits, "wxFILTER_XDIGITS")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Non-xdigit characters are filtered out.")); + + fgSizer->Add(new wxCheckBox(this, Id_Space, "wxFILTER_SPACE")) + ->GetWindow()->SetValidator(styleVal); + fgSizer->Add(new wxStaticText(this, wxID_ANY, "Allow spaces.")); + + // Set the main sizer. + wxBoxSizer *mainsizer = new wxBoxSizer( wxVERTICAL ); + + mainsizer->Add(fgSizer, wxSizerFlags(1).Border(wxALL, 10).Expand()); + + mainsizer->Add(CreateButtonSizer(wxOK | wxCANCEL), + wxSizerFlags().Expand().DoubleBorder()); + + SetSizer(mainsizer); + mainsizer->SetSizeHints(this); + + // Bind event handlers. + Bind(wxEVT_CHECKBOX, &TextValidatorDialog::OnChecked, this); + Bind(wxEVT_UPDATE_UI, &TextValidatorDialog::OnUpdateUI, + this, Id_Ascii, Id_ExcludeCharListTxt); +} + +void TextValidatorDialog::OnUpdateUI(wxUpdateUIEvent& event) +{ + event.Enable(!m_noValidation); + + if ( m_noValidation ) + event.Check(false); +} + +void TextValidatorDialog::OnChecked(wxCommandEvent& event) +{ + if ( event.GetId() == Id_None ) + { + m_noValidation = event.IsChecked(); + + if ( m_noValidation ) + { + long style = wxFILTER_NONE; + + // we should keep this flag on if it has been set. + if ( HasFlag(wxFILTER_EMPTY) ) + style = wxFILTER_EMPTY; + + m_validatorStyle = style; + + m_charIncludes.clear(); + m_charExcludes.clear(); + m_includes.clear(); + m_excludes.clear(); + } + } +} + +void TextValidatorDialog::OnKillFocus(wxFocusEvent &event) +{ + wxTextCtrl* txtCtrl = wxDynamicCast(event.GetEventObject(), wxTextCtrl); + + if ( txtCtrl && txtCtrl->IsModified() ) + { + const int id = event.GetId(); + + if ( id == Id_IncludeCharListTxt ) + { + m_charIncludes = txtCtrl->GetValue(); + } + else if ( id == Id_ExcludeCharListTxt ) + { + m_charExcludes = txtCtrl->GetValue(); + } + else if ( id == Id_IncludeListTxt ) + { + m_includes = wxSplit(txtCtrl->GetValue(), ' '); + } + else if ( id == Id_ExcludeListTxt ) + { + m_excludes = wxSplit(txtCtrl->GetValue(), ' '); + } + } + + event.Skip(); +} + +void TextValidatorDialog::ApplyValidator() +{ + if ( !m_txtCtrl ) + return; + + wxString tooltip = "uses wxTextValidator with "; + + if ( m_noValidation ) + { + tooltip += "wxFILTER_NONE|"; + } + else + { + if ( HasFlag(wxFILTER_ASCII) ) + tooltip += "wxFILTER_ASCII|"; + if ( HasFlag(wxFILTER_ALPHA) ) + tooltip += "wxFILTER_ALPHA|"; + if ( HasFlag(wxFILTER_ALPHANUMERIC) ) + tooltip += "wxFILTER_ALPHANUMERIC|"; + if ( HasFlag(wxFILTER_DIGITS) ) + tooltip += "wxFILTER_DIGITS|"; + if ( HasFlag(wxFILTER_NUMERIC) ) + tooltip += "wxFILTER_NUMERIC|"; + if ( HasFlag(wxFILTER_XDIGITS) ) + tooltip += "wxFILTER_XDIGITS|"; + if ( HasFlag(wxFILTER_SPACE) ) + tooltip += "wxFILTER_SPACE|"; + if ( HasFlag(wxFILTER_INCLUDE_LIST) ) + tooltip += "wxFILTER_INCLUDE_LIST|"; + if ( HasFlag(wxFILTER_INCLUDE_CHAR_LIST) ) + tooltip += "wxFILTER_INCLUDE_CHAR_LIST|"; + if ( HasFlag(wxFILTER_EXCLUDE_LIST) ) + tooltip += "wxFILTER_EXCLUDE_LIST|"; + if ( HasFlag(wxFILTER_EXCLUDE_CHAR_LIST) ) + tooltip += "wxFILTER_EXCLUDE_CHAR_LIST|"; + } + + if ( HasFlag(wxFILTER_EMPTY) ) + { + tooltip += "wxFILTER_EMPTY|"; + } + + tooltip.RemoveLast(); // remove the trailing '|' char. + + if ( !m_charIncludes.empty() ) + { + tooltip += "\nAllowed chars: "; + tooltip += m_charIncludes; + } + + if ( !m_charExcludes.empty() ) + { + tooltip += "\nDisallowed chars: "; + tooltip += m_charExcludes; + } + + m_txtCtrl->SetToolTip(tooltip); + + // Prepare and set the wxTextValidator + wxTextValidator txtVal(m_validatorStyle, &g_data.m_string); + txtVal.SetCharIncludes(m_charIncludes); + txtVal.SetCharExcludes(m_charExcludes); + txtVal.SetIncludes(m_includes); + txtVal.SetExcludes(m_excludes); + + m_txtCtrl->SetValidator(txtVal); + m_txtCtrl->SetFocus(); +} + +bool TextValidatorDialog::StyleValidator::TransferToWindow() +{ + wxASSERT( wxDynamicCast(m_validatorWindow, wxCheckBox) ); + + if ( m_style ) + { + wxCheckBox* cb = (wxCheckBox*)GetWindow(); + if ( !cb ) + return false; + + const long style = 1 << (cb->GetId()-wxID_HIGHEST-1); + + cb->SetValue((*m_style & style) != 0); + } + + return true; +} + +bool TextValidatorDialog::StyleValidator::TransferFromWindow() +{ + wxASSERT( wxDynamicCast(m_validatorWindow, wxCheckBox) ); + + if ( m_style ) + { + wxCheckBox* cb = (wxCheckBox*)GetWindow(); + if ( !cb ) + return false; + + const long style = 1 << (cb->GetId()-wxID_HIGHEST-1); + + if ( cb->IsChecked() ) + *m_style |= style; + else + *m_style &= ~style; + } + + return true; +} diff --git a/samples/validate/validate.h b/samples/validate/validate.h index 226f703ba3..b482568b63 100644 --- a/samples/validate/validate.h +++ b/samples/validate/validate.h @@ -48,7 +48,8 @@ public: const wxSize& size = wxDefaultSize, const long style = wxDEFAULT_DIALOG_STYLE); - bool TransferDataToWindow() wxOVERRIDE; + void OnChangeValidator(wxCommandEvent& event); + wxTextCtrl *m_text; wxComboBox *m_combobox; @@ -56,6 +57,81 @@ public: wxTextCtrl *m_numericTextDouble; }; +// ---------------------------------------------------------------------------- +// TextValidatorDialog +// ---------------------------------------------------------------------------- +class TextValidatorDialog : public wxDialog +{ +public: + TextValidatorDialog(wxWindow *parent, wxTextCtrl* txtCtrl); + + void OnUpdateUI(wxUpdateUIEvent& event); + void OnChecked(wxCommandEvent& event); + void OnKillFocus( wxFocusEvent &event ); + + void ApplyValidator(); + +private: + // Special validator for our checkboxes + class StyleValidator : public wxValidator + { + public: + StyleValidator(long* style) { m_style = style; } + + virtual bool Validate(wxWindow *WXUNUSED(parent)) wxOVERRIDE { return true; } + virtual wxObject* Clone() const wxOVERRIDE { return new StyleValidator(*this); } + + // Called to transfer data to the window + virtual bool TransferToWindow() wxOVERRIDE; + + // Called to transfer data from the window + virtual bool TransferFromWindow() wxOVERRIDE; + + private: + long* m_style; + }; + +private: + bool HasFlag(wxTextValidatorStyle style) const + { + return (m_validatorStyle & style) != 0; + } + + enum + { + // CheckBoxes Ids (should be in sync with wxTextValidatorStyle) + Id_None = wxID_HIGHEST, + Id_Empty, + Id_Ascii, + Id_Alpha, + Id_Alphanumeric, + Id_Digits, + Id_Numeric, + Id_IncludeList, + Id_IncludeCharList, + Id_ExcludeList, + Id_ExcludeCharList, + Id_Xdigits, + Id_Space, + + // TextCtrls Ids + Id_IncludeListTxt, + Id_IncludeCharListTxt, + Id_ExcludeListTxt, + Id_ExcludeCharListTxt, + }; + + wxTextCtrl* const m_txtCtrl; + + bool m_noValidation; + long m_validatorStyle; + + wxString m_charIncludes; + wxString m_charExcludes; + wxArrayString m_includes; + wxArrayString m_excludes; +}; + class MyData { public: diff --git a/src/common/valtext.cpp b/src/common/valtext.cpp index 04f85840e2..7da7621209 100644 --- a/src/common/valtext.cpp +++ b/src/common/valtext.cpp @@ -23,6 +23,7 @@ #include #include "wx/textctrl.h" #include "wx/combobox.h" + #include "wx/log.h" #include "wx/utils.h" #include "wx/msgdlg.h" #include "wx/intl.h" @@ -76,25 +77,6 @@ wxTextValidator::wxTextValidator(const wxTextValidator& val) void wxTextValidator::SetStyle(long style) { m_validatorStyle = style; - -#if wxDEBUG_LEVEL - int check; - check = (int)HasFlag(wxFILTER_ALPHA) + (int)HasFlag(wxFILTER_ALPHANUMERIC) + - (int)HasFlag(wxFILTER_DIGITS) + (int)HasFlag(wxFILTER_NUMERIC); - wxASSERT_MSG(check <= 1, - "It makes sense to use only one of the wxFILTER_ALPHA/wxFILTER_ALPHANUMERIC/" - "wxFILTER_SIMPLE_NUMBER/wxFILTER_NUMERIC styles"); - - wxASSERT_MSG(((int)HasFlag(wxFILTER_INCLUDE_LIST) + (int)HasFlag(wxFILTER_INCLUDE_CHAR_LIST) <= 1) && - ((int)HasFlag(wxFILTER_EXCLUDE_LIST) + (int)HasFlag(wxFILTER_EXCLUDE_CHAR_LIST) <= 1), - "Using both wxFILTER_[IN|EX]CLUDE_LIST _and_ wxFILTER_[IN|EX]CLUDE_CHAR_LIST " - "doesn't work since wxTextValidator internally uses the same array for both"); - - check = (int)HasFlag(wxFILTER_INCLUDE_LIST) + (int)HasFlag(wxFILTER_INCLUDE_CHAR_LIST) + - (int)HasFlag(wxFILTER_EXCLUDE_LIST) + (int)HasFlag(wxFILTER_EXCLUDE_CHAR_LIST); - wxASSERT_MSG(check <= 1, - "Using both an include/exclude list may lead to unexpected results"); -#endif // wxDEBUG_LEVEL } bool wxTextValidator::Copy(const wxTextValidator& val) @@ -102,10 +84,12 @@ bool wxTextValidator::Copy(const wxTextValidator& val) wxValidator::Copy(val); m_validatorStyle = val.m_validatorStyle; - m_stringValue = val.m_stringValue; + m_stringValue = val.m_stringValue; - m_includes = val.m_includes; - m_excludes = val.m_excludes; + m_charIncludes = val.m_charIncludes; + m_charExcludes = val.m_charExcludes; + m_includes = val.m_includes; + m_excludes = val.m_excludes; return true; } @@ -153,25 +137,7 @@ bool wxTextValidator::Validate(wxWindow *parent) if ( !text ) return false; - wxString val(text->GetValue()); - - wxString errormsg; - - // We can only do some kinds of validation once the input is complete, so - // check for them here: - if ( HasFlag(wxFILTER_EMPTY) && val.empty() ) - errormsg = _("Required information entry is empty."); - else if ( HasFlag(wxFILTER_INCLUDE_LIST) && m_includes.Index(val) == wxNOT_FOUND ) - errormsg = wxString::Format(_("'%s' is not one of the valid strings"), val); - else if ( HasFlag(wxFILTER_EXCLUDE_LIST) && m_excludes.Index(val) != wxNOT_FOUND ) - errormsg = wxString::Format(_("'%s' is one of the invalid strings"), val); - else if ( !(errormsg = IsValid(val)).empty() ) - { - // NB: this format string should always contain exactly one '%s' - wxString buf; - buf.Printf(errormsg, val.c_str()); - errormsg = buf; - } + const wxString& errormsg = IsValid(text->GetValue()); if ( !errormsg.empty() ) { @@ -215,89 +181,98 @@ bool wxTextValidator::TransferFromWindow() return true; } -// IRIX mipsPro refuses to compile wxStringCheck() if func is inline so -// let's work around this by using this non-template function instead of -// wxStringCheck(). And while this might be fractionally less efficient because -// the function call won't be inlined like this, we don't care enough about -// this to add extra #ifs for non-IRIX case. -namespace +wxString wxTextValidator::IsValid(const wxString& str) const { + if ( HasFlag(wxFILTER_EMPTY) && str.empty() ) + return _("Required information entry is empty."); + else if ( IsExcluded(str) ) + return wxString::Format(_("'%s' is one of the invalid strings"), str); + else if ( !IsIncluded(str) ) + return wxString::Format(_("'%s' is not one of the valid strings"), str); -bool CheckString(bool (*func)(const wxUniChar&), const wxString& str) -{ - for ( wxString::const_iterator i = str.begin(); i != str.end(); ++i ) + // check the whole string for invalid chars. + for ( wxString::const_iterator i = str.begin(), end = str.end(); + i != end; ++i ) { - if ( !func(*i) ) - return false; + if ( !IsValidChar(*i) ) + { + return wxString::Format( + _("'%s' contains invalid character(s)"), str); + } } - return true; + return wxString(); } -} // anonymous namespace - -wxString wxTextValidator::IsValid(const wxString& val) const -{ - // wxFILTER_EMPTY is checked for in wxTextValidator::Validate - - if ( HasFlag(wxFILTER_ASCII) && !val.IsAscii() ) - return _("'%s' should only contain ASCII characters."); - if ( HasFlag(wxFILTER_ALPHA) && !CheckString(wxIsalpha, val) ) - return _("'%s' should only contain alphabetic characters."); - if ( HasFlag(wxFILTER_ALPHANUMERIC) && !CheckString(wxIsalnum, val) ) - return _("'%s' should only contain alphabetic or numeric characters."); - if ( HasFlag(wxFILTER_DIGITS) && !CheckString(wxIsdigit, val) ) - return _("'%s' should only contain digits."); - if ( HasFlag(wxFILTER_NUMERIC) && !wxIsNumeric(val) ) - return _("'%s' should be numeric."); - if ( HasFlag(wxFILTER_INCLUDE_CHAR_LIST) && !ContainsOnlyIncludedCharacters(val) ) - return _("'%s' doesn't consist only of valid characters"); - if ( HasFlag(wxFILTER_EXCLUDE_CHAR_LIST) && ContainsExcludedCharacters(val) ) - return _("'%s' contains illegal characters"); - - return wxEmptyString; -} - -bool wxTextValidator::ContainsOnlyIncludedCharacters(const wxString& val) const -{ - for ( wxString::const_iterator i = val.begin(); i != val.end(); ++i ) - if (m_includes.Index((wxString) *i) == wxNOT_FOUND) - // one character of 'val' is NOT present in m_includes... - return false; - - // all characters of 'val' are present in m_includes - return true; -} - -bool wxTextValidator::ContainsExcludedCharacters(const wxString& val) const -{ - for ( wxString::const_iterator i = val.begin(); i != val.end(); ++i ) - if (m_excludes.Index((wxString) *i) != wxNOT_FOUND) - // one character of 'val' is present in m_excludes... - return true; - - // all characters of 'val' are NOT present in m_excludes - return false; -} void wxTextValidator::SetCharIncludes(const wxString& chars) { - wxArrayString arr; + m_charIncludes.clear(); - for ( wxString::const_iterator i = chars.begin(); i != chars.end(); ++i ) - arr.Add(*i); + AddCharIncludes(chars); +} - SetIncludes(arr); +void wxTextValidator::AddCharIncludes(const wxString& chars) +{ + m_charIncludes += chars; } void wxTextValidator::SetCharExcludes(const wxString& chars) { - wxArrayString arr; + m_charExcludes.clear(); - for ( wxString::const_iterator i = chars.begin(); i != chars.end(); ++i ) - arr.Add(*i); + AddCharExcludes(chars); +} - SetExcludes(arr); +void wxTextValidator::AddCharExcludes(const wxString& chars) +{ + m_charExcludes += chars; +} + +void wxTextValidator::SetIncludes(const wxArrayString& includes) +{ + // preserve compatibily with versions prior 3.1.3 which used m_includes + // to store the list of char includes. + if ( HasFlag(wxFILTER_INCLUDE_CHAR_LIST) ) + { + for ( wxArrayString::const_iterator i = includes.begin(), + end = includes.end(); i != end; ++i ) + { + AddCharIncludes(*i); + } + + return; + } + + m_includes = includes; +} + +void wxTextValidator::AddInclude(const wxString& include) +{ + m_includes.push_back(include); +} + +void wxTextValidator::SetExcludes(const wxArrayString& excludes) +{ + // preserve compatibily with versions prior 3.1.3 which used m_excludes + // to store the list of char excludes. + if ( HasFlag(wxFILTER_EXCLUDE_CHAR_LIST) ) + { + for ( wxArrayString::const_iterator i = excludes.begin(), + end = excludes.end(); i != end; ++i ) + { + AddCharExcludes(*i); + } + + return; + } + + m_excludes = excludes; +} + +void wxTextValidator::AddExclude(const wxString& exclude) +{ + m_excludes.push_back(exclude); } void wxTextValidator::OnChar(wxKeyEvent& event) @@ -313,7 +288,7 @@ void wxTextValidator::OnChar(wxKeyEvent& event) int keyCode = event.GetUnicodeKey(); #else // !wxUSE_UNICODE int keyCode = event.GetKeyCode(); - if (keyCode > WXK_START) + if ( keyCode > WXK_START ) return; #endif // wxUSE_UNICODE/!wxUSE_UNICODE @@ -321,8 +296,8 @@ void wxTextValidator::OnChar(wxKeyEvent& event) if (keyCode < WXK_SPACE || keyCode == WXK_DELETE) return; - wxString str((wxUniChar)keyCode, 1); - if (IsValid(str).empty()) + // Filter out invalid characters + if ( IsValidChar(static_cast(keyCode)) ) return; if ( !wxValidator::IsSilent() ) @@ -332,6 +307,71 @@ void wxTextValidator::OnChar(wxKeyEvent& event) event.Skip(false); } +bool wxTextValidator::IsValidChar(const wxUniChar& c) const +{ + if ( !m_validatorStyle ) // no filtering if HasFlag(wxFILTER_NONE) + return true; + + if ( IsCharExcluded(c) ) // disallow any char in the m_charExcludes. + return false; + + if ( IsCharIncluded(c) ) // allow any char in the m_charIncludes. + return true; + + if ( !HasFlag(wxFILTER_CC) ) + { + // Validity is entirely determined by the exclude/include char lists + // and this character is in neither, so consider that it is valid if + // and only if we accept anything. + return !HasFlag(wxFILTER_INCLUDE_CHAR_LIST); + } + + if ( HasFlag(wxFILTER_SPACE) && wxIsspace(c) ) + return true; + if ( HasFlag(wxFILTER_ASCII) && c.IsAscii() ) + return true; + if ( HasFlag(wxFILTER_NUMERIC) && wxIsNumeric(c) ) + return true; + if ( HasFlag(wxFILTER_ALPHANUMERIC) && wxIsalnum(c) ) + return true; + if ( HasFlag(wxFILTER_ALPHA) && wxIsalpha(c) ) + return true; + if ( HasFlag(wxFILTER_DIGITS) && wxIsdigit(c) ) + return true; + if ( HasFlag(wxFILTER_XDIGITS) && wxIsxdigit(c) ) + return true; + + // If we are here, this means that the char c does not belong to any of the + // character classes checked above (e.g. emoji chars) so just return false. + + return false; +} + +// kept for compatibility reasons. +bool wxTextValidator::ContainsOnlyIncludedCharacters(const wxString& str) const +{ + for ( wxString::const_iterator i = str.begin(), end = str.end(); + i != end; ++i ) + { + if ( !IsCharIncluded(*i) ) + return false; + } + + return true; +} + +// kept for compatibility reasons. +bool wxTextValidator::ContainsExcludedCharacters(const wxString& str) const +{ + for ( wxString::const_iterator i = str.begin(), end = str.end(); + i != end; ++i ) + { + if ( IsCharExcluded(*i) ) + return true; + } + + return false; +} #endif // wxUSE_VALIDATORS && (wxUSE_TEXTCTRL || wxUSE_COMBOBOX) diff --git a/src/generic/datectlg.cpp b/src/generic/datectlg.cpp index 7a405e2c9b..df0ac9c572 100644 --- a/src/generic/datectlg.cpp +++ b/src/generic/datectlg.cpp @@ -262,9 +262,7 @@ private: if ( m_combo ) { - wxArrayString allowedChars; - for ( wxChar c = wxT('0'); c <= wxT('9'); c++ ) - allowedChars.Add(wxString(c, 1)); + wxString allowedChars = wxS("0123456789"); const wxChar *p2 = m_format.c_str(); while ( *p2 ) @@ -272,12 +270,12 @@ private: if ( *p2 == '%') p2 += 2; else - allowedChars.Add(wxString(*p2++, 1)); + allowedChars << (*p2++); // append char } #if wxUSE_VALIDATORS wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST); - tv.SetIncludes(allowedChars); + tv.SetCharIncludes(allowedChars); m_combo->SetValidator(tv); #endif diff --git a/src/propgrid/props.cpp b/src/propgrid/props.cpp index 2587f841b5..ba3889d90e 100644 --- a/src/propgrid/props.cpp +++ b/src/propgrid/props.cpp @@ -160,47 +160,41 @@ wxNumericPropertyValidator:: wxNumericPropertyValidator( NumericType numericType, int base ) : wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST) { - wxArrayString arr; - arr.Add(wxS("0")); - arr.Add(wxS("1")); - arr.Add(wxS("2")); - arr.Add(wxS("3")); - arr.Add(wxS("4")); - arr.Add(wxS("5")); - arr.Add(wxS("6")); - arr.Add(wxS("7")); + long style = GetStyle(); - if ( base >= 10 ) + // always allow plus and minus signs + wxString allowedChars("+-"); + + switch ( base ) { - arr.Add(wxS("8")); - arr.Add(wxS("9")); - if ( base >= 16 ) - { - arr.Add(wxS("a")); arr.Add(wxS("A")); - arr.Add(wxS("b")); arr.Add(wxS("B")); - arr.Add(wxS("c")); arr.Add(wxS("C")); - arr.Add(wxS("d")); arr.Add(wxS("D")); - arr.Add(wxS("e")); arr.Add(wxS("E")); - arr.Add(wxS("f")); arr.Add(wxS("F")); - } + case 2: + allowedChars += wxS("01"); + break; + case 8: + allowedChars += wxS("01234567"); + break; + case 10: + style |= wxFILTER_DIGITS; + break; + case 16: + style |= wxFILTER_XDIGITS; + break; + + default: + wxLogWarning( _("Unknown base %d. Base 10 will be used."), base ); + style |= wxFILTER_DIGITS; } - if ( numericType == Signed ) + if ( numericType == Float ) { - arr.Add(wxS("+")); - arr.Add(wxS("-")); - } - else if ( numericType == Float ) - { - arr.Add(wxS("+")); - arr.Add(wxS("-")); - arr.Add(wxS("e")); arr.Add(wxS("E")); + allowedChars += wxS("eE"); // Use locale-specific decimal point - arr.Add(wxString::Format(wxS("%g"), 1.1)[1]); + allowedChars += wxString::Format(wxS("%g"), 1.1)[1]; } - SetIncludes(arr); + SetStyle(style); + SetCharIncludes(allowedChars); } bool wxNumericPropertyValidator::Validate(wxWindow* parent) @@ -2021,15 +2015,7 @@ wxValidator* wxFileProperty::GetClassValidator() static wxString v; wxTextValidator* validator = new wxTextValidator(wxFILTER_EXCLUDE_CHAR_LIST,&v); - wxArrayString exChars; - exChars.Add(wxS("?")); - exChars.Add(wxS("*")); - exChars.Add(wxS("|")); - exChars.Add(wxS("<")); - exChars.Add(wxS(">")); - exChars.Add(wxS("\"")); - - validator->SetExcludes(exChars); + validator->SetCharExcludes(wxString("?*|<>\"")); WX_PG_DOGETVALIDATOR_EXIT(validator) #else diff --git a/tests/Makefile.in b/tests/Makefile.in index 9fdee154fb..14e4f749e9 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -260,6 +260,7 @@ TEST_GUI_OBJECTS = \ test_gui_wrapsizer.o \ test_gui_toplevel.o \ test_gui_valnum.o \ + test_gui_valtext.o \ test_gui_clientsize.o \ test_gui_setsize.o \ test_gui_xrctest.o @@ -1052,6 +1053,9 @@ test_gui_toplevel.o: $(srcdir)/toplevel/toplevel.cpp $(TEST_GUI_ODEP) test_gui_valnum.o: $(srcdir)/validators/valnum.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/validators/valnum.cpp +test_gui_valtext.o: $(srcdir)/validators/valtext.cpp $(TEST_GUI_ODEP) + $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/validators/valtext.cpp + test_gui_clientsize.o: $(srcdir)/window/clientsize.cpp $(TEST_GUI_ODEP) $(CXXC) -c -o $@ $(TEST_GUI_CXXFLAGS) $(srcdir)/window/clientsize.cpp diff --git a/tests/makefile.bcc b/tests/makefile.bcc index 97c1cdd1fb..51c88f2ddd 100644 --- a/tests/makefile.bcc +++ b/tests/makefile.bcc @@ -247,6 +247,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_wrapsizer.obj \ $(OBJS)\test_gui_toplevel.obj \ $(OBJS)\test_gui_valnum.obj \ + $(OBJS)\test_gui_valtext.obj \ $(OBJS)\test_gui_clientsize.obj \ $(OBJS)\test_gui_setsize.obj \ $(OBJS)\test_gui_xrctest.obj @@ -1107,6 +1108,9 @@ $(OBJS)\test_gui_toplevel.obj: .\toplevel\toplevel.cpp $(OBJS)\test_gui_valnum.obj: .\validators\valnum.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\validators\valnum.cpp +$(OBJS)\test_gui_valtext.obj: .\validators\valtext.cpp + $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\validators\valtext.cpp + $(OBJS)\test_gui_clientsize.obj: .\window\clientsize.cpp $(CXX) -q -c -P -o$@ $(TEST_GUI_CXXFLAGS) .\window\clientsize.cpp diff --git a/tests/makefile.gcc b/tests/makefile.gcc index 1e70e93aaa..c7894bdeb1 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -242,6 +242,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_wrapsizer.o \ $(OBJS)\test_gui_toplevel.o \ $(OBJS)\test_gui_valnum.o \ + $(OBJS)\test_gui_valtext.o \ $(OBJS)\test_gui_clientsize.o \ $(OBJS)\test_gui_setsize.o \ $(OBJS)\test_gui_xrctest.o @@ -1084,6 +1085,9 @@ $(OBJS)\test_gui_toplevel.o: ./toplevel/toplevel.cpp $(OBJS)\test_gui_valnum.o: ./validators/valnum.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\test_gui_valtext.o: ./validators/valtext.cpp + $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< + $(OBJS)\test_gui_clientsize.o: ./window/clientsize.cpp $(CXX) -c -o $@ $(TEST_GUI_CXXFLAGS) $(CPPDEPS) $< diff --git a/tests/makefile.vc b/tests/makefile.vc index 204a77a4f8..5e104bd30e 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -253,6 +253,7 @@ TEST_GUI_OBJECTS = \ $(OBJS)\test_gui_wrapsizer.obj \ $(OBJS)\test_gui_toplevel.obj \ $(OBJS)\test_gui_valnum.obj \ + $(OBJS)\test_gui_valtext.obj \ $(OBJS)\test_gui_clientsize.obj \ $(OBJS)\test_gui_setsize.obj \ $(OBJS)\test_gui_xrctest.obj @@ -1298,6 +1299,9 @@ $(OBJS)\test_gui_toplevel.obj: .\toplevel\toplevel.cpp $(OBJS)\test_gui_valnum.obj: .\validators\valnum.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\validators\valnum.cpp +$(OBJS)\test_gui_valtext.obj: .\validators\valtext.cpp + $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\validators\valtext.cpp + $(OBJS)\test_gui_clientsize.obj: .\window\clientsize.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_GUI_CXXFLAGS) .\window\clientsize.cpp diff --git a/tests/test.bkl b/tests/test.bkl index 10d70213c2..8acc4145ff 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -272,6 +272,7 @@ sizers/wrapsizer.cpp toplevel/toplevel.cpp validators/valnum.cpp + validators/valtext.cpp window/clientsize.cpp window/setsize.cpp xml/xrctest.cpp diff --git a/tests/test_vc7_test_gui.vcproj b/tests/test_vc7_test_gui.vcproj index f52b5ad5c6..7f908ef8e5 100644 --- a/tests/test_vc7_test_gui.vcproj +++ b/tests/test_vc7_test_gui.vcproj @@ -595,6 +595,9 @@ + + diff --git a/tests/test_vc8_test_gui.vcproj b/tests/test_vc8_test_gui.vcproj index 2b7d9b7722..86ddb5dea7 100644 --- a/tests/test_vc8_test_gui.vcproj +++ b/tests/test_vc8_test_gui.vcproj @@ -1262,6 +1262,10 @@ RelativePath=".\validators\valnum.cpp" > + + diff --git a/tests/test_vc9_test_gui.vcproj b/tests/test_vc9_test_gui.vcproj index f698792488..25d5167c03 100644 --- a/tests/test_vc9_test_gui.vcproj +++ b/tests/test_vc9_test_gui.vcproj @@ -1234,6 +1234,10 @@ RelativePath=".\validators\valnum.cpp" > + + diff --git a/tests/validators/valtext.cpp b/tests/validators/valtext.cpp new file mode 100644 index 0000000000..9c2dbb334f --- /dev/null +++ b/tests/validators/valtext.cpp @@ -0,0 +1,190 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/validators/valtext.cpp +// Purpose: wxTextValidator unit test +// Author: Ali Kettab +// Created: 2019-01-01 +/////////////////////////////////////////////////////////////////////////////// + +#include "testprec.h" + +#if wxUSE_VALIDATORS && wxUSE_UIACTIONSIMULATOR + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/app.h" + #include "wx/textctrl.h" + #include "wx/valtext.h" +#endif // WX_PRECOMP + +#include "wx/uiaction.h" + +class TextValidatorTestCase +{ +public: + TextValidatorTestCase() + : m_text(new wxTextCtrl(wxTheApp->GetTopWindow(), wxID_ANY)) + { + } + + ~TextValidatorTestCase() + { + delete m_text; + } + +protected: + wxTextCtrl* const m_text; +}; + +#define TEXT_VALIDATOR_TEST_CASE(name, tags) \ + TEST_CASE_METHOD(TextValidatorTestCase, name, tags) + +TEXT_VALIDATOR_TEST_CASE("wxTextValidator::IsValid", "[wxTextValidator][filters]") +{ + wxString value = ""; + wxTextValidator val(wxFILTER_NONE, &value); + + SECTION("wxFILTER_NONE - no filtering should take place") + { + CHECK( val.IsValid("wx-90.?! @_~E+{").empty() ); + } + + SECTION("wxFILTER_EMPTY - empty strings are filtered out") + { + val.SetStyle(wxFILTER_EMPTY); + + CHECK( !val.IsValid("").empty() ); + CHECK( val.IsValid(" ").empty() ); // space is valid + } + + SECTION("wxFILTER_ASCII - non-ASCII characters are filtered out") + { + val.SetStyle(wxFILTER_ASCII); + + CHECK( val.IsValid("wx-90.?! @_~E+{").empty() ); + } + + SECTION("wxFILTER_ALPHA - non-alpha characters are filtered out") + { + val.SetStyle(wxFILTER_ALPHA); + + CHECK( val.IsValid("wx").empty() ); + CHECK( !val.IsValid("wx_").empty() ); // _ is not alpha + } + + SECTION("wxFILTER_ALPHANUMERIC - non-alphanumeric characters are filtered out") + { + val.SetStyle(wxFILTER_ALPHANUMERIC); + + CHECK( val.IsValid("wx01").empty() ); + CHECK( !val.IsValid("wx 01").empty() ); // 'space' is not alphanumeric + } + + SECTION("wxFILTER_DIGITS - non-digit characters are filtered out") + { + val.SetStyle(wxFILTER_DIGITS); + + CHECK( val.IsValid("97").empty() ); + CHECK( !val.IsValid("9.7").empty() ); // . is not digit + } + + SECTION("wxFILTER_XDIGITS - non-xdigit characters are filtered out") + { + val.SetStyle(wxFILTER_XDIGITS); + + CHECK( val.IsValid("90AEF").empty() ); + CHECK( !val.IsValid("90GEF").empty() ); // G is not xdigit + } + + SECTION("wxFILTER_NUMERIC - non-numeric characters are filtered out") + { + val.SetStyle(wxFILTER_NUMERIC); + + CHECK( val.IsValid("+90.e-2").empty() ); + CHECK( !val.IsValid("-8.5#0").empty() ); // # is not numeric + } + + SECTION("wxFILTER_INCLUDE_LIST - use include list") + { + val.SetStyle(wxFILTER_INCLUDE_LIST); + + wxArrayString includes; + includes.push_back("wxMSW"); + includes.push_back("wxGTK"); + includes.push_back("wxOSX"); + val.SetIncludes(includes); + + CHECK( val.IsValid("wxGTK").empty() ); + CHECK( !val.IsValid("wxQT").empty() ); // wxQT is not included + + SECTION("wxFILTER_EXCLUDE_LIST - use exclude with include list") + { + wxArrayString excludes; + excludes.push_back("wxGTK"); + excludes.push_back("wxGTK1"); + val.SetExcludes(excludes); + + CHECK( val.IsValid("wxOSX").empty() ); + CHECK( !val.IsValid("wxGTK").empty() ); // wxGTK now excluded + } + } + + SECTION("wxFILTER_EXCLUDE_LIST - use exclude list") + { + val.SetStyle(wxFILTER_EXCLUDE_LIST); + + wxArrayString excludes; + excludes.push_back("wxMSW"); + excludes.push_back("wxGTK"); + excludes.push_back("wxOSX"); + val.SetExcludes(excludes); + + CHECK( val.IsValid("wxQT & wxUNIV").empty() ); + CHECK( !val.IsValid("wxMSW").empty() ); // wxMSW is excluded + + SECTION("wxFILTER_INCLUDE_LIST - use include with exclude list") + { + wxArrayString includes; + includes.push_back("wxGTK"); + val.SetIncludes(includes); // exclusion takes priority over inclusion. + + CHECK( val.IsValid("wxUNIV").empty() ); + CHECK( !val.IsValid("wxMSW").empty() ); // wxMSW still excluded + } + } + + SECTION("wxFILTER_INCLUDE_CHAR_LIST - use include char list") + { + val.SetStyle(wxFILTER_INCLUDE_CHAR_LIST); + val.SetCharIncludes("tuvwxyz.012+-"); + + CHECK( val.IsValid("0.2t+z-1").empty() ); + CHECK( !val.IsValid("x*y").empty() ); // * is not included + + val.AddCharIncludes("*"); + + CHECK( val.IsValid("x*y").empty() ); // * now included + CHECK( !val.IsValid("x%y").empty() ); // % is not included + + val.AddCharExcludes("*"); // exclusion takes priority over inclusion. + + CHECK( !val.IsValid("x*y").empty() ); // * now excluded + } + + SECTION("wxFILTER_EXCLUDE_CHAR_LIST - use exclude char list") + { + val.SetStyle(wxFILTER_EXCLUDE_CHAR_LIST); + val.SetCharExcludes("tuvwxyz.012+-"); + + CHECK( val.IsValid("A*B=?").empty() ); + CHECK( !val.IsValid("0.6/t").empty() ); // t is excluded + + val.AddCharIncludes("t"); // exclusion takes priority over inclusion. + + CHECK( !val.IsValid("0.6/t").empty() ); // t still excluded + } +} + +#endif // wxUSE_VALIDATORS && wxUSE_UIACTIONSIMULATOR