1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/numformatter.cpp
3 // Purpose: wxNumberFormatter
4 // Author: Fulvio Senore, Vadim Zeitlin
6 // Copyright: (c) 2010 wxWidgets team
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
21 #include "wx/numformatter.h"
25 #include <locale.h> // for setlocale and LC_ALL
28 // ----------------------------------------------------------------------------
30 // ----------------------------------------------------------------------------
35 // Contains information about the locale which was used to initialize our
36 // cached values of the decimal and thousands separators. Notice that it isn't
37 // enough to store just wxLocale because the user code may call setlocale()
38 // directly and storing just C locale string is not enough because we can use
39 // the OS API directly instead of the CRT ones on some platforms. So just store
58 // Return true if this is the first time this function is called for this
59 // object or if the program locale has changed since the last time it was
60 // called. Otherwise just return false indicating that updating locale-
61 // dependent information is not necessary.
62 bool NotInitializedOrHasChanged()
64 wxLocale
* const wxloc
= wxGetLocale();
65 const char * const cloc
= setlocale(LC_ALL
, NULL
);
66 if ( m_wxloc
|| m_cloc
)
68 if ( m_wxloc
== wxloc
&& strcmp(m_cloc
, cloc
) == 0 )
73 //else: Not initialized yet.
76 m_cloc
= wxCRT_StrdupA(cloc
);
91 // Non-owned pointer to wxLocale which was used.
95 // Owned pointer to the C locale string.
98 wxDECLARE_NO_COPY_CLASS(LocaleId
);
101 } // anonymous namespace
103 // ============================================================================
104 // wxNumberFormatter implementation
105 // ============================================================================
107 // ----------------------------------------------------------------------------
108 // Locale information accessors
109 // ----------------------------------------------------------------------------
111 wxChar
wxNumberFormatter::GetDecimalSeparator()
114 // Notice that while using static variable here is not MT-safe, the worst
115 // that can happen is that we redo the initialization if we're called
116 // concurrently from more than one thread so it's not a real problem.
117 static wxChar s_decimalSeparator
= 0;
119 // Remember the locale which was current when we initialized, we must redo
120 // the initialization if the locale changed.
121 static LocaleId s_localeUsedForInit
;
123 if ( s_localeUsedForInit
.NotInitializedOrHasChanged() )
126 s
= wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT
, wxLOCALE_CAT_NUMBER
);
129 // We really must have something for decimal separator, so fall
130 // back to the C locale default.
131 s_decimalSeparator
= '.';
135 // To the best of my knowledge there are no locales like this.
136 wxASSERT_MSG( s
.length() == 1,
137 "Multi-character decimal separator?" );
139 s_decimalSeparator
= s
[0];
143 return s_decimalSeparator
;
146 #endif // wxUSE_INTL/!wxUSE_INTL
149 bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar
*sep
)
152 static wxChar s_thousandsSeparator
= 0;
153 static LocaleId s_localeUsedForInit
;
155 if ( s_localeUsedForInit
.NotInitializedOrHasChanged() )
158 s
= wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP
, wxLOCALE_CAT_NUMBER
);
161 wxASSERT_MSG( s
.length() == 1,
162 "Multi-character thousands separator?" );
164 s_thousandsSeparator
= s
[0];
166 //else: Unlike above it's perfectly fine for the thousands separator to
167 // be empty if grouping is not used, so just leave it as 0.
170 if ( !s_thousandsSeparator
)
174 *sep
= s_thousandsSeparator
;
180 #endif // wxUSE_INTL/!wxUSE_INTL
183 // ----------------------------------------------------------------------------
184 // Conversion to string and helpers
185 // ----------------------------------------------------------------------------
187 wxString
wxNumberFormatter::PostProcessIntString(wxString s
, int style
)
189 if ( style
& Style_WithThousandsSep
)
190 AddThousandsSeparators(s
);
192 wxASSERT_MSG( !(style
& Style_NoTrailingZeroes
),
193 "Style_NoTrailingZeroes can't be used with integer values" );
198 wxString
wxNumberFormatter::ToString(long val
, int style
)
200 return PostProcessIntString(wxString::Format("%ld", val
), style
);
203 #ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
205 wxString
wxNumberFormatter::ToString(wxLongLong_t val
, int style
)
207 return PostProcessIntString(wxString::Format("%" wxLongLongFmtSpec
"d", val
),
211 #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
213 wxString
wxNumberFormatter::ToString(double val
, int precision
, int style
)
215 const wxString fmt
= wxString::Format("%%.%df", precision
);
216 wxString s
= wxString::Format(fmt
, val
);
218 if ( style
& Style_WithThousandsSep
)
219 AddThousandsSeparators(s
);
221 if ( style
& Style_NoTrailingZeroes
)
222 RemoveTrailingZeroes(s
);
227 void wxNumberFormatter::AddThousandsSeparators(wxString
& s
)
230 if ( !GetThousandsSeparatorIfUsed(&thousandsSep
) )
233 size_t pos
= s
.find(GetDecimalSeparator());
234 if ( pos
== wxString::npos
)
236 // Start grouping at the end of an integer number.
240 // We currently group digits by 3 independently of the locale. This is not
241 // the right thing to do and we should use lconv::grouping (under POSIX)
242 // and GetLocaleInfo(LOCALE_SGROUPING) (under MSW) to get information about
243 // the correct grouping to use. This is something that needs to be done at
244 // wxLocale level first and then used here in the future (TODO).
245 const size_t GROUP_LEN
= 3;
247 while ( pos
> GROUP_LEN
)
250 s
.insert(pos
, thousandsSep
);
254 void wxNumberFormatter::RemoveTrailingZeroes(wxString
& s
)
256 const size_t posDecSep
= s
.find(GetDecimalSeparator());
257 wxCHECK_RET( posDecSep
!= wxString::npos
,
258 wxString::Format("No decimal separator in \"%s\"", s
) );
259 wxCHECK_RET( posDecSep
, "Can't start with decimal separator" );
261 // Find the last character to keep.
262 size_t posLastNonZero
= s
.find_last_not_of("0");
264 // If it's the decimal separator itself, don't keep it neither.
265 if ( posLastNonZero
== posDecSep
)
268 s
.erase(posLastNonZero
+ 1);
271 // ----------------------------------------------------------------------------
272 // Conversion from strings
273 // ----------------------------------------------------------------------------
275 void wxNumberFormatter::RemoveThousandsSeparators(wxString
& s
)
278 if ( !GetThousandsSeparatorIfUsed(&thousandsSep
) )
281 s
.Replace(wxString(thousandsSep
), wxString());
284 bool wxNumberFormatter::FromString(wxString s
, long *val
)
286 RemoveThousandsSeparators(s
);
287 return s
.ToLong(val
);
290 #ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
292 bool wxNumberFormatter::FromString(wxString s
, wxLongLong_t
*val
)
294 RemoveThousandsSeparators(s
);
295 return s
.ToLongLong(val
);
298 #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
300 bool wxNumberFormatter::FromString(wxString s
, double *val
)
302 RemoveThousandsSeparators(s
);
303 return s
.ToDouble(val
);