///////////////////////////////////////////////////////////////////////////// // // Backport from wxWidgets-3.0-rc1 // ///////////////////////////////////////////////////////////////////////////// // Name: src/common/numformatter.cpp // Purpose: NumberFormatter // Author: Fulvio Senore, Vadim Zeitlin // Created: 2010-11-06 // Copyright: (c) 2010 wxWidgets team // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include #ifdef __BORLANDC__ #pragma hdrstop #endif #ifdef __WIN32__ #include #endif #include "numformatter.h" #include #include // for setlocale and LC_ALL #include #include // ---------------------------------------------------------------------------- // local helpers // ---------------------------------------------------------------------------- // ============================================================================ // NumberFormatter implementation // ============================================================================ // ---------------------------------------------------------------------------- // Locale information accessors // ---------------------------------------------------------------------------- wxChar NumberFormatter::GetDecimalSeparator() { #if wxUSE_INTL struct lconv *info = localeconv(); wxString s = info ? wxString::FromUTF8(info->decimal_point) : wxT("."); if (s.empty()) { // We really must have something for decimal separator, so fall // back to the C locale default. s = wxT("."); } return s[0]; #else // !wxUSE_INTL return wxT('.'); #endif // wxUSE_INTL/!wxUSE_INTL } bool NumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep) { #if wxUSE_INTL struct lconv *info = localeconv(); wxString s = info ? wxString::FromUTF8(info->thousands_sep) : wxT(""); if (s.IsEmpty()) { return false; } *sep = s[0]; return true; #else // !wxUSE_INTL wxUnusedVar(sep); return false; #endif // wxUSE_INTL/!wxUSE_INTL } // ---------------------------------------------------------------------------- // Conversion to string and helpers // ---------------------------------------------------------------------------- wxString NumberFormatter::PostProcessIntString(wxString s, int style) { if ( style & Style_WithThousandsSep ) AddThousandsSeparators(s); wxASSERT_MSG( !(style & Style_NoTrailingZeroes), wxT("Style_NoTrailingZeroes can't be used with integer values") ); wxASSERT_MSG( !(style & Style_OneTrailingZero), wxT("Style_OneTrailingZero can't be used with integer values") ); wxASSERT_MSG( !(style & Style_TwoTrailingZeroes), wxT("Style_TwoTrailingZeroes can't be used with integer values") ); wxASSERT_MSG( !(style & Style_ThreeTrailingZeroes), wxT("Style_ThreeTrailingZeroes can't be used with integer values") ); return s; } wxString NumberFormatter::ToString(long val, int style) { return PostProcessIntString(wxString::Format(wxT("%ld"), val), style); } #ifdef HAS_LONG_LONG_T_DIFFERENT_FROM_LONG wxString NumberFormatter::ToString(wxLongLong_t val, int style) { #if wxCHECK_VERSION(3,0,0) return PostProcessIntString(wxString::Format("%" wxLongLongFmtSpec "d", val), style); #else return PostProcessIntString(wxString::Format(wxT("%") wxLongLongFmtSpec wxT("d"), val), style); #endif } #endif // HAS_LONG_LONG_T_DIFFERENT_FROM_LONG wxString NumberFormatter::ToString(double val, int precision, int style) { wxString format; if ( precision == -1 ) { format = wxT("%g"); } else // Use fixed precision. { format.Printf(wxT("%%.%df"), precision); } if (isnan(val)) { return _("NaN"); } if (isinf(val)) { return _("-Infinity"); } wxString s = wxString::Format(format, val); if ( style & Style_WithThousandsSep ) AddThousandsSeparators(s); if ( precision != -1 ) { if ( style & Style_NoTrailingZeroes ) RemoveTrailingZeroes(s, 0); if ( style & Style_OneTrailingZero ) RemoveTrailingZeroes(s, 1); if ( style & Style_TwoTrailingZeroes ) RemoveTrailingZeroes(s, 2); if ( style & Style_ThreeTrailingZeroes ) RemoveTrailingZeroes(s, 3); } return s; } void NumberFormatter::AddThousandsSeparators(wxString& s) { wxChar thousandsSep; if ( !GetThousandsSeparatorIfUsed(&thousandsSep) ) return; size_t pos = s.find(GetDecimalSeparator()); if ( pos == wxString::npos ) { // Start grouping at the end of an integer number. 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(wxT("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; while ( pos > start + GROUP_LEN ) { pos -= GROUP_LEN; s.insert(pos, thousandsSep); } } void NumberFormatter::RemoveTrailingZeroes(wxString& s, size_t retain /* = 0 */) { const size_t posDecSep = s.find(GetDecimalSeparator()); wxCHECK_RET( posDecSep != wxString::npos, wxString::Format(wxT("No decimal separator in \"%s\""), s.c_str()) ); wxCHECK_RET( posDecSep, wxT("Can't start with decimal separator" )); // Find the last character to keep. size_t posLastCharacterToKeep = s.find_last_not_of(wxT("0")); // If it's the decimal separator itself, remove it. if ((posLastCharacterToKeep == posDecSep) && (retain == 0)) { posLastCharacterToKeep--; } else if ((posLastCharacterToKeep - posDecSep) < retain) { posLastCharacterToKeep = retain + posDecSep; } s.erase(posLastCharacterToKeep + 1); } // ---------------------------------------------------------------------------- // Conversion from strings // ---------------------------------------------------------------------------- void NumberFormatter::RemoveThousandsSeparators(wxString& s) { wxChar thousandsSep; if ( !GetThousandsSeparatorIfUsed(&thousandsSep) ) return; s.Replace(wxString(thousandsSep), wxString()); } bool NumberFormatter::FromString(wxString s, long *val) { RemoveThousandsSeparators(s); return s.ToLong(val); } #ifdef HAS_LONG_LONG_T_DIFFERENT_FROM_LONG bool NumberFormatter::FromString(wxString s, wxLongLong_t *val) { RemoveThousandsSeparators(s); return s.ToLongLong(val); } #endif // HAS_LONG_LONG_T_DIFFERENT_FROM_LONG bool NumberFormatter::FromString(wxString s, double *val) { RemoveThousandsSeparators(s); return s.ToDouble(val); }