X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6686fbad16ec929f33cbbcc1b638bf00406218c5..87f0b1323b7ac77f02133b836c8dfee63b0fd387:/src/common/numformatter.cpp?ds=sidebyside diff --git a/src/common/numformatter.cpp b/src/common/numformatter.cpp index 851259cbfd..0c3a6a7a1a 100644 --- a/src/common/numformatter.cpp +++ b/src/common/numformatter.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: numformatter.cpp +// Name: src/common/numformatter.cpp // Purpose: wxNumberFormatter // Author: Fulvio Senore, Vadim Zeitlin // Created: 2010-11-06 @@ -21,6 +21,83 @@ #include "wx/numformatter.h" #include "wx/intl.h" +#include // 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 // ============================================================================ @@ -31,12 +108,17 @@ 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); @@ -57,14 +139,18 @@ wxChar wxNumberFormatter::GetDecimalSeparator() } 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); @@ -77,8 +163,6 @@ bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep) } //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 ) @@ -88,16 +172,18 @@ bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep) *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); @@ -107,6 +193,21 @@ wxString wxNumberFormatter::ToString(long val, int style) 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); @@ -134,6 +235,10 @@ void wxNumberFormatter::AddThousandsSeparators(wxString& 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 @@ -141,7 +246,7 @@ void wxNumberFormatter::AddThousandsSeparators(wxString& s) // 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); @@ -151,7 +256,8 @@ void wxNumberFormatter::AddThousandsSeparators(wxString& s) 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. @@ -183,6 +289,16 @@ bool wxNumberFormatter::FromString(wxString s, long *val) 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);