/////////////////////////////////////////////////////////////////////////////
-// Name: numformatter.cpp
+// Name: src/common/numformatter.cpp
// Purpose: wxNumberFormatter
// Author: Fulvio Senore, Vadim Zeitlin
// Created: 2010-11-06
#include "wx/numformatter.h"
#include "wx/intl.h"
+#include <locale.h> // for setlocale and LC_ALL
+
+// ----------------------------------------------------------------------------
+// local helpers
+// ----------------------------------------------------------------------------
+
+namespace
+{
+
+// Contains information about the locale which was used to initialize our
+// cached values of the decimal and thousands separators. Notice that it isn't
+// enough to store just wxLocale because the user code may call setlocale()
+// directly and storing just C locale string is not enough because we can use
+// the OS API directly instead of the CRT ones on some platforms. So just store
+// both.
+class LocaleId
+{
+public:
+ LocaleId()
+ {
+#if wxUSE_INTL
+ m_wxloc = NULL;
+#endif // wxUSE_INTL
+ m_cloc = NULL;
+ }
+
+ ~LocaleId()
+ {
+ Free();
+ }
+
+#if wxUSE_INTL
+ // Return true if this is the first time this function is called for this
+ // object or if the program locale has changed since the last time it was
+ // called. Otherwise just return false indicating that updating locale-
+ // dependent information is not necessary.
+ bool NotInitializedOrHasChanged()
+ {
+ wxLocale * const wxloc = wxGetLocale();
+ const char * const cloc = setlocale(LC_ALL, NULL);
+ if ( m_wxloc || m_cloc )
+ {
+ if ( m_wxloc == wxloc && strcmp(m_cloc, cloc) == 0 )
+ return false;
+
+ Free();
+ }
+ //else: Not initialized yet.
+
+ m_wxloc = wxloc;
+ m_cloc = wxCRT_StrdupA(cloc);
+
+ return true;
+ }
+#endif // wxUSE_INTL
+
+private:
+ void Free()
+ {
+#if wxUSE_INTL
+ free(m_cloc);
+#endif // wxUSE_INTL
+ }
+
+#if wxUSE_INTL
+ // Non-owned pointer to wxLocale which was used.
+ wxLocale *m_wxloc;
+#endif // wxUSE_INTL
+
+ // Owned pointer to the C locale string.
+ char *m_cloc;
+
+ wxDECLARE_NO_COPY_CLASS(LocaleId);
+};
+
+} // anonymous namespace
+
// ============================================================================
// wxNumberFormatter implementation
// ============================================================================
wxChar wxNumberFormatter::GetDecimalSeparator()
{
+#if wxUSE_INTL
// 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 )
+ // Remember the locale which was current when we initialized, we must redo
+ // the initialization if the locale changed.
+ static LocaleId s_localeUsedForInit;
+
+ if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
{
const wxString
s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
}
return s_decimalSeparator;
+#else // !wxUSE_INTL
+ return wxT('.');
+#endif // wxUSE_INTL/!wxUSE_INTL
}
bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
{
+#if wxUSE_INTL
static wxChar s_thousandsSeparator = 0;
- static bool s_initialized = false;
+ static LocaleId s_localeUsedForInit;
- if ( !s_initialized )
+ if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
{
const wxString
s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER);
}
//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 )
*sep = s_thousandsSeparator;
return true;
+#else // !wxUSE_INTL
+ wxUnusedVar(sep);
+ return false;
+#endif // wxUSE_INTL/!wxUSE_INTL
}
// ----------------------------------------------------------------------------
// Conversion to string and helpers
// ----------------------------------------------------------------------------
-wxString wxNumberFormatter::ToString(long val, int style)
+wxString wxNumberFormatter::PostProcessIntString(wxString s, int style)
{
- wxString s = wxString::Format("%ld", val);
-
if ( style & Style_WithThousandsSep )
AddThousandsSeparators(s);
return s;
}
+wxString wxNumberFormatter::ToString(long val, int style)
+{
+ return PostProcessIntString(wxString::Format("%ld", val), style);
+}
+
+#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+wxString wxNumberFormatter::ToString(wxLongLong_t val, int style)
+{
+ return PostProcessIntString(wxString::Format("%" wxLongLongFmtSpec "d", val),
+ style);
+}
+
+#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
wxString wxNumberFormatter::ToString(double val, int precision, int style)
{
- const wxString fmt = wxString::Format("%%.%df", precision);
- wxString s = wxString::Format(fmt, val);
+ wxString s = wxString::FromDouble(val,precision);
if ( style & Style_WithThousandsSep )
AddThousandsSeparators(s);
pos = s.length();
}
+ // End grouping at the beginning of the digits -- there could be at a sign
+ // before their start.
+ const size_t start = s.find_first_of("0123456789");
+
// 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
// wxLocale level first and then used here in the future (TODO).
const size_t GROUP_LEN = 3;
- while ( pos > GROUP_LEN )
+ while ( pos > start + 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 != wxString::npos,
+ wxString::Format("No decimal separator in \"%s\"", s) );
wxCHECK_RET( posDecSep, "Can't start with decimal separator" );
// Find the last character to keep.
return s.ToLong(val);
}
+#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+bool wxNumberFormatter::FromString(wxString s, wxLongLong_t *val)
+{
+ RemoveThousandsSeparators(s);
+ return s.ToLongLong(val);
+}
+
+#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
bool wxNumberFormatter::FromString(wxString s, double *val)
{
RemoveThousandsSeparators(s);