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.
This commit is contained in:
Vadim Zeitlin
2021-08-05 18:30:30 +02:00
parent 37a23e1ab1
commit b3cab73680
2 changed files with 39 additions and 96 deletions

View File

@@ -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 <code>wxString::Format("%ld", val)</code>.
@@ -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);

View File

@@ -16,84 +16,7 @@
#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
#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);
}