]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/valnum.cpp
Add wxIntegerValidator and wxFloatingPointValidator classes.
[wxWidgets.git] / src / common / valnum.cpp
diff --git a/src/common/valnum.cpp b/src/common/valnum.cpp
new file mode 100644 (file)
index 0000000..8a84e4f
--- /dev/null
@@ -0,0 +1,299 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        src/common/valnumtext.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
+// ============================================================================
+
+BEGIN_EVENT_TABLE(wxNumValidatorBase, wxValidator)
+    EVT_CHAR(wxNumValidatorBase::OnChar)
+    EVT_KILL_FOCUS(wxNumValidatorBase::OnKillFocus)
+END_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