///////////////////////////////////////////////////////////////////////////// // Name: src/common/valnum.cpp // Purpose: Numeric validator classes implementation // Author: Vadim Zeitlin based on the submission of Fulvio Senore // Created: 2010-11-06 // Copyright: (c) 2010 wxWidgets team // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ============================================================================ // Declarations // ============================================================================ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #if wxUSE_VALIDATORS && wxUSE_TEXTCTRL #ifndef WX_PRECOMP #include "wx/textctrl.h" #include "wx/combobox.h" #endif #include "wx/valnum.h" #include "wx/numformatter.h" // ============================================================================ // wxNumValidatorBase implementation // ============================================================================ wxBEGIN_EVENT_TABLE(wxNumValidatorBase, wxValidator) EVT_CHAR(wxNumValidatorBase::OnChar) EVT_KILL_FOCUS(wxNumValidatorBase::OnKillFocus) wxEND_EVENT_TABLE() int wxNumValidatorBase::GetFormatFlags() const { int flags = wxNumberFormatter::Style_None; if ( m_style & wxNUM_VAL_THOUSANDS_SEPARATOR ) flags |= wxNumberFormatter::Style_WithThousandsSep; if ( m_style & wxNUM_VAL_NO_TRAILING_ZEROES ) flags |= wxNumberFormatter::Style_NoTrailingZeroes; return flags; } wxTextEntry *wxNumValidatorBase::GetTextEntry() const { #if wxUSE_TEXTCTRL if ( wxTextCtrl *text = wxDynamicCast(m_validatorWindow, wxTextCtrl) ) return text; #endif // wxUSE_TEXTCTRL #if wxUSE_COMBOBOX if ( wxComboBox *combo = wxDynamicCast(m_validatorWindow, wxComboBox) ) return combo; #endif // wxUSE_COMBOBOX wxFAIL_MSG("Can only be used with wxTextCtrl or wxComboBox"); return NULL; } void wxNumValidatorBase::GetCurrentValueAndInsertionPoint(wxString& val, int& pos) const { wxTextEntry * const control = GetTextEntry(); if ( !control ) return; val = control->GetValue(); pos = control->GetInsertionPoint(); long selFrom, selTo; control->GetSelection(&selFrom, &selTo); const long selLen = selTo - selFrom; if ( selLen ) { // Remove selected text because pressing a key would make it disappear. val.erase(selFrom, selLen); // And adjust the insertion point to have correct position in the new // string. if ( pos > selFrom ) { if ( pos >= selTo ) pos -= selLen; else pos = selFrom; } } } bool wxNumValidatorBase::IsMinusOk(const wxString& val, int pos) const { // Minus is only ever accepted in the beginning of the string. if ( pos != 0 ) return false; // And then only if there is no existing minus sign there. if ( !val.empty() && val[0] == '-' ) return false; return true; } void wxNumValidatorBase::OnChar(wxKeyEvent& event) { // By default we just validate this key so don't prevent the normal // handling from taking place. event.Skip(); if ( !m_validatorWindow ) return; #if wxUSE_UNICODE const int ch = event.GetUnicodeKey(); if ( ch == WXK_NONE ) { // It's a character without any Unicode equivalent at all, e.g. cursor // arrow or function key, we never filter those. return; } #else // !wxUSE_UNICODE const int ch = event.GetKeyCode(); if ( ch > WXK_DELETE ) { // Not a character neither. return; } #endif // wxUSE_UNICODE/!wxUSE_UNICODE if ( ch < WXK_SPACE || ch == WXK_DELETE ) { // Allow ASCII control characters and Delete. return; } // Check if this character is allowed in the current state. wxString val; int pos; GetCurrentValueAndInsertionPoint(val, pos); if ( !IsCharOk(val, pos, ch) ) { if ( !wxValidator::IsSilent() ) wxBell(); // Do not skip the event in this case, stop handling it here. event.Skip(false); } } void wxNumValidatorBase::OnKillFocus(wxFocusEvent& event) { wxTextEntry * const control = GetTextEntry(); if ( !control ) return; // When we change the control value below, its "modified" status is reset // so we need to explicitly keep it marked as modified if it was so in the // first place. // // Notice that only wxTextCtrl (and not wxTextEntry) has // IsModified()/MarkDirty() methods hence the need for dynamic cast. wxTextCtrl * const text = wxDynamicCast(m_validatorWindow, wxTextCtrl); const bool wasModified = text ? text->IsModified() : false; control->ChangeValue(NormalizeString(control->GetValue())); if ( wasModified ) text->MarkDirty(); event.Skip(); } // ============================================================================ // wxIntegerValidatorBase implementation // ============================================================================ wxString wxIntegerValidatorBase::ToString(LongestValueType value) const { return wxNumberFormatter::ToString(value, GetFormatFlags()); } bool wxIntegerValidatorBase::FromString(const wxString& s, LongestValueType *value) { return wxNumberFormatter::FromString(s, value); } bool wxIntegerValidatorBase::IsCharOk(const wxString& val, int pos, wxChar ch) const { // We may accept minus sign if we can represent negative numbers at all. if ( ch == '-' ) { // Notice that entering '-' can make our value invalid, for example if // we're limited to -5..15 range and the current value is 12, then the // new value would be (invalid) -12. We consider it better to let the // user do this because perhaps he is going to press Delete key next to // make it -2 and forcing him to delete 1 first would be unnatural. // // TODO: It would be nice to indicate that the current control contents // is invalid (if it's indeed going to be the case) once // wxValidator supports doing this non-intrusively. return m_min < 0 && IsMinusOk(val, pos); } // We only accept digits here (remember that '-' is taken care of by the // base class already). if ( ch < '0' || ch > '9' ) return false; // And the value after insertion needs to be in the defined range. LongestValueType value; if ( !FromString(GetValueAfterInsertingChar(val, pos, ch), &value) ) return false; return IsInRange(value); } // ============================================================================ // wxFloatingPointValidatorBase implementation // ============================================================================ wxString wxFloatingPointValidatorBase::ToString(LongestValueType value) const { return wxNumberFormatter::ToString(value, m_precision, GetFormatFlags()); } bool wxFloatingPointValidatorBase::FromString(const wxString& s, LongestValueType *value) { return wxNumberFormatter::FromString(s, value); } bool wxFloatingPointValidatorBase::IsCharOk(const wxString& val, int pos, wxChar ch) const { // We may accept minus sign if we can represent negative numbers at all. if ( ch == '-' ) return m_min < 0 && IsMinusOk(val, pos); const wxChar separator = wxNumberFormatter::GetDecimalSeparator(); if ( ch == separator ) { if ( val.find(separator) != wxString::npos ) { // There is already a decimal separator, can't insert another one. return false; } // Prepending a separator before the minus sign isn't allowed. if ( pos == 0 && !val.empty() && val[0] == '-' ) return false; // Otherwise always accept it, adding a decimal separator doesn't // change the number value and, in particular, can't make it invalid. // OTOH the checks below might not pass because strings like "." or // "-." are not valid numbers so parsing them would fail, hence we need // to treat it specially here. return true; } // Must be a digit then. if ( ch < '0' || ch > '9' ) return false; // Check whether the value we'd obtain if we accepted this key is correct. const wxString newval(GetValueAfterInsertingChar(val, pos, ch)); LongestValueType value; if ( !FromString(newval, &value) ) return false; // Also check that it doesn't have too many decimal digits. const size_t posSep = newval.find(separator); if ( posSep != wxString::npos && newval.length() - posSep - 1 > m_precision ) return false; // Finally check whether it is in the range. return IsInRange(value); } #endif // wxUSE_VALIDATORS && wxUSE_TEXTCTRL