From b3cab73680df087172867cefc907a37baeb35f49 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 5 Aug 2021 18:30:30 +0200 Subject: [PATCH] Use wxUILocale in wxNumberFormatter Always use the UI locale conventions for floating point numbers in wxNumberFormatter, making it different from wxString::{To,From}Double() functions that use C locale, which may, or not, correspond to the UI locale. --- interface/wx/numformatter.h | 18 +++--- src/common/numformatter.cpp | 117 +++++++++--------------------------- 2 files changed, 39 insertions(+), 96 deletions(-) diff --git a/interface/wx/numformatter.h b/interface/wx/numformatter.h index 1479bae1c4..42c260609f 100644 --- a/interface/wx/numformatter.h +++ b/interface/wx/numformatter.h @@ -8,11 +8,15 @@ /** @class wxNumberFormatter - Helper class for formatting and parsing numbers with thousands separators. + Formatting and parsing numbers using the current UI locale conventions, + including support for using the correct decimal point character and + thousands separators. This class contains only static functions, so users must not create instances but directly call the member functions. + @see wxUILocale + @since 2.9.2 @library{wxbase} @@ -32,7 +36,7 @@ public: /** If this flag is given, thousands separators will be inserted in the - number string representation as defined by the current locale. + number string representation as defined by the current UI locale. */ Style_WithThousandsSep = 0x01, @@ -56,7 +60,7 @@ public: Returns string representation of an integer number. By default, the string will use thousands separators if appropriate for - the current locale. This can be avoided by passing Style_None as @a + the current UI locale. This can be avoided by passing Style_None as @a flags in which case the call to the function has exactly the same effect as wxString::Format("%ld", val). @@ -94,7 +98,7 @@ public: Parse a string representation of a number possibly including thousands separators. - These functions parse number representation in the current locale. On + These functions parse number representation in the current UI locale. On success they return @true and store the result at the location pointed to by @a val (which can't be @NULL), otherwise @false is returned. @@ -114,7 +118,7 @@ public: //@} /** - Get the decimal separator for the current locale. + Get the decimal separator for the current UI locale. Decimal separators is always defined and we fall back to returning '.' in case of an error. @@ -123,14 +127,14 @@ public: /** Get the thousands separator if grouping of the digits is used by the - current locale. + current UI locale. The value returned in @a sep should be only used if the function returns @true, otherwise no thousands separator should be used at all. @param sep Points to the variable receiving the thousands separator character - if it is used by the current locale. May be @NULL if only the + if it is used by the current UI locale. May be @NULL if only the function return value is needed. */ static bool GetThousandsSeparatorIfUsed(wxChar *sep); diff --git a/src/common/numformatter.cpp b/src/common/numformatter.cpp index e57ab02335..c7fdd5d012 100644 --- a/src/common/numformatter.cpp +++ b/src/common/numformatter.cpp @@ -16,84 +16,7 @@ #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 +#include "wx/uilocale.h" // ============================================================================ // wxNumberFormatter implementation @@ -111,14 +34,10 @@ wxChar wxNumberFormatter::GetDecimalSeparator() // concurrently from more than one thread so it's not a real problem. static wxChar s_decimalSeparator = 0; - // 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() ) + if ( !s_decimalSeparator ) { const wxString - s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER); + s = wxUILocale::GetCurrent().GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER); if ( s.length() == 1 ) { s_decimalSeparator = s[0]; @@ -141,12 +60,14 @@ bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep) { #if wxUSE_INTL static wxChar s_thousandsSeparator = 0; - static LocaleId s_localeUsedForInit; + static bool s_thousandsSeparatorInitialized = false; - if ( s_localeUsedForInit.NotInitializedOrHasChanged() ) + if ( !s_thousandsSeparatorInitialized ) { + s_thousandsSeparatorInitialized = true; + const wxString - s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER); + s = wxUILocale::GetCurrent().GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER); if ( s.length() == 1 ) { s_thousandsSeparator = s[0]; @@ -172,6 +93,21 @@ bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep) // Conversion to string and helpers // ---------------------------------------------------------------------------- +namespace +{ + +void ReplaceSeparatorIfNecessary(wxString& s, wxChar sepOld, wxChar sepNew) +{ + if ( sepNew != sepOld ) + { + const size_t posSep = s.find(sepOld); + if ( posSep != wxString::npos ) + s[posSep] = sepNew; + } +} + +} // anonymous namespace + wxString wxNumberFormatter::PostProcessIntString(wxString s, int style) { if ( style & Style_WithThousandsSep ) @@ -206,7 +142,9 @@ wxString wxNumberFormatter::ToString(wxULongLong_t val, int style) wxString wxNumberFormatter::ToString(double val, int precision, int style) { - wxString s = wxString::FromDouble(val,precision); + wxString s = wxString::FromCDouble(val,precision); + + ReplaceSeparatorIfNecessary(s, '.', GetDecimalSeparator()); if ( style & Style_WithThousandsSep ) AddThousandsSeparators(s); @@ -330,5 +268,6 @@ bool wxNumberFormatter::FromString(wxString s, wxULongLong_t *val) bool wxNumberFormatter::FromString(wxString s, double *val) { RemoveThousandsSeparators(s); - return s.ToDouble(val); + ReplaceSeparatorIfNecessary(s, GetDecimalSeparator(), '.'); + return s.ToCDouble(val); }