]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/numformatter.cpp
Add wxNumberFormatter class helping to deal with thousands separators.
[wxWidgets.git] / src / common / numformatter.cpp
diff --git a/src/common/numformatter.cpp b/src/common/numformatter.cpp
new file mode 100644 (file)
index 0000000..851259c
--- /dev/null
@@ -0,0 +1,190 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        numformatter.cpp
+// Purpose:     wxNumberFormatter
+// Author:      Fulvio Senore, Vadim Zeitlin
+// Created:     2010-11-06
+// Copyright:   (c) 2010 wxWidgets team
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#include "wx/numformatter.h"
+#include "wx/intl.h"
+
+// ============================================================================
+// wxNumberFormatter implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// Locale information accessors
+// ----------------------------------------------------------------------------
+
+wxChar wxNumberFormatter::GetDecimalSeparator()
+{
+    // Notice that while using static variable here is not MT-safe, the worst
+    // that can happen is that we redo the initialization if we're called
+    // concurrently from more than one thread so it's not a real problem.
+    static wxChar s_decimalSeparator = 0;
+
+    if ( !s_decimalSeparator )
+    {
+        const wxString
+            s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
+        if ( s.empty() )
+        {
+            // We really must have something for decimal separator, so fall
+            // back to the C locale default.
+            s_decimalSeparator = '.';
+        }
+        else
+        {
+            // To the best of my knowledge there are no locales like this.
+            wxASSERT_MSG( s.length() == 1,
+                          "Multi-character decimal separator?" );
+
+            s_decimalSeparator = s[0];
+        }
+    }
+
+    return s_decimalSeparator;
+}
+
+bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
+{
+    static wxChar s_thousandsSeparator = 0;
+    static bool s_initialized = false;
+
+    if ( !s_initialized )
+    {
+        const wxString
+            s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER);
+        if ( !s.empty() )
+        {
+            wxASSERT_MSG( s.length() == 1,
+                          "Multi-character thousands separator?" );
+
+            s_thousandsSeparator = s[0];
+        }
+        //else: Unlike above it's perfectly fine for the thousands separator to
+        //      be empty if grouping is not used, so just leave it as 0.
+
+        s_initialized = true;
+    }
+
+    if ( !s_thousandsSeparator )
+        return false;
+
+    if ( sep )
+        *sep = s_thousandsSeparator;
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+// Conversion to string and helpers
+// ----------------------------------------------------------------------------
+
+wxString wxNumberFormatter::ToString(long val, int style)
+{
+    wxString s = wxString::Format("%ld", val);
+
+    if ( style & Style_WithThousandsSep )
+        AddThousandsSeparators(s);
+
+    wxASSERT_MSG( !(style & Style_NoTrailingZeroes),
+                  "Style_NoTrailingZeroes can't be used with integer values" );
+
+    return s;
+}
+
+wxString wxNumberFormatter::ToString(double val, int precision, int style)
+{
+    const wxString fmt = wxString::Format("%%.%df", precision);
+    wxString s = wxString::Format(fmt, val);
+
+    if ( style & Style_WithThousandsSep )
+        AddThousandsSeparators(s);
+
+    if ( style & Style_NoTrailingZeroes )
+        RemoveTrailingZeroes(s);
+
+    return s;
+}
+
+void wxNumberFormatter::AddThousandsSeparators(wxString& s)
+{
+    wxChar thousandsSep;
+    if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
+        return;
+
+    size_t pos = s.find(GetDecimalSeparator());
+    if ( pos == wxString::npos )
+    {
+        // Start grouping at the end of an integer number.
+        pos = s.length();
+    }
+
+    // We currently group digits by 3 independently of the locale. This is not
+    // the right thing to do and we should use lconv::grouping (under POSIX)
+    // and GetLocaleInfo(LOCALE_SGROUPING) (under MSW) to get information about
+    // the correct grouping to use. This is something that needs to be done at
+    // wxLocale level first and then used here in the future (TODO).
+    const size_t GROUP_LEN = 3;
+
+    while ( pos > GROUP_LEN )
+    {
+        pos -= GROUP_LEN;
+        s.insert(pos, thousandsSep);
+    }
+}
+
+void wxNumberFormatter::RemoveTrailingZeroes(wxString& s)
+{
+    const size_t posDecSep = s.find(GetDecimalSeparator());
+    wxCHECK_RET( posDecSep != wxString::npos, "No decimal separator" );
+    wxCHECK_RET( posDecSep, "Can't start with decimal separator" );
+
+    // Find the last character to keep.
+    size_t posLastNonZero = s.find_last_not_of("0");
+
+    // If it's the decimal separator itself, don't keep it neither.
+    if ( posLastNonZero == posDecSep )
+        posLastNonZero--;
+
+    s.erase(posLastNonZero + 1);
+}
+
+// ----------------------------------------------------------------------------
+// Conversion from strings
+// ----------------------------------------------------------------------------
+
+void wxNumberFormatter::RemoveThousandsSeparators(wxString& s)
+{
+    wxChar thousandsSep;
+    if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
+        return;
+
+    s.Replace(wxString(thousandsSep), wxString());
+}
+
+bool wxNumberFormatter::FromString(wxString s, long *val)
+{
+    RemoveThousandsSeparators(s);
+    return s.ToLong(val);
+}
+
+bool wxNumberFormatter::FromString(wxString s, double *val)
+{
+    RemoveThousandsSeparators(s);
+    return s.ToDouble(val);
+}