[Git][wxwidgets/wxwidgets][master] 4 commits: Support more aspects in wxUILocale and wxNumberFormatter

1 view
Skip to first unread message

Vadim Zeitlin (@_VZ_)

unread,
Oct 9, 2025, 5:56:00 PM (12 days ago) Oct 9
to wx-commi...@googlegroups.com


Vadim Zeitlin pushed to branch master at wxWidgets / wxWidgets


Commits:
8c46e47e by Ulrich Telle at 2025-10-09T23:16:22+02:00
Support more aspects in wxUILocale and wxNumberFormatter

Add support for currency amounts, including functions for retrieving
currency symbol and currency code for the current locale and the
possibility to format such amounts according to the locale rules.

Allow retrieving more information about numeric values formatting, for
both numbers and currency amounts, including grouping information when
group separator is used and number of digits after decimal separator and
also use them when formatting numbers.

Finally add function to retrieve the measurement system (metric or
imperial) used by the current locale.

See #1781.

Closes #25765.

- - - - -
534f238a by Blake-Madden at 2025-10-09T23:19:08+02:00
Generic image list: use initalCount argument to reserve elements

This can be a decent optimization that makes use of the currently unused
parameter.

See #25868.

- - - - -
216c9b42 by Blake-Madden at 2025-10-09T23:19:28+02:00
Add support for reserving number of images in ribbon's image list

Allow optionally specifying the number of initial images in
wxRibbonBar::GetButtonImageList().

Closes #25868.

- - - - -
d0318096 by Blake-Madden at 2025-10-09T23:21:13+02:00
Document wxBUILD_DEBUG_LEVEL in CMake overview

Closes #25870.

- - - - -


18 changed files:

- docs/doxygen/overviews/cmake.md
- include/wx/localedefs.h
- include/wx/numformatter.h
- include/wx/private/uilocale.h
- include/wx/ribbon/bar.h
- include/wx/uilocale.h
- interface/wx/numformatter.h
- interface/wx/ribbon/bar.h
- interface/wx/uilocale.h
- src/common/numformatter.cpp
- src/common/uilocale.cpp
- src/generic/imaglist.cpp
- src/msw/uilocale.cpp
- src/osx/core/uilocale.mm
- src/ribbon/bar.cpp
- src/unix/uilocale.cpp
- tests/intl/intltest.cpp
- tests/strings/numformatter.cpp


Changes:

=====================================
docs/doxygen/overviews/cmake.md
=====================================
@@ -72,6 +72,7 @@ wxUSE_GUI | BOOL | ON | Build the UI libraries
wxBUILD_COMPATIBILITY | STRING | 3.2 | Enable API compatibility with 3.0, 3.2 or neither ("NONE")
wxBUILD_PRECOMP | BOOL | ON | Use precompiled headers
wxBUILD_MONOLITHIC | BOOL | OFF | Build a single library
+wxBUILD_DEBUG_LEVEL | STRING | 1 | 0, 1, or 2 (corresponds to wxDEBUG_LEVEL)

Note that on macOS, the option `CMAKE_OSX_ARCHITECTURES` is used to specify which architecture(s) to build.
For example, the following will build a "universal binary 2" (i.e., ARM64 and Intel x86_64) library.


=====================================
include/wx/localedefs.h
=====================================
@@ -12,6 +12,8 @@

#include "wx/defs.h"

+#include <vector>
+
// ----------------------------------------------------------------------------
// wxLayoutDirection: used by wxWindow, wxDC etc
// ----------------------------------------------------------------------------
@@ -122,6 +124,53 @@ enum wxLocaleForm
wxLOCALE_FORM_ENGLISH
};

+enum class wxMeasurementSystem
+{
+ Unknown,
+ Metric,
+ NonMetric
+};
+
+enum class wxCurrencySymbolPosition
+{
+ PrefixNoSep,
+ PrefixWithSep,
+ SuffixNoSep,
+ SuffixWithSep
+};
+
+struct wxLocaleNumberFormatting
+{
+ wxLocaleNumberFormatting() = default;
+ wxLocaleNumberFormatting(const wxString& groupSeparator_, const std::vector<int>& grouping_,
+ const wxString& decimalSeparator_, int fractionalDigits_)
+ : groupSeparator(groupSeparator_), grouping(grouping_),
+ decimalSeparator(decimalSeparator_), fractionalDigits(fractionalDigits_)
+ {
+ }
+ wxString groupSeparator;
+ std::vector<int> grouping;
+ wxString decimalSeparator;
+ int fractionalDigits = 0;
+};
+
+struct wxLocaleCurrencyInfo
+{
+ wxLocaleCurrencyInfo() = default;
+ wxLocaleCurrencyInfo(const wxString& symbol_, const wxString& code_,
+ const wxCurrencySymbolPosition currencySymbolPos_,
+ const wxLocaleNumberFormatting currencyFormat_)
+ : currencySymbol(symbol_), currencyCode(code_),
+ currencySymbolPos(currencySymbolPos_),
+ currencyFormat(currencyFormat_)
+ {
+ }
+ wxString currencySymbol; // the currency symbol (for example "$")
+ wxString currencyCode; // the currency ISO code (for example "USD")
+ wxCurrencySymbolPosition currencySymbolPos = wxCurrencySymbolPosition::PrefixWithSep;
+ wxLocaleNumberFormatting currencyFormat;
+};
+
// ----------------------------------------------------------------------------
// wxLanguageInfo: encapsulates wxLanguage to OS native lang.desc.
// translation information


=====================================
include/wx/numformatter.h
=====================================
@@ -25,6 +25,9 @@ public:
Style_NoTrailingZeroes = 0x02, // Only for floating point numbers
Style_SignPlus = 0x04,
Style_SignSpace = 0x08,
+ Style_Currency = 0x10, // Currency, without currency symbol
+ Style_CurrencySymbol = 0x20, // Currency with currency symbol
+ Style_CurrencyCode = 0x40, // Currency with ISO 4217 code
};

// Format a number as a string. By default, the thousands separator is
@@ -75,18 +78,24 @@ public:
// number. Also used by ToString().
static void RemoveTrailingZeroes(wxString& s);

+ // Remove currency symbol or code
+ static wxString RemoveCurrencySymbolOrCode(wxString s, int style);
+
private:
// Post-process the string representing an integer.
static wxString PostProcessIntString(wxString s, int style);

// Add the thousands separators to a string representing a number without
// the separators. This is used by ToString(Style_WithThousandsSep).
- static void AddThousandsSeparators(wxString& s);
+ static void AddThousandsSeparators(wxString& s, int style);

// Add the sign prefix to a string representing a number without
// the prefix. This is used by ToString().
static void AddSignPrefix(wxString& s, int style);

+ // Add currency symbol or code depending on style
+ static void AddCurrency(wxString& s, int style);
+
// Remove all thousands separators from a string representing a number.
static void RemoveThousandsSeparators(wxString& s);
};


=====================================
include/wx/private/uilocale.h
=====================================
@@ -96,6 +96,14 @@ public:
#endif // wxUSE_DATETIME

virtual wxLayoutDirection GetLayoutDirection() const = 0;
+
+ virtual wxLocaleNumberFormatting GetNumberFormatting() const = 0;
+ virtual wxString GetCurrencySymbol() const = 0;
+ virtual wxString GetCurrencyCode() const = 0;
+ virtual wxCurrencySymbolPosition GetCurrencySymbolPosition() const = 0;
+ virtual wxLocaleCurrencyInfo GetCurrencyInfo() const = 0;
+ virtual wxMeasurementSystem UsesMetricSystem() const = 0;
+
virtual int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const = 0;



=====================================
include/wx/ribbon/bar.h
=====================================
@@ -163,7 +163,7 @@ public:

// Return the image list containing images of the given size, creating it
// if necessary.
- wxImageList* GetButtonImageList(wxSize size);
+ wxImageList* GetButtonImageList(wxSize size, int initialCount = 1);

protected:
friend class wxRibbonPage;


=====================================
include/wx/uilocale.h
=====================================
@@ -186,6 +186,27 @@ public:
// Query the layout direction of the current locale.
wxLayoutDirection GetLayoutDirection() const;

+ // Query infos about number formatting of the current locale
+ wxLocaleNumberFormatting GetNumberFormatting() const;
+
+ // Query the curreny symbol of the current locale
+ wxString GetCurrencySymbol() const;
+
+ // Query the currency code of the current locale
+ wxString GetCurrencyCode() const;
+
+ // Query the currency symbol position of the current locale
+ wxCurrencySymbolPosition GetCurrencySymbolPosition() const;
+
+ // Query the currency infos of the current locale
+ wxLocaleCurrencyInfo GetCurrencyInfo() const;
+
+ // Query whether the current locale uses the metric system
+ wxMeasurementSystem UsesMetricSystem() const;
+
+ // Guess from the region whether the current locale uses the metric system
+ static wxMeasurementSystem GuessMetricSystemFromRegion(const wxLocaleIdent& idLocale);
+
// Compares two strings in the order defined by this locale.
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags = wxCompare_CaseSensitive) const;


=====================================
interface/wx/numformatter.h
=====================================
@@ -37,6 +37,8 @@ public:
/**
If this flag is given, thousands separators will be inserted in the
number string representation as defined by the current UI locale.
+ Thousands separators will be applied according to the grouping rules
+ of the current UI locale.
*/
Style_WithThousandsSep = 0x01,

@@ -74,6 +76,40 @@ public:
@since 3.3.1
*/
Style_SignSpace = 0x08,
+
+ /**
+ If this flag is given, the number will be interpreted
+ as a currency value and formatted according to the rules
+ of the current UI locale.
+
+ @since 3.3.2
+ */
+ Style_Currency = 0x10,
+
+ /**
+ If this flag is given, the currency symbol of the current
+ UI locale will be prepended or appended to the currency value
+ according to the rules of the current locale.
+
+ The style will only take effect, if it is combined with Style_Currency.
+ The style should not be combined with Style_CurrencyCode.
+
+ @since 3.3.2
+ */
+ Style_CurrencySymbol = 0x20, // Currency with currency symbol
+
+ /**
+ If this flag is given, the ISO 42127 currency code of the current
+ UI locale will be prepended to the currency value. It will be separated
+ from the currency value by a space character.
+
+ The style will only take effect, if it is combined with Style_Currency.
+ The style should not be combined with Style_CurrencySymbol.
+
+ @since 3.3.2
+ */
+ Style_CurrencyCode = 0x40, // Currency with ISO 4217 code
+
};

/**
@@ -180,4 +216,22 @@ public:
*/
static void RemoveTrailingZeroes(wxString& str);

+ /**
+ Remove currency symbol or code, and grouping separators,
+ according to the given style flags.
+
+ @note This function allows to remove the currency symbol or code
+ from a string with a formatted currency value, so that
+ FromString() can be used afterwards to retrieve the numerical value.
+ Additionally, grouping separators are removed, if necessary.
+
+ @param[in] str
+ The string to remove the currency symbol or code or grouping separators from.
+ @param flags
+ Combination of values from the Style enumeration.
+
+ @since 3.3.2
+ */
+ static wxString RemoveCurrencySymbolOrCode(wxString str, int flags);
+
};


=====================================
interface/wx/ribbon/bar.h
=====================================
@@ -480,9 +480,14 @@ public:
Return the image list containing images of the given size, creating it
if necessary.

+ @param size The size of the images going into this image list.
+ @param initialCount The initial number of images to reserve for the
+ image list (this parameter is available since wxWidgets 3.3.2).
+ @return The existing (or newly created) image list.
+
@since 3.1.4
*/
- wxImageList* GetButtonImageList(wxSize size);
+ wxImageList* GetButtonImageList(wxSize size, int initialCount = 1);

/**
Perform initial layout and size calculations of the bar and its


=====================================
interface/wx/uilocale.h
=====================================
@@ -296,6 +296,106 @@ public:
*/
wxLayoutDirection GetLayoutDirection() const;

+ /**
+ Return all settings related to number formatting for the current locale.
+ The information includes:
+ - the grouping separator
+ - the grouping specification
+ - the decimal separator
+ - the number of fractional digits
+
+ @return
+ The wxLocaleNumberFormatting structure with the number formatting details.
+ @since 3.3.2
+ */
+ wxLocaleNumberFormatting GetNumberFormatting() const;
+
+ /**
+ Query the currency symbol of the current locale.
+
+ @return
+ The currency symbol that is typically used locally for currency values.
+ If no currency symbol could be determined, an empty string will be returned.
+ @since 3.3.2
+ */
+ wxString GetCurrencySymbol() const;
+
+ /**
+ Query the ISO 4217 currency code of the current locale.
+
+ @return
+ The 3-letter ISO 4217 currency code.
+ If no currency code could be determined, an empty string will be returned.
+ @since 3.3.2
+ */
+ wxString GetCurrencyCode() const;
+
+ /**
+ Query the currency symbol position of the current locale.
+
+ The currency symbol can be positioned in one of 4 ways:
+ as a prefix or suffix to the currency value, either separated from the value
+ by a space character or not. Accordingly, one of the following values is returned:
+ - wxCurrencySymbolPosition::PrefixWithSep
+ - wxCurrencySymbolPosition::PrefixNoSep
+ - wxCurrencySymbolPosition::SuffixWithSep
+ - wxCurrencySymbolPosition::SuffixNoSep
+
+ @note The position of the currency symbol does not affect the representation
+ with the currency code. The currency code is always placed before the
+ currency value and separated by a space.
+
+ @return
+ The currency symbol position.
+ @since 3.3.2
+ */
+ wxCurrencySymbolPosition GetCurrencySymbolPosition() const;
+
+ /**
+ Return all settings related to currency formatting for the current locale.
+
+ The currency information includes the following items:
+ - the currency symbol
+ - the currency code
+ - the currency symbol position
+ - the currency value formatting information
+
+ @return
+ The wxLocaleCurrencyInfo structure.
+ @since 3.3.2
+ */
+ wxLocaleCurrencyInfo GetCurrencyInfo() const;
+
+ /**
+ Query whether the current locale uses the metric system
+
+ @note If wxMeasurementSystem::Unknown is returned,
+ GuessMetricSystemFromRegion() can be used as a fallback
+
+ @return
+ The measurement system, one of the values
+ wxMeasurementSystem::Metric, wxMeasurementSystem::NonMetric,
+ or wxMeasurementSystem::Unknown.
+ @since 3.3.2
+
+ @see GuessMetricSystemFromRegion()
+ */
+ wxMeasurementSystem UsesMetricSystem() const;
+
+ /**
+ Guess whether the current locale uses the metric system
+ base on from the region part of the locale identification.
+
+ @param idLocale
+ The id of the locale for which the measurement system should be guessed.
+ @return
+ The guessed measurement system, one of the values
+ wxMeasurementSystem::Metric, wxMeasurementSystem::NonMetric,
+ or wxMeasurementSystem::Unknown.
+ @since 3.3.2
+ */
+ static wxMeasurementSystem GuessMetricSystemFromRegion(const wxLocaleIdent& idLocale);
+
/**
Return true if locale is supported on the current system.



=====================================
src/common/numformatter.cpp
=====================================
@@ -83,13 +83,15 @@ void ReplaceSeparatorIfNecessary(wxString& s, wxChar sepOld, wxChar sepNew)
wxString wxNumberFormatter::PostProcessIntString(wxString s, int style)
{
if ( style & Style_WithThousandsSep )
- AddThousandsSeparators(s);
+ AddThousandsSeparators(s, style);

wxASSERT_MSG( !(style & Style_NoTrailingZeroes),
"Style_NoTrailingZeroes can't be used with integer values" );

AddSignPrefix(s, style);

+ AddCurrency(s, style);
+
return s;
}

@@ -121,13 +123,15 @@ wxString wxNumberFormatter::ToString(double val, int precision, int style)
ReplaceSeparatorIfNecessary(s, '.', GetDecimalSeparator());

if ( style & Style_WithThousandsSep )
- AddThousandsSeparators(s);
+ AddThousandsSeparators(s, style);

if ( style & Style_NoTrailingZeroes )
RemoveTrailingZeroes(s);

AddSignPrefix(s, style);

+ AddCurrency(s, style);
+
return s;
}

@@ -148,17 +152,33 @@ wxString wxNumberFormatter::Format(const wxString& format, double val)
return s;
}

-void wxNumberFormatter::AddThousandsSeparators(wxString& s)
+void wxNumberFormatter::AddThousandsSeparators(wxString& s, int style)
{
// Thousands separators for numbers in scientific format are not relevant.
if ( s.find_first_of("eE") != wxString::npos )
return;

- wxChar thousandsSep;
- if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
+ wxString groupingSeparator;
+ std::vector<int> grouping;
+ wxString decimalSeparator = GetDecimalSeparator();
+#if wxUSE_INTL
+ wxLocaleNumberFormatting numForm;
+ if (style & Style_Currency)
+ numForm = wxUILocale::GetCurrent().GetCurrencyInfo().currencyFormat;
+ else
+ numForm = wxUILocale::GetCurrent().GetNumberFormatting();
+ if (!numForm.groupSeparator.empty())
+ {
+ groupingSeparator = numForm.groupSeparator;
+ grouping = numForm.grouping;
+ decimalSeparator = numForm.decimalSeparator;
+ }
+#endif // wxUSE_INTL
+
+ if (groupingSeparator.empty() )
return;

- size_t pos = s.find(GetDecimalSeparator());
+ size_t pos = s.find(decimalSeparator);
if ( pos == wxString::npos )
{
// Start grouping at the end of an integer number.
@@ -169,17 +189,47 @@ void wxNumberFormatter::AddThousandsSeparators(wxString& s)
// 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
- // 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;
+ if (grouping.empty())
+ return;

- while ( pos > start + GROUP_LEN )
+ // The vector grouping conatins a list of group length.
+ // Beginning from the right, each group length is applied once
+ // to determine the next position of a group separator,
+ // unless the last entry entry is a zero, in which case
+ // the last group length is applied repeatedly.
+ size_t groupIndex = 0;
+ size_t groupLen = grouping[0];
+ size_t nextGroupLen = groupLen;
+
+ // Repeat while the group length is valid (i.e. > 0)
+ // and the potential group separator position lies
+ // within the number.
+ while (groupLen > 0 && pos > start)
{
- pos -= GROUP_LEN;
- s.insert(pos, thousandsSep);
+ // Determine next group separator position
+ pos = (pos >= groupLen) ? pos - groupLen : 0;
+
+ // Apply group separator if it is within the number
+ if (pos > start)
+ s.insert(pos, groupingSeparator);
+
+ // Select next group length
+ if (++groupIndex < grouping.size())
+ {
+ // Set the group length to the next group length entry
+ // unless the next group length is zero,
+ // in which case the last group length remains in effect.
+ nextGroupLen = grouping[groupIndex];
+ if (nextGroupLen != 0)
+ groupLen = nextGroupLen;
+ }
+ else if (nextGroupLen != 0)
+ {
+ // If the next group length is not zero,
+ // the last group length was already applied once, and
+ // no further group is to be applied.
+ groupLen = 0;
+ }
}
}

@@ -232,6 +282,101 @@ void wxNumberFormatter::AddSignPrefix(wxString& s, int style)
}
}

+void wxNumberFormatter::AddCurrency(wxString& s, int style)
+{
+#if wxUSE_INTL
+ if (style & Style_Currency)
+ {
+ auto currencyInfo = wxUILocale::GetCurrent().GetCurrencyInfo();
+ wxString currencyStr;
+ wxCurrencySymbolPosition currencyPos{ wxCurrencySymbolPosition::PrefixWithSep };
+ if (style & Style_CurrencySymbol)
+ {
+ currencyStr = currencyInfo.currencySymbol;
+ currencyPos = currencyInfo.currencySymbolPos;
+ }
+ else if (style & Style_CurrencyCode)
+ {
+ currencyStr = currencyInfo.currencyCode;
+ }
+
+ if (!currencyStr.empty())
+ {
+ switch (currencyPos)
+ {
+ case wxCurrencySymbolPosition::PrefixWithSep:
+ s = currencyStr + wxString(" ") + s;
+ break;
+ case wxCurrencySymbolPosition::PrefixNoSep:
+ s = currencyStr + s;
+ break;
+ case wxCurrencySymbolPosition::SuffixWithSep:
+ s = s + wxString(" ") + currencyStr;
+ break;
+ case wxCurrencySymbolPosition::SuffixNoSep:
+ s = s + currencyStr;
+ break;
+ }
+ }
+ }
+#else
+ wxUnused(s);
+ wxUnused(style);
+#endif // wxUSE_INTL
+}
+
+wxString wxNumberFormatter::RemoveCurrencySymbolOrCode(wxString s, int style)
+{
+#if wxUSE_INTL
+ if (style & Style_Currency)
+ {
+ auto currencyInfo = wxUILocale::GetCurrent().GetCurrencyInfo();
+ wxString thousandsSep = currencyInfo.currencyFormat.groupSeparator;
+ wxString currencyStr;
+ wxCurrencySymbolPosition currencyPos{ wxCurrencySymbolPosition::PrefixWithSep };
+ if (style & Style_CurrencySymbol)
+ {
+ currencyStr = currencyInfo.currencySymbol;
+ currencyPos = currencyInfo.currencySymbolPos;
+ }
+ else if (style & Style_CurrencyCode)
+ {
+ currencyStr = currencyInfo.currencyCode;
+ }
+
+ if (!currencyStr.empty())
+ {
+ wxString valueStr;
+ switch (currencyPos)
+ {
+ case wxCurrencySymbolPosition::PrefixWithSep:
+ currencyStr += wxString(" ");
+ // Fall through to case without separator
+ case wxCurrencySymbolPosition::PrefixNoSep:
+ if (s.StartsWith(currencyStr, &valueStr))
+ s = valueStr;
+ break;
+ case wxCurrencySymbolPosition::SuffixWithSep:
+ currencyStr = wxString(" ") + currencyStr;
+ // Fall through to case without separator
+ case wxCurrencySymbolPosition::SuffixNoSep:
+ if (s.EndsWith(currencyStr, &valueStr))
+ s = valueStr;
+ break;
+ }
+ }
+ if (style & Style_WithThousandsSep && !thousandsSep.empty())
+ {
+ s.Replace(thousandsSep, wxString());
+ }
+ }
+ return s;
+#else
+ wxUnused(style);
+ return s;
+#endif // wxUSE_INTL
+}
+
// ----------------------------------------------------------------------------
// Conversion from strings
// ----------------------------------------------------------------------------


=====================================
src/common/uilocale.cpp
=====================================
@@ -994,6 +994,70 @@ wxLayoutDirection wxUILocale::GetLayoutDirection() const
return dir;
}

+wxLocaleNumberFormatting wxUILocale::GetNumberFormatting() const
+{
+ if (!m_impl)
+ return wxLocaleNumberFormatting();
+
+ return m_impl->GetNumberFormatting();
+}
+
+wxString wxUILocale::GetCurrencySymbol() const
+{
+ if (!m_impl)
+ return wxString();
+
+ return m_impl->GetCurrencySymbol();
+}
+
+wxString wxUILocale::GetCurrencyCode() const
+{
+ if (!m_impl)
+ return wxString();
+
+ return m_impl->GetCurrencyCode();
+}
+
+wxCurrencySymbolPosition wxUILocale::GetCurrencySymbolPosition() const
+{
+ if (!m_impl)
+ return wxCurrencySymbolPosition::PrefixWithSep;
+
+ return m_impl->GetCurrencySymbolPosition();
+}
+
+wxLocaleCurrencyInfo wxUILocale::GetCurrencyInfo() const
+{
+ if (!m_impl)
+ return wxLocaleCurrencyInfo();
+
+ return m_impl->GetCurrencyInfo();
+}
+
+wxMeasurementSystem wxUILocale::UsesMetricSystem() const
+{
+ if (!m_impl)
+ return wxMeasurementSystem::Unknown;
+
+ return m_impl->UsesMetricSystem();
+}
+
+wxMeasurementSystem wxUILocale::GuessMetricSystemFromRegion(const wxLocaleIdent& idLocale)
+{
+ wxString region = idLocale.GetRegion();
+ // In 2025 only in the United States, Liberia, and Myanmar
+ // the metric system is not the default measurement system.
+ if (!region.empty())
+ {
+ if (region == "US" || region == "LR" || region == "MM")
+ return wxMeasurementSystem::NonMetric;
+ else
+ return wxMeasurementSystem::Metric;
+ }
+ else
+ return wxMeasurementSystem::Unknown;
+}
+
int
wxUILocale::CompareStrings(const wxString& lhs,
const wxString& rhs,


=====================================
src/generic/imaglist.cpp
=====================================
@@ -58,12 +58,15 @@ int wxGenericImageList::GetImageCount() const
return wxSsize(m_images);
}

-bool wxGenericImageList::Create( int width, int height, bool mask, int WXUNUSED(initialCount) )
+bool wxGenericImageList::Create( int width, int height, bool mask, int initialCount )
{
// Prevent from storing negative dimensions
m_size = wxSize(wxMax(width, 0), wxMax(height, 0));
m_useMask = mask;

+ if ( initialCount > 1 )
+ m_images.reserve(initialCount);
+
// Images must have proper size
return m_size != wxSize(0, 0);
}


=====================================
src/msw/uilocale.cpp
=====================================
@@ -28,8 +28,12 @@

#include "wx/scopedarray.h"
#include "wx/dynlib.h"
+#include "wx/tokenzr.h"
#include "wx/wxcrt.h"

+#include <array>
+#include <vector>
+
#ifndef LOCALE_NAME_USER_DEFAULT
#define LOCALE_NAME_USER_DEFAULT nullptr
#endif
@@ -304,6 +308,45 @@ public:
return wxLayout_Default;
}

+ wxLocaleNumberFormatting GetNumberFormatting() const override
+ {
+ wxLocaleNumberFormatting numForm;
+ numForm.decimalSeparator = ".";
+ numForm.groupSeparator = "";
+ numForm.grouping = {};
+ numForm.fractionalDigits = 2;
+ return numForm;
+ }
+
+ wxString GetCurrencySymbol() const override
+ {
+ return "$";
+ }
+
+ wxString GetCurrencyCode() const override
+ {
+ return "USD";
+ }
+
+ wxCurrencySymbolPosition GetCurrencySymbolPosition() const override
+ {
+ return wxCurrencySymbolPosition::PrefixWithSep;
+ }
+
+ wxLocaleCurrencyInfo GetCurrencyInfo() const override
+ {
+ return wxLocaleCurrencyInfo(
+ GetCurrencySymbol(),
+ GetCurrencyCode(),
+ GetCurrencySymbolPosition(),
+ GetNumberFormatting());
+ }
+
+ wxMeasurementSystem UsesMetricSystem() const override
+ {
+ return wxMeasurementSystem::Metric;
+ }
+
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const override
{
@@ -472,13 +515,15 @@ public:
switch ( index )
{
case wxLOCALE_THOUSANDS_SEP:
- str = DoGetInfo(LOCALE_STHOUSAND);
+ str = DoGetInfo(cat == wxLOCALE_CAT_MONEY
+ ? LOCALE_SMONTHOUSANDSEP
+ : LOCALE_STHOUSAND);
break;

case wxLOCALE_DECIMAL_POINT:
str = DoGetInfo(cat == wxLOCALE_CAT_MONEY
- ? LOCALE_SMONDECIMALSEP
- : LOCALE_SDECIMAL);
+ ? LOCALE_SMONDECIMALSEP
+ : LOCALE_SDECIMAL);
break;

case wxLOCALE_SHORT_DATE_FMT:
@@ -642,6 +687,53 @@ public:
return m_layoutDir;
}

+ wxLocaleNumberFormatting GetNumberFormatting() const override
+ {
+ return DoGetNumberFormatting(wxLOCALE_CAT_NUMBER);
+ }
+
+ wxString GetCurrencySymbol() const override
+ {
+ return DoGetInfo(LOCALE_SCURRENCY);
+ }
+
+ wxString GetCurrencyCode() const override
+ {
+ return wxString(DoGetInfo(LOCALE_SINTLSYMBOL)).Left(3);
+ }
+
+ wxCurrencySymbolPosition GetCurrencySymbolPosition() const override
+ {
+ static std::array<wxCurrencySymbolPosition, 4> symPos = {
+ wxCurrencySymbolPosition::PrefixNoSep, wxCurrencySymbolPosition::SuffixNoSep,
+ wxCurrencySymbolPosition::PrefixWithSep, wxCurrencySymbolPosition::SuffixWithSep };
+ wxString posStr = wxString(DoGetInfo(LOCALE_ICURRENCY));
+ unsigned int posIdx;
+ return posStr.ToUInt(&posIdx) && posIdx < symPos.size()
+ ? symPos[posIdx]
+ : wxCurrencySymbolPosition::PrefixWithSep;
+ }
+
+ wxLocaleCurrencyInfo GetCurrencyInfo() const override
+ {
+ wxLocaleNumberFormatting currencyFormatting = DoGetNumberFormatting(wxLOCALE_CAT_MONEY);
+ return wxLocaleCurrencyInfo(
+ GetCurrencySymbol(),
+ GetCurrencyCode(),
+ GetCurrencySymbolPosition(),
+ currencyFormatting);
+ }
+
+ wxMeasurementSystem UsesMetricSystem() const override
+ {
+ wxString str = DoGetInfo(LOCALE_IMEASURE);
+ if (!str.empty())
+ {
+ return (str.IsSameAs("0")) ? wxMeasurementSystem::Metric : wxMeasurementSystem::NonMetric;
+ }
+ return wxMeasurementSystem::Unknown;
+ }
+
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const override
{
@@ -696,6 +788,36 @@ private:
return buf;
}

+ wxLocaleNumberFormatting DoGetNumberFormatting(wxLocaleCategory cat) const
+ {
+ wxString groupSeparator = DoGetInfo(cat == wxLOCALE_CAT_MONEY
+ ? LOCALE_SMONTHOUSANDSEP
+ : LOCALE_STHOUSAND);
+ wxString groupingInfo = DoGetInfo(cat == wxLOCALE_CAT_MONEY
+ ? LOCALE_SMONGROUPING
+ : LOCALE_SGROUPING);
+ wxString decimalSeparator = DoGetInfo(cat == wxLOCALE_CAT_MONEY
+ ? LOCALE_SMONDECIMALSEP
+ : LOCALE_SDECIMAL);
+ wxString digits = DoGetInfo(cat == wxLOCALE_CAT_MONEY
+ ? LOCALE_ICURRDIGITS
+ : LOCALE_IDIGITS);
+ int fractionalDigits;
+ if (digits.empty() || !digits.ToInt(&fractionalDigits))
+ fractionalDigits = 0;
+
+ // Extract grouping lengths from groupingInfo
+ std::vector<int> grouping;
+ for (wxStringTokenizer tokenizer(groupingInfo, ";"); tokenizer.HasMoreTokens();)
+ {
+ int value = 0;
+ if (tokenizer.GetNextToken().ToInt(&value))
+ grouping.push_back(value);
+ }
+
+ return wxLocaleNumberFormatting(groupSeparator, grouping, decimalSeparator, fractionalDigits);
+ }
+
const wchar_t* const m_name;

mutable wxLayoutDirection m_layoutDir = wxLayout_Default;


=====================================
src/osx/core/uilocale.mm
=====================================
@@ -27,13 +27,12 @@
#include "wx/osx/core/cfref.h"
#include "wx/osx/core/cfstring.h"

-#import <Foundation/NSArray.h>
-#import <Foundation/NSString.h>
-#import <Foundation/NSLocale.h>
-#import <Foundation/NSDateFormatter.h>
+#import <Foundation/Foundation.h>

#include "wx/osx/private/uilocale.h"

+#include <vector>
+
extern wxString
wxGetInfoFromCFLocale(CFLocaleRef cfloc, wxLocaleInfo index, wxLocaleCategory cat);

@@ -165,10 +164,20 @@ public:
#endif // wxUSE_DATETIME

wxLayoutDirection GetLayoutDirection() const override;
+
+ wxLocaleNumberFormatting GetNumberFormatting() const override;
+ wxString GetCurrencySymbol() const override;
+ wxString GetCurrencyCode() const override;
+ wxCurrencySymbolPosition GetCurrencySymbolPosition() const override;
+ wxLocaleCurrencyInfo GetCurrencyInfo() const override;
+ wxMeasurementSystem UsesMetricSystem() const override;
+
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const override;

private:
+ wxLocaleNumberFormatting DoGetNumberFormatting(wxLocaleCategory cat) const;
+
NSLocale* const m_nsloc;

wxDECLARE_NO_COPY_CLASS(wxUILocaleImplCF);
@@ -349,6 +358,132 @@ wxUILocaleImplCF::GetLayoutDirection() const
return wxLayout_Default;
}

+wxLocaleNumberFormatting
+wxUILocaleImplCF::GetNumberFormatting() const
+{
+ return DoGetNumberFormatting(wxLOCALE_CAT_NUMBER);
+}
+
+wxString
+wxUILocaleImplCF::GetCurrencySymbol() const
+{
+ NSString* str = [m_nsloc currencySymbol];
+ return wxCFStringRef::AsString(str);
+}
+
+wxString
+wxUILocaleImplCF::GetCurrencyCode() const
+{
+ NSString* str = [m_nsloc currencyCode];
+ return wxCFStringRef::AsString(str);
+}
+
+wxCurrencySymbolPosition
+wxUILocaleImplCF::GetCurrencySymbolPosition() const
+{
+ wxCFRef<NSNumberFormatter*> cfRefFormatter = [[NSNumberFormatter alloc] init];
+ NSNumberFormatter* formatter = cfRefFormatter.get();
+ formatter.locale = m_nsloc;
+ formatter.numberStyle = NSNumberFormatterCurrencyStyle;
+
+ NSString* formatted = [formatter stringFromNumber:@123];
+ NSString* symbol = formatter.currencySymbol;
+
+ NSRange symbolRange = [formatted rangeOfString:symbol];
+ BOOL symbolBefore = (symbolRange.location == 0);
+
+ wxCurrencySymbolPosition symbolPos = wxCurrencySymbolPosition::PrefixWithSep;
+ BOOL hasSpace = NO;
+ if (symbolRange.location != NSNotFound)
+ {
+ if (symbolBefore)
+ {
+ // Check character succeeding the currency symbol
+ NSUInteger idx = NSMaxRange(symbolRange);
+ if (idx < formatted.length)
+ {
+ unichar after = [formatted characterAtIndex:idx];
+ hasSpace = [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:after];
+ }
+ symbolPos = (hasSpace)
+ ? wxCurrencySymbolPosition::PrefixWithSep
+ : wxCurrencySymbolPosition::PrefixNoSep;
+ }
+ else
+ {
+ // Check character preceding the currency symbol
+ if (symbolRange.location > 0)
+ {
+ unichar before = [formatted characterAtIndex:symbolRange.location - 1];
+ hasSpace = [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:before];
+ }
+ symbolPos = (hasSpace)
+ ? wxCurrencySymbolPosition::SuffixWithSep
+ : wxCurrencySymbolPosition::SuffixNoSep;
+ }
+ }
+
+ return symbolPos;
+}
+
+wxLocaleNumberFormatting
+wxUILocaleImplCF::DoGetNumberFormatting(wxLocaleCategory cat) const
+{
+ wxCFRef<NSNumberFormatter*> cfRefFormatter = [[NSNumberFormatter alloc] init];
+ NSNumberFormatter* formatter = cfRefFormatter.get();
+ formatter.locale = m_nsloc;
+ formatter.numberStyle = (cat == wxLOCALE_CAT_MONEY)
+ ? NSNumberFormatterCurrencyStyle
+ : NSNumberFormatterDecimalStyle;
+
+ wxString groupSeparator = wxCFStringRef::AsString(formatter.groupingSeparator);
+
+ std::vector<int> grouping;
+ int groupingSize = (int) formatter.groupingSize;
+ int secondaryGroupingSize = (int) formatter.secondaryGroupingSize;
+ if (groupingSize > 0)
+ {
+ if (secondaryGroupingSize > 0 && secondaryGroupingSize != groupingSize)
+ {
+ grouping.push_back(groupingSize);
+ grouping.push_back(secondaryGroupingSize);
+ grouping.push_back(0);
+ }
+ else
+ {
+ grouping.push_back(groupingSize);
+ grouping.push_back(0);
+ }
+ }
+
+ wxString decimalSeparator = wxCFStringRef::AsString(formatter.decimalSeparator);
+ int fractionalDigits = (int) formatter.minimumFractionDigits;
+
+ return wxLocaleNumberFormatting(groupSeparator, grouping, decimalSeparator, fractionalDigits);
+}
+
+wxLocaleCurrencyInfo
+wxUILocaleImplCF::GetCurrencyInfo() const
+{
+ wxLocaleNumberFormatting currencyFormatting = DoGetNumberFormatting(wxLOCALE_CAT_MONEY);
+ return wxLocaleCurrencyInfo(
+ GetCurrencySymbol(),
+ GetCurrencyCode(),
+ GetCurrencySymbolPosition(),
+ currencyFormatting);
+}
+
+wxMeasurementSystem
+wxUILocaleImplCF::UsesMetricSystem() const
+{
+ if ([m_nsloc respondsToSelector:@selector(usesMetricSystem)])
+ {
+ BOOL isMetric = [m_nsloc usesMetricSystem];
+ return (isMetric) ? wxMeasurementSystem::Metric : wxMeasurementSystem::NonMetric;
+ }
+ return wxMeasurementSystem::Unknown;
+}
+
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
{


=====================================
src/ribbon/bar.cpp
=====================================
@@ -810,7 +810,7 @@ void wxRibbonBar::CommonInit(long style)
m_ribbon_state = wxRIBBON_BAR_PINNED;
}

-wxImageList* wxRibbonBar::GetButtonImageList(wxSize size)
+wxImageList* wxRibbonBar::GetButtonImageList(wxSize size, int initialCount)
{
for ( size_t n = 0; n < m_image_lists.size(); ++n )
{
@@ -819,7 +819,7 @@ wxImageList* wxRibbonBar::GetButtonImageList(wxSize size)
}

wxImageList* const
- il = new wxImageList(size.GetWidth(), size.GetHeight(), /*mask*/false);
+ il = new wxImageList(size.GetWidth(), size.GetHeight(), /*mask*/false, initialCount);
m_image_lists.push_back(il);

return il;


=====================================
src/unix/uilocale.cpp
=====================================
@@ -41,11 +41,27 @@
#include "wx/tokenzr.h"
#include "wx/utils.h"

+#include <array>
+#include <vector>
+
#define TRACE_I18N wxS("i18n")

namespace
{

+// Enumeration for accessing member variables of the localeconv structure
+// used as fallback, if nl_langinfo_l or nl_langinfo are not available
+enum wxLocaleConvAttr
+{
+ LOCALE_CONV_THOUSANDS_SEP,
+ LOCALE_CONV_DECIMAL_POINT,
+ LOCALE_CONV_GROUPING,
+ LOCALE_CONV_DIGITS,
+ LOCALE_CONV_CURRENCY_SYMBOL,
+ LOCALE_CONV_CURRENCY_CODE,
+ LOCALE_CONV_CURRENCY_SYM_POS
+};
+
// Small helper function: get the value of the given environment variable and
// return true only if the variable was found and has non-empty value.
inline bool wxGetNonEmptyEnvVar(const wxString& name, wxString* value)
@@ -191,6 +207,13 @@ public:
#endif // wxUSE_DATETIME
wxLayoutDirection GetLayoutDirection() const override;

+ wxLocaleNumberFormatting GetNumberFormatting() const override;
+ wxString GetCurrencySymbol() const override;
+ wxString GetCurrencyCode() const override;
+ wxCurrencySymbolPosition GetCurrencySymbolPosition() const override;
+ wxLocaleCurrencyInfo GetCurrencyInfo() const override;
+ wxMeasurementSystem UsesMetricSystem() const override;
+
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const override;

@@ -214,6 +237,11 @@ private:

const wxString& GetCodeSet() const;

+ wxLocaleNumberFormatting DoGetNumberFormatting(wxLocaleCategory cat) const;
+
+#if !(defined(HAVE_LANGINFO_H) && defined(__GLIBC__))
+ wxString DoGetInfoFromLocaleConv(wxLocaleConvAttr index, wxLocaleCategory cat) const;
+#endif

wxLocaleIdent m_locId;

@@ -520,7 +548,7 @@ wxUILocaleImplUnix::GetLangInfo(nl_item item) const
if ( m_locale )
return nl_langinfo_l(item, m_locale);
#else
- TempLocaleSetter setThisLocale(LC_CTYPE, m_locId.GetName());
+ TempLocaleSetter setThisLocale(LC_ALL, m_locId.GetName());
#endif // HAVE_LOCALE_T

return nl_langinfo(item);
@@ -534,7 +562,7 @@ wxUILocaleImplUnix::GetLangInfoWide(nl_item item) const
if ( m_locale )
return (wchar_t*) nl_langinfo_l(item, m_locale);
#else
- TempLocaleSetter setThisLocale(LC_CTYPE, m_locId.GetName());
+ TempLocaleSetter setThisLocale(LC_ALL, m_locId.GetName());
#endif // HAVE_LOCALE_T

return (wchar_t*) nl_langinfo(item);
@@ -591,7 +619,6 @@ wxUILocaleImplUnix::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
#else
wxUnusedVar(cat);
#endif
-
return GetLangInfo(RADIXCHAR);

case wxLOCALE_SHORT_DATE_FMT:
@@ -832,6 +859,88 @@ wxUILocaleImplUnix::GetLayoutDirection() const
return wxLayout_Default;
}

+wxLocaleNumberFormatting
+wxUILocaleImplUnix::GetNumberFormatting() const
+{
+ return DoGetNumberFormatting(wxLOCALE_CAT_NUMBER);
+}
+
+wxString
+wxUILocaleImplUnix::GetCurrencySymbol() const
+{
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ wxString currencyStr = wxString(GetLangInfo(CURRENCY_SYMBOL), wxCSConv(GetCodeSet()));
+ if (!currencyStr.empty() &&
+ (currencyStr[0] == '+' ||
+ currencyStr[0] == '-' ||
+ currencyStr[0] == '.'))
+ {
+ currencyStr.erase(0, 1);
+ }
+ return currencyStr;
+#else
+ return DoGetInfoFromLocaleConv(LOCALE_CONV_CURRENCY_SYMBOL, wxLOCALE_CAT_DEFAULT);
+#endif
+}
+
+wxString
+wxUILocaleImplUnix::GetCurrencyCode() const
+{
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ wxString currencyCode = wxString(GetLangInfo(INT_CURR_SYMBOL), wxCSConv(GetCodeSet()));
+ return currencyCode.Left(3);
+#else
+ return DoGetInfoFromLocaleConv(LOCALE_CONV_CURRENCY_CODE, wxLOCALE_CAT_DEFAULT);
+#endif
+}
+
+wxCurrencySymbolPosition
+wxUILocaleImplUnix::GetCurrencySymbolPosition() const
+{
+ static std::array<wxCurrencySymbolPosition, 4> symPos = {
+ wxCurrencySymbolPosition::PrefixNoSep, wxCurrencySymbolPosition::SuffixNoSep,
+ wxCurrencySymbolPosition::PrefixWithSep, wxCurrencySymbolPosition::SuffixWithSep };
+
+ wxUint32 posIdx = 0;
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ const char* csPrecedes = GetLangInfo(P_CS_PRECEDES);
+ const char* sepBySpace = GetLangInfo(P_SEP_BY_SPACE);
+ posIdx += (csPrecedes[0] == 0) ? 1 : 0;
+ posIdx += (sepBySpace[0] == 0) ? 0 : 2;
+#else
+ wxString symbolPosition = DoGetInfoFromLocaleConv(LOCALE_CONV_CURRENCY_SYM_POS, wxLOCALE_CAT_DEFAULT);
+ posIdx = symbolPosition.GetChar(0).GetValue();
+#endif
+ return (posIdx < symPos.size()) ? symPos[posIdx] : wxCurrencySymbolPosition::PrefixWithSep;
+}
+
+wxLocaleCurrencyInfo
+wxUILocaleImplUnix::GetCurrencyInfo() const
+{
+ wxLocaleNumberFormatting currencyFormatting = DoGetNumberFormatting(wxLOCALE_CAT_MONEY);
+ return wxLocaleCurrencyInfo(
+ GetCurrencySymbol(),
+ GetCurrencyCode(),
+ GetCurrencySymbolPosition(),
+ currencyFormatting);
+}
+
+wxMeasurementSystem
+wxUILocaleImplUnix::UsesMetricSystem() const
+{
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ wxString measureStr = GetLangInfo(_NL_MEASUREMENT_MEASUREMENT);
+ if (!measureStr.empty() && measureStr[0].GetValue() == 1)
+ return wxMeasurementSystem::Metric;
+ else if (!measureStr.empty() && measureStr[0].GetValue() == 2)
+ return wxMeasurementSystem::NonMetric;
+ else
+ return wxMeasurementSystem::Unknown;
+#else
+ return wxUILocale::GuessMetricSystemFromRegion(GetLocaleId());
+#endif
+}
+
int
wxUILocaleImplUnix::CompareStrings(const wxString& lhs, const wxString& rhs,
int WXUNUSED(flags)) const
@@ -854,6 +963,166 @@ wxUILocaleImplUnix::CompareStrings(const wxString& lhs, const wxString& rhs,
return 0;
}

+wxLocaleNumberFormatting
+wxUILocaleImplUnix::DoGetNumberFormatting(wxLocaleCategory cat) const
+{
+ wxString groupSeparator;
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ const char* groupSep =
+ #ifdef MON_THOUSANDS_SEP
+ (cat == wxLOCALE_CAT_MONEY)
+ ? GetLangInfo(MON_THOUSANDS_SEP)
+ : GetLangInfo(THOUSEP);
+ #else
+ GetLangInfo(THOUSEP);
+ #endif
+ groupSeparator = wxString(groupSep, wxCSConv(GetCodeSet()));
+#else
+ groupSeparator = DoGetInfoFromLocaleConv(LOCALE_CONV_THOUSANDS_SEP, cat);
+#endif
+
+ std::vector<int> grouping;
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ const char* groupingInfo =
+ #ifdef MON_GROUPING
+ (cat == wxLOCALE_CAT_MONEY)
+ ? GetLangInfo(MON_GROUPING)
+ : GetLangInfo(GROUPING);
+ #else
+ GetLangInfo(GROUPING);
+ #endif
+
+ // Include the terminating '\0' in total length
+ size_t groupLen = strlen(groupingInfo) + 1;
+ for (size_t j = 0; j < groupLen; ++j)
+ {
+ auto val = groupingInfo[j];
+ if (val == CHAR_MAX)
+ break;
+ grouping.push_back(static_cast<int>(val));
+ }
+#else
+ wxString groupingStr = DoGetInfoFromLocaleConv(LOCALE_CONV_GROUPING, cat);
+ for (auto ch : groupingStr)
+ {
+ auto val = ch.GetValue();
+ if (val == CHAR_MAX)
+ break;
+ grouping.push_back(static_cast<int>(val));
+ }
+#endif
+
+ wxString decimalSeparator;
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ const char* decimalSep =
+ #ifdef MON_DECIMAL_POINT
+ (cat == wxLOCALE_CAT_MONEY)
+ ? GetLangInfo(MON_DECIMAL_POINT)
+ : GetLangInfo(RADIXCHAR);
+ #else
+ GetLangInfo(RADIXCHAR);
+ #endif
+ decimalSeparator = wxString(decimalSep, wxCSConv(GetCodeSet()));
+#else
+ decimalSeparator = DoGetInfoFromLocaleConv(LOCALE_CONV_DECIMAL_POINT, cat);
+#endif
+
+ int fractionalDigits = 0;
+#if defined(HAVE_LANGINFO_H) && defined(__GLIBC__)
+ const char* fracDigits = GetLangInfo(FRAC_DIGITS);
+ fractionalDigits = (fracDigits) ? fracDigits[0] : 0;
+#else
+ wxString fracDigits = DoGetInfoFromLocaleConv(LOCALE_CONV_DIGITS, cat);
+ fractionalDigits = (!fracDigits.empty()) ? fracDigits.GetChar(0).GetValue() : 0;
+#endif
+
+ return wxLocaleNumberFormatting(groupSeparator, grouping, decimalSeparator, fractionalDigits);
+}
+
+#if !(defined(HAVE_LANGINFO_H) && defined(__GLIBC__))
+
+wxString
+wxUILocaleImplUnix::DoGetInfoFromLocaleConv(wxLocaleConvAttr index, wxLocaleCategory cat) const
+{
+ // localeconv accesses only the global locale
+ // temporarily set this locale
+ TempLocaleSetter setThisLocale(LC_ALL, m_locId.GetName());
+
+ lconv* const lc = localeconv();
+ if (!lc)
+ return wxString();
+
+ switch (index)
+ {
+ case LOCALE_CONV_THOUSANDS_SEP:
+ {
+ if (cat == wxLOCALE_CAT_MONEY)
+ return wxString(lc->mon_thousands_sep, wxConvLibc);
+ else
+ return wxString(lc->thousands_sep, wxConvLibc);
+ }
+
+ case LOCALE_CONV_DECIMAL_POINT:
+ {
+ if (cat == wxLOCALE_CAT_MONEY)
+ return wxString(lc->mon_decimal_point, wxConvLibc);
+ else
+ return wxString(lc->decimal_point, wxConvLibc);
+ }
+
+ case LOCALE_CONV_GROUPING:
+ {
+ wxString groupingStr;
+ const char* grouping;
+ if (cat == wxLOCALE_CAT_MONEY)
+ grouping = lc->mon_grouping;
+ else
+ grouping = lc->grouping;
+
+ size_t groupLen = strlen(grouping);
+ for (size_t j = 0; j < groupLen; ++j)
+ groupingStr.Append(wxUniChar(grouping[j]));
+ groupingStr.Append(wxUniChar(0));
+ return groupingStr;
+ }
+
+ case LOCALE_CONV_CURRENCY_SYMBOL:
+ {
+ return wxString(lc->currency_symbol, wxConvLibc);
+ }
+
+ case LOCALE_CONV_CURRENCY_CODE:
+ {
+ return wxString(lc->int_curr_symbol, wxConvLibc).Left(3);
+ }
+
+ case LOCALE_CONV_DIGITS:
+ {
+ wxString currencyDigitsStr(wxUniChar(lc->frac_digits));
+ return currencyDigitsStr;
+ }
+
+ case LOCALE_CONV_CURRENCY_SYM_POS:
+ {
+ char csPrecedes = lc->p_cs_precedes;
+ char sepBySpace = lc->p_sep_by_space;
+ wxUint32 posIdx = 0;
+ posIdx += (csPrecedes == 0) ? 1 : 0;
+ posIdx += (sepBySpace == 0) ? 0 : 2;
+ wxString symbolPosition;
+ symbolPosition.Append(wxUniChar(posIdx));
+ return symbolPosition;
+ }
+
+ default:
+ wxFAIL_MSG("unknown localeconv value");
+ }
+
+ return wxString();
+}
+
+#endif
+
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
{


=====================================
tests/intl/intltest.cpp
=====================================
@@ -417,11 +417,35 @@ TEST_CASE("wxUILocale::IsSupported", "[uilocale]")

TEST_CASE("wxUILocale::GetInfo", "[uilocale]")
{
- CHECK( wxUILocale::FromTag("en").GetInfo(wxLOCALE_DECIMAL_POINT) == "." );
+ const wxUILocale locEN(wxUILocale::FromTag("en-US"));
+ CHECK( locEN.GetInfo(wxLOCALE_DECIMAL_POINT) == "." );
+ CHECK( locEN.GetCurrencySymbol() == "$");
+ CHECK( locEN.GetCurrencyCode() == "USD");
+ CHECK( locEN.GetCurrencyInfo().currencyFormat.fractionalDigits == 2);
+ CHECK( locEN.GetCurrencySymbolPosition() == wxCurrencySymbolPosition::PrefixNoSep );
+ CHECK( locEN.UsesMetricSystem() == wxMeasurementSystem::NonMetric);
+
+ const wxUILocale locDE(wxUILocale::FromTag("de-DE"));
+ if (CheckSupported(locDE, "German"))
+ {
+ CHECK( locDE.GetInfo(wxLOCALE_DECIMAL_POINT) == ",");
+ CHECK( locDE.GetCurrencySymbol() == L"\u20AC");
+ CHECK( locDE.GetCurrencyCode() == "EUR");
+ CHECK( locDE.GetCurrencyInfo().currencyFormat.fractionalDigits == 2);
+ CHECK( locDE.GetCurrencySymbolPosition() == wxCurrencySymbolPosition::SuffixWithSep);
+ CHECK( locDE.UsesMetricSystem() == wxMeasurementSystem::Metric);
+ }

- const wxUILocale locDE(wxUILocale::FromTag("de"));
- if ( CheckSupported(locDE, "German") )
- CHECK( locDE.GetInfo(wxLOCALE_DECIMAL_POINT) == "," );
+ const wxUILocale locFR(wxUILocale::FromTag("fr-FR"));
+ if (CheckSupported(locFR, "French"))
+ {
+ CHECK( locFR.GetInfo(wxLOCALE_DECIMAL_POINT) == ",");
+ CHECK( locFR.GetCurrencySymbol() == L"\u20AC");
+ CHECK( locFR.GetCurrencyCode() == "EUR");
+ CHECK( locFR.GetCurrencyInfo().currencyFormat.fractionalDigits == 2);
+ CHECK( locFR.GetCurrencySymbolPosition() == wxCurrencySymbolPosition::SuffixWithSep);
+ CHECK( locFR.UsesMetricSystem() == wxMeasurementSystem::Metric);
+ }

// This one shows that "Swiss High German" locale (de_CH) correctly uses
// dot, and not comma, as decimal separator, even under macOS, where POSIX


=====================================
tests/strings/numformatter.cpp
=====================================
@@ -15,6 +15,9 @@

#include "wx/numformatter.h"
#include "wx/intl.h"
+#include "wx/uilocale.h"
+
+#include <vector>

// ----------------------------------------------------------------------------
// test class
@@ -341,3 +344,504 @@ TEST_CASE_METHOD(NumFormatterTestCase, "NumFormatter::DoubleFromString", "[numfo
CHECK( wxNumberFormatter::FromString("123456789.012", &d) );
CHECK( d == 123456789.012 );
}
+
+// ----------------------------------------------------------------------------
+// test class for currency formatting
+// ----------------------------------------------------------------------------
+
+// Euro Sign in UTF-8
+#define EUROSIGN "\xe2\x82\xac"
+
+// No-Break Space in UTF-8
+#define NBSP "\xc2\xa0"
+
+// Narrow No-Break Space in UTF-8
+#define NNBSP "\xe2\x80\xaf"
+
+class CurrencyFormatterTestCase
+{
+public:
+ CurrencyFormatterTestCase() :
+ // The locale 'de-AT' is known to have different grouping characters
+ // for ordinary numbers and currency values.
+ m_locale(wxLANGUAGE_GERMAN_AUSTRIAN, wxLOCALE_DONT_LOAD_DEFAULT)
+ {
+ }
+
+protected:
+ bool CanRunTest() const
+ {
+ bool ok = m_locale.IsOk();
+ if (ok)
+ {
+ wxLocaleNumberFormatting numForm = wxUILocale::GetCurrent().GetNumberFormatting();
+#ifdef __GLIBC__
+ ok = numForm.groupSeparator == ".";
+#else
+ ok = numForm.groupSeparator == wxString::FromUTF8(NBSP);
+#endif
+ }
+ return ok;
+ }
+
+private:
+ wxLocale m_locale;
+
+ wxDECLARE_NO_COPY_CLASS(CurrencyFormatterTestCase);
+};
+
+// ----------------------------------------------------------------------------
+// tests themselves
+// ----------------------------------------------------------------------------
+
+TEST_CASE_METHOD(CurrencyFormatterTestCase, "CurrencyFormatterTestCase::NumericValuesToString", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+#ifdef __GLIBC__
+ CHECK( wxString::FromUTF8("12.345.678") == wxNumberFormatter::ToString(12345678L, wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( wxString::FromUTF8("12" NNBSP "345" NNBSP "678") == wxNumberFormatter::ToString(12345678L, wxNumberFormatter::Style_Currency|wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( wxString::FromUTF8("12.345.678,12") == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( wxString::FromUTF8("12" NNBSP "345" NNBSP "678,12") == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( wxString::FromUTF8(EUROSIGN " 12" NNBSP "345" NNBSP "678,12") == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_CurrencySymbol | wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( wxString::FromUTF8("EUR 12" NNBSP "345" NNBSP "678,12") == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_CurrencyCode | wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( "12345678,12" == wxNumberFormatter::RemoveCurrencySymbolOrCode(wxString::FromUTF8("EUR 12" NNBSP "345" NNBSP "678,12"), wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_CurrencyCode | wxNumberFormatter::Style_WithThousandsSep));
+#else
+ CHECK( wxString::FromUTF8("12" NBSP "345" NBSP "678") == wxNumberFormatter::ToString(12345678L, wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( "12.345.678" == wxNumberFormatter::ToString(12345678L, wxNumberFormatter::Style_Currency|wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( wxString::FromUTF8("12" NBSP "345" NBSP "678,12") == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( "12.345.678,12" == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( wxString::FromUTF8(EUROSIGN " 12.345.678,12") == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_CurrencySymbol | wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( "EUR 12.345.678,12" == wxNumberFormatter::ToString(12345678.12, 2, wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_CurrencyCode | wxNumberFormatter::Style_WithThousandsSep));
+ CHECK( "12345678,12" == wxNumberFormatter::RemoveCurrencySymbolOrCode("EUR 12.345.678,12", wxNumberFormatter::Style_Currency
+ | wxNumberFormatter::Style_CurrencyCode | wxNumberFormatter::Style_WithThousandsSep));
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// test class (India locale)
+// ----------------------------------------------------------------------------
+class NumFormatterAlternateTestCase
+{
+public:
+ NumFormatterAlternateTestCase() :
+ // We need to use a locale with known decimal point and which uses the
+ // thousands separator for the tests to make sense.
+ m_locale(wxLANGUAGE_ENGLISH_INDIA, wxLOCALE_DONT_LOAD_DEFAULT)
+ {
+ }
+
+protected:
+ bool CanRunTest() const
+ {
+ bool ok = m_locale.IsOk();
+ if (ok)
+ {
+ wxLocaleNumberFormatting numForm = wxUILocale::GetCurrent().GetNumberFormatting();
+ ok = (!numForm.grouping.empty() && numForm.grouping.back() == 0);
+ }
+ return ok;
+ }
+
+private:
+ wxLocale m_locale;
+
+ wxDECLARE_NO_COPY_CLASS(NumFormatterAlternateTestCase);
+};
+
+// ----------------------------------------------------------------------------
+// tests themselves
+// ----------------------------------------------------------------------------
+
+TEST_CASE_METHOD(NumFormatterAlternateTestCase, "NumFormatterAlternateTestCase::LongToString", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( "1" == wxNumberFormatter::ToString( 1L) );
+ CHECK( "-1" == wxNumberFormatter::ToString( -1L) );
+ CHECK( "12" == wxNumberFormatter::ToString( 12L) );
+ CHECK( "-12" == wxNumberFormatter::ToString( -12L) );
+ CHECK( "123" == wxNumberFormatter::ToString( 123L) );
+ CHECK( "-123" == wxNumberFormatter::ToString( -123L) );
+ CHECK( "1,234" == wxNumberFormatter::ToString( 1234L) );
+ CHECK( "-1,234" == wxNumberFormatter::ToString( -1234L) );
+ CHECK( "12,345" == wxNumberFormatter::ToString( 12345L) );
+ CHECK( "-12,345" == wxNumberFormatter::ToString( -12345L) );
+ CHECK( "1,23,456" == wxNumberFormatter::ToString( 123456L) );
+ CHECK( "-1,23,456" == wxNumberFormatter::ToString( -123456L) );
+ CHECK( "12,34,567" == wxNumberFormatter::ToString( 1234567L) );
+ CHECK( "-12,34,567" == wxNumberFormatter::ToString( -1234567L) );
+ CHECK( "1,23,45,678" == wxNumberFormatter::ToString( 12345678L) );
+ CHECK( "-1,23,45,678" == wxNumberFormatter::ToString( -12345678L) );
+ CHECK( "12,34,56,789" == wxNumberFormatter::ToString( 123456789L) );
+}
+
+#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+TEST_CASE_METHOD(NumFormatterAlternateTestCase, "NumFormatterAlternateTestCase::LongLongToString", "[numformatter]")
+{
+ if ( !CanRunTest())
+ return;
+
+ CHECK( "1" == wxNumberFormatter::ToString(wxLL( 1)) );
+ CHECK( "12" == wxNumberFormatter::ToString(wxLL( 12)) );
+ CHECK( "123" == wxNumberFormatter::ToString(wxLL( 123)) );
+ CHECK( "1,234" == wxNumberFormatter::ToString(wxLL( 1234)) );
+ CHECK( "12,345" == wxNumberFormatter::ToString(wxLL( 12345)) );
+ CHECK( "1,23,456" == wxNumberFormatter::ToString(wxLL( 123456)) );
+ CHECK( "12,34,567" == wxNumberFormatter::ToString(wxLL( 1234567)) );
+ CHECK( "1,23,45,678" == wxNumberFormatter::ToString(wxLL( 12345678)) );
+ CHECK( "12,34,56,789" == wxNumberFormatter::ToString(wxLL( 123456789)) );
+}
+
+#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+TEST_CASE_METHOD(NumFormatterAlternateTestCase, "NumFormatterAlternateTestCase::DoubleToString", "[numformatter]")
+{
+ if ( !CanRunTest())
+ return;
+
+ CHECK( "1.0" == wxNumberFormatter::ToString(1., 1));
+ CHECK( "0.123456" == wxNumberFormatter::ToString(0.123456, 6) );
+ CHECK( "1.234567" == wxNumberFormatter::ToString(1.234567, 6) );
+ CHECK( "12.34567" == wxNumberFormatter::ToString(12.34567, 5) );
+ CHECK( "123.4567" == wxNumberFormatter::ToString(123.4567, 4) );
+ CHECK( "1,234.56" == wxNumberFormatter::ToString(1234.56, 2) );
+ CHECK( "12,345.6" == wxNumberFormatter::ToString(12345.6, 1) );
+ CHECK( "12,345.6" == wxNumberFormatter::ToString(12345.6, 1) );
+ CHECK( "12,34,56,789.0" == wxNumberFormatter::ToString(123456789., 1) );
+ CHECK( "12,34,56,789.012" == wxNumberFormatter::ToString(123456789.012, 3) );
+ CHECK( "12,345" == wxNumberFormatter::ToString(12345.012, -1) );
+ CHECK( "-123.1230" == wxNumberFormatter::ToString(-123.123, 4, wxNumberFormatter::Style_None) );
+ CHECK( "0.0" == wxNumberFormatter::ToString(0.02, 1, wxNumberFormatter::Style_None) );
+ CHECK( "-0.0" == wxNumberFormatter::ToString(-0.02, 1, wxNumberFormatter::Style_None) );
+}
+
+TEST_CASE_METHOD(NumFormatterAlternateTestCase, "NumFormatterAlternateTestCase::NoTrailingZeroes", "[numformatter]")
+{
+ WX_ASSERT_FAILS_WITH_ASSERT
+ (
+ wxNumberFormatter::ToString(123L, wxNumberFormatter::Style_NoTrailingZeroes)
+ );
+
+ if ( !CanRunTest())
+ return;
+
+ CHECK( "123.000" == wxNumberFormatter::ToString(123., 3) );
+ CHECK( "123" == wxNumberFormatter::ToString(123., 3, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "123" == wxNumberFormatter::ToString(123., 9, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "123.456" == wxNumberFormatter::ToString(123.456, 3, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "123.456000000" == wxNumberFormatter::ToString(123.456, 9) );
+ CHECK( "123.456" == wxNumberFormatter::ToString(123.456, 9, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "123.12" == wxNumberFormatter::ToString(123.123, 2, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "123" == wxNumberFormatter::ToString(123.123, 0, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "0" == wxNumberFormatter::ToString(-0.000123, 3, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "123" == wxNumberFormatter::ToString(123., -1, wxNumberFormatter::Style_NoTrailingZeroes) );
+ CHECK( "1e-120" == wxNumberFormatter::ToString(1e-120, -1, wxNumberFormatter::Style_NoTrailingZeroes) );
+}
+
+TEST_CASE_METHOD(NumFormatterAlternateTestCase, "NumFormatterAlternateTestCase::LongFromString", "[numformatter]")
+{
+ if ( !CanRunTest())
+ return;
+
+ WX_ASSERT_FAILS_WITH_ASSERT
+ (
+ wxNumberFormatter::FromString("123", static_cast<long *>(0))
+ );
+
+ long l;
+ CHECK( !wxNumberFormatter::FromString("", &l) );
+ CHECK( !wxNumberFormatter::FromString("foo", &l) );
+ CHECK( !wxNumberFormatter::FromString("1.234", &l) );
+
+ CHECK( wxNumberFormatter::FromString("123", &l) );
+ CHECK( 123 == l );
+
+ CHECK( wxNumberFormatter::FromString("1234", &l) );
+ CHECK( 1234 == l );
+
+ CHECK( wxNumberFormatter::FromString("1,234", &l) );
+ CHECK( 1234 == l );
+
+ CHECK( wxNumberFormatter::FromString("12,345", &l) );
+ CHECK( 12345 == l );
+
+ CHECK( wxNumberFormatter::FromString("1,23,456", &l) );
+ CHECK( 123456 == l );
+
+ CHECK( wxNumberFormatter::FromString("1,234,567", &l) );
+ CHECK( 1234567 == l );
+}
+
+#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+TEST_CASE_METHOD(NumFormatterAlternateTestCase, "NumFormatterAlternateTestCase::LongLongFromString", "[numformatter]")
+{
+ if ( !CanRunTest())
+ return;
+
+ WX_ASSERT_FAILS_WITH_ASSERT
+ (
+ wxNumberFormatter::FromString("123", static_cast<wxLongLong_t *>(0))
+ );
+
+ wxLongLong_t l;
+ CHECK( !wxNumberFormatter::FromString("", &l) );
+ CHECK( !wxNumberFormatter::FromString("foo", &l) );
+ CHECK( !wxNumberFormatter::FromString("1.234", &l) );
+
+ CHECK( wxNumberFormatter::FromString("123", &l) );
+ CHECK( 123 == l );
+
+ CHECK( wxNumberFormatter::FromString("1234", &l) );
+ CHECK( 1234 == l );
+
+ CHECK( wxNumberFormatter::FromString("1,234", &l) );
+ CHECK( 1234 == l );
+
+ CHECK( wxNumberFormatter::FromString("12,345", &l) );
+ CHECK( 12345 == l );
+
+ CHECK( wxNumberFormatter::FromString("1,23,456", &l) );
+ CHECK( 123456 == l );
+
+ CHECK( wxNumberFormatter::FromString("12,34,567", &l) );
+ CHECK( 1234567 == l );
+}
+
+#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+TEST_CASE_METHOD(NumFormatterAlternateTestCase, "NumFormatterAlternateTestCase::DoubleFromString", "[numformatter]")
+{
+ if ( !CanRunTest())
+ return;
+
+ WX_ASSERT_FAILS_WITH_ASSERT
+ (
+ wxNumberFormatter::FromString("123", static_cast<double *>(0))
+ );
+
+ double d;
+ CHECK( !wxNumberFormatter::FromString("", &d) );
+ CHECK( !wxNumberFormatter::FromString("bar", &d) );
+
+ CHECK( wxNumberFormatter::FromString("123", &d) );
+ CHECK( 123. == d );
+
+ CHECK( wxNumberFormatter::FromString("123.456789012", &d) );
+ CHECK( 123.456789012 == d );
+
+ CHECK( wxNumberFormatter::FromString("1,234.56789012", &d) );
+ CHECK( 1234.56789012 == d );
+
+ CHECK( wxNumberFormatter::FromString("12,345.6789012", &d) );
+ CHECK( 12345.6789012 == d );
+
+ CHECK( wxNumberFormatter::FromString("1,23,456.789012", &d) );
+ CHECK( 123456.789012 == d );
+
+ CHECK( wxNumberFormatter::FromString("1,234,567.89012", &d) );
+ CHECK( 1234567.89012 == d );
+
+ CHECK( wxNumberFormatter::FromString("1,23,45,678.9012", &d) );
+ CHECK( 12345678.9012 == d );
+
+ CHECK( wxNumberFormatter::FromString("12,34,56,789.012", &d) );
+ CHECK( 123456789.012 == d );
+
+ CHECK( wxNumberFormatter::FromString("123456789.012", &d) );
+ CHECK( 123456789.012 == d );
+}
+
+//--------------------------------------------------------------------------
+// Test case with grouping in US format i.e, "3;0"
+//--------------------------------------------------------------------------
+
+class FormatNumberTestCase
+{
+public:
+ FormatNumberTestCase() :
+ // We need to use a locale with known decimal point and which uses the
+ // thousands separator for the tests to make sense.
+ m_locale(wxLANGUAGE_ENGLISH_US, wxLOCALE_DONT_LOAD_DEFAULT)
+ {
+ }
+
+protected:
+ bool CanRunTest() const { return m_locale.IsOk(); }
+
+private:
+ wxLocale m_locale;
+
+};
+
+TEST_CASE_METHOD(FormatNumberTestCase, "FormatNumberTestCase::PositiveIntegers", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( 0L) == "0" );
+ CHECK( wxNumberFormatter::ToString( 1L) == "1" );
+ CHECK( wxNumberFormatter::ToString( 12L) == "12" );
+ CHECK( wxNumberFormatter::ToString( 123L) == "123" );
+ CHECK( wxNumberFormatter::ToString( 1234L) == "1,234" );
+ CHECK( wxNumberFormatter::ToString( 12345L) == "12,345" );
+ CHECK( wxNumberFormatter::ToString( 123456L) == "123,456" );
+ CHECK( wxNumberFormatter::ToString( 1234567L) == "1,234,567" );
+ CHECK( wxNumberFormatter::ToString( 12345678L) == "12,345,678" );
+ CHECK( wxNumberFormatter::ToString( 123456789L) == "123,456,789" );
+ CHECK( wxNumberFormatter::ToString( 1234567890L) == "1,234,567,890" );
+}
+
+TEST_CASE_METHOD(FormatNumberTestCase, "FormatNumberTestCase::NegativeIntegers", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( -1L) == "-1" );
+ CHECK( wxNumberFormatter::ToString( -12L) == "-12" );
+ CHECK( wxNumberFormatter::ToString( -123L) == "-123" );
+ CHECK( wxNumberFormatter::ToString( -1234L) == "-1,234" );
+ CHECK( wxNumberFormatter::ToString( -12345L) == "-12,345" );
+ CHECK( wxNumberFormatter::ToString( -123456L) == "-123,456" );
+ CHECK( wxNumberFormatter::ToString( -1234567L) == "-1,234,567" );
+ CHECK( wxNumberFormatter::ToString( -12345678L) == "-12,345,678" );
+ CHECK( wxNumberFormatter::ToString( -123456789L) == "-123,456,789" );
+ CHECK( wxNumberFormatter::ToString( -1234567890L) == "-1,234,567,890" );
+}
+
+TEST_CASE_METHOD(FormatNumberTestCase, "FormatNumberTestCase::PositiveReals", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( 0.1234, 4) == "0.1234" );
+ CHECK( wxNumberFormatter::ToString( 1.1234, 4) == "1.1234" );
+ CHECK( wxNumberFormatter::ToString( 12.1234, 4) == "12.1234" );
+ CHECK( wxNumberFormatter::ToString( 123.1234, 4) == "123.1234" );
+ CHECK( wxNumberFormatter::ToString( 1234.1234, 4) == "1,234.1234" );
+ CHECK( wxNumberFormatter::ToString( 12345.1234, 4) == "12,345.1234" );
+ CHECK( wxNumberFormatter::ToString( 123456.1234, 4) == "123,456.1234" );
+ CHECK( wxNumberFormatter::ToString( 1234567.1234, 4) == "1,234,567.1234" );
+ CHECK( wxNumberFormatter::ToString( 12345678.1234, 4) == "12,345,678.1234" );
+ CHECK( wxNumberFormatter::ToString( 123456789.1234, 4) == "123,456,789.1234" );
+ CHECK( wxNumberFormatter::ToString( 1234567890.1234, 4) == "1,234,567,890.1234" );
+}
+
+TEST_CASE_METHOD(FormatNumberTestCase, "FormatNumberTestCase::NegativeReals", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( -1.1234, 4) == "-1.1234" );
+ CHECK( wxNumberFormatter::ToString( -12.1234, 4) == "-12.1234" );
+ CHECK( wxNumberFormatter::ToString( -123.1234, 4) == "-123.1234" );
+ CHECK( wxNumberFormatter::ToString( -1234.1234, 4) == "-1,234.1234" );
+ CHECK( wxNumberFormatter::ToString( -12345.1234, 4) == "-12,345.1234" );
+ CHECK( wxNumberFormatter::ToString( -123456.1234, 4) == "-123,456.1234" );
+ CHECK( wxNumberFormatter::ToString( -1234567.1234, 4) == "-1,234,567.1234" );
+ CHECK( wxNumberFormatter::ToString( -12345678.1234, 4) == "-12,345,678.1234" );
+ CHECK( wxNumberFormatter::ToString( -123456789.1234, 4) == "-123,456,789.1234" );
+ CHECK( wxNumberFormatter::ToString( -1234567890.1234, 4) == "-1,234,567,890.1234" );
+}
+
+//--------------------------------------------------------------------------
+// Alternate test case with grouping in Indian format i.e, "3;2;0"
+//--------------------------------------------------------------------------
+
+class FormatNumberAlternateTestCase
+{
+public:
+ FormatNumberAlternateTestCase() :
+ // We need to use a locale with known decimal point and which uses the
+ // thousands separator for the tests to make sense.
+ m_locale(wxLANGUAGE_ENGLISH_INDIA, wxLOCALE_DONT_LOAD_DEFAULT)
+ {
+ }
+
+protected:
+ bool CanRunTest() const { return m_locale.IsOk(); }
+
+private:
+ wxLocale m_locale;
+};
+
+TEST_CASE_METHOD(FormatNumberAlternateTestCase, "FormatNumberAlternateTestCase::PositiveIntegers", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( 0L) == "0" );
+ CHECK( wxNumberFormatter::ToString( 1L) == "1" );
+ CHECK( wxNumberFormatter::ToString( 12L) == "12" );
+ CHECK( wxNumberFormatter::ToString( 123L) == "123" );
+ CHECK( wxNumberFormatter::ToString( 1234L) == "1,234" );
+ CHECK( wxNumberFormatter::ToString( 12345L) == "12,345" );
+ CHECK( wxNumberFormatter::ToString( 123456L) == "1,23,456" );
+ CHECK( wxNumberFormatter::ToString( 1234567L) == "12,34,567" );
+ CHECK( wxNumberFormatter::ToString( 12345678L) == "1,23,45,678" );
+ CHECK( wxNumberFormatter::ToString( 123456789L) == "12,34,56,789" );
+ CHECK( wxNumberFormatter::ToString( 1234567890L) == "1,23,45,67,890" );
+}
+
+TEST_CASE_METHOD(FormatNumberAlternateTestCase, "FormatNumberAlternateTestCase::NegativeIntegers", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( -1L) == "-1" );
+ CHECK( wxNumberFormatter::ToString( -12L) == "-12" );
+ CHECK( wxNumberFormatter::ToString( -123L) == "-123" );
+ CHECK( wxNumberFormatter::ToString( -1234L) == "-1,234" );
+ CHECK( wxNumberFormatter::ToString( -12345L) == "-12,345" );
+ CHECK( wxNumberFormatter::ToString( -123456L) == "-1,23,456" );
+ CHECK( wxNumberFormatter::ToString( -1234567L) == "-12,34,567" );
+ CHECK( wxNumberFormatter::ToString( -12345678L) == "-1,23,45,678" );
+ CHECK( wxNumberFormatter::ToString( -123456789L) == "-12,34,56,789" );
+ CHECK( wxNumberFormatter::ToString( -1234567890L) == "-1,23,45,67,890" );
+}
+
+TEST_CASE_METHOD(FormatNumberAlternateTestCase, "FormatNumberAlternateTestCase::PositiveReals", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( 0.1234, 4) == "0.1234" );
+ CHECK( wxNumberFormatter::ToString( 1.1234, 4) == "1.1234" );
+ CHECK( wxNumberFormatter::ToString( 12.1234, 4) == "12.1234" );
+ CHECK( wxNumberFormatter::ToString( 123.1234, 4) == "123.1234" );
+ CHECK( wxNumberFormatter::ToString( 1234.1234, 4) == "1,234.1234" );
+ CHECK( wxNumberFormatter::ToString( 12345.1234, 4) == "12,345.1234" );
+ CHECK( wxNumberFormatter::ToString( 123456.1234, 4) == "1,23,456.1234" );
+ CHECK( wxNumberFormatter::ToString( 1234567.1234, 4) == "12,34,567.1234" );
+ CHECK( wxNumberFormatter::ToString( 12345678.1234, 4) == "1,23,45,678.1234" );
+ CHECK( wxNumberFormatter::ToString( 123456789.1234, 4) == "12,34,56,789.1234" );
+ CHECK( wxNumberFormatter::ToString( 1234567890.1234, 4) == "1,23,45,67,890.1234" );
+}
+
+TEST_CASE_METHOD(FormatNumberAlternateTestCase, "FormatNumberAlternateTestCase::NegativeReals", "[numformatter]")
+{
+ if (!CanRunTest())
+ return;
+
+ CHECK( wxNumberFormatter::ToString( -1.1234, 4) == "-1.1234" );
+ CHECK( wxNumberFormatter::ToString( -12.1234, 4) == "-12.1234" );
+ CHECK( wxNumberFormatter::ToString( -123.1234, 4) == "-123.1234" );
+ CHECK( wxNumberFormatter::ToString( -1234.1234, 4) == "-1,234.1234" );
+ CHECK( wxNumberFormatter::ToString( -12345.1234, 4) == "-12,345.1234" );
+ CHECK( wxNumberFormatter::ToString( -123456.1234, 4) == "-1,23,456.1234" );
+ CHECK( wxNumberFormatter::ToString( -1234567.1234, 4) == "-12,34,567.1234" );
+ CHECK( wxNumberFormatter::ToString( -12345678.1234, 4) == "-1,23,45,678.1234" );
+ CHECK( wxNumberFormatter::ToString( -123456789.1234, 4) == "-12,34,56,789.1234" );
+ CHECK( wxNumberFormatter::ToString( -1234567890.1234, 4) == "-1,23,45,67,890.1234" );
+}



View it on GitLab: https://gitlab.com/wxwidgets/wxwidgets/-/compare/9469d6d4c8eac45f1634411048e4ebb541132ccb...d0318096de0166f351a717ce8769041f0933ab7e

--
View it on GitLab: https://gitlab.com/wxwidgets/wxwidgets/-/compare/9469d6d4c8eac45f1634411048e4ebb541132ccb...d0318096de0166f351a717ce8769041f0933ab7e
You're receiving this email because of your account on gitlab.com.


Reply all
Reply to author
Forward
0 new messages