]> git.saurik.com Git - wxWidgets.git/blame - src/common/numformatter.cpp
Add markup support to wxOSX/Cocoa wxStaticText and wxButton.
[wxWidgets.git] / src / common / numformatter.cpp
CommitLineData
6686fbad
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: numformatter.cpp
3// Purpose: wxNumberFormatter
4// Author: Fulvio Senore, Vadim Zeitlin
5// Created: 2010-11-06
6// Copyright: (c) 2010 wxWidgets team
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// ----------------------------------------------------------------------------
11// headers
12// ----------------------------------------------------------------------------
13
14// For compilers that support precompilation, includes "wx.h".
15#include "wx/wxprec.h"
16
17#ifdef __BORLANDC__
18 #pragma hdrstop
19#endif
20
21#include "wx/numformatter.h"
22#include "wx/intl.h"
23
d8d9844b
VZ
24// ----------------------------------------------------------------------------
25// local helpers
26// ----------------------------------------------------------------------------
27
28namespace
29{
30
31// Contains information about the locale which was used to initialize our
32// cached values of the decimal and thousands separators. Notice that it isn't
33// enough to store just wxLocale because the user code may call setlocale()
34// directly and storing just C locale string is not enough because we can use
35// the OS API directly instead of the CRT ones on some platforms. So just store
36// both.
37class LocaleId
38{
39public:
40 LocaleId()
41 {
42 m_wxloc = NULL;
43 m_cloc = NULL;
44 }
45
46 ~LocaleId()
47 {
48 Free();
49 }
50
51 // Return true if this is the first time this function is called for this
52 // object or if the program locale has changed since the last time it was
53 // called. Otherwise just return false indicating that updating locale-
54 // dependent information is not necessary.
55 bool NotInitializedOrHasChanged()
56 {
57 wxLocale * const wxloc = wxGetLocale();
58 const char * const cloc = setlocale(LC_ALL, NULL);
59 if ( m_wxloc || m_cloc )
60 {
61 if ( m_wxloc == wxloc && strcmp(m_cloc, cloc) == 0 )
62 return false;
63
64 Free();
65 }
66 //else: Not initialized yet.
67
68 m_wxloc = wxloc;
69 m_cloc = wxCRT_StrdupA(cloc);
70
71 return true;
72 }
73
74private:
75 void Free()
76 {
77 free(m_cloc);
78 }
79
80 // Non-owned pointer to wxLocale which was used.
81 wxLocale *m_wxloc;
82
83 // Owned pointer to the C locale string.
84 char *m_cloc;
85
86 wxDECLARE_NO_COPY_CLASS(LocaleId);
87};
88
89} // anonymous namespace
90
6686fbad
VZ
91// ============================================================================
92// wxNumberFormatter implementation
93// ============================================================================
94
95// ----------------------------------------------------------------------------
96// Locale information accessors
97// ----------------------------------------------------------------------------
98
99wxChar wxNumberFormatter::GetDecimalSeparator()
100{
101 // Notice that while using static variable here is not MT-safe, the worst
102 // that can happen is that we redo the initialization if we're called
103 // concurrently from more than one thread so it's not a real problem.
104 static wxChar s_decimalSeparator = 0;
105
0d30c79b
VZ
106 // Remember the locale which was current when we initialized, we must redo
107 // the initialization if the locale changed.
d8d9844b 108 static LocaleId s_localeUsedForInit;
0d30c79b 109
d8d9844b 110 if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
6686fbad
VZ
111 {
112 const wxString
113 s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
114 if ( s.empty() )
115 {
116 // We really must have something for decimal separator, so fall
117 // back to the C locale default.
118 s_decimalSeparator = '.';
119 }
120 else
121 {
122 // To the best of my knowledge there are no locales like this.
123 wxASSERT_MSG( s.length() == 1,
124 "Multi-character decimal separator?" );
125
126 s_decimalSeparator = s[0];
127 }
128 }
129
130 return s_decimalSeparator;
131}
132
133bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
134{
135 static wxChar s_thousandsSeparator = 0;
d8d9844b 136 static LocaleId s_localeUsedForInit;
6686fbad 137
d8d9844b 138 if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
6686fbad
VZ
139 {
140 const wxString
141 s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER);
142 if ( !s.empty() )
143 {
144 wxASSERT_MSG( s.length() == 1,
145 "Multi-character thousands separator?" );
146
147 s_thousandsSeparator = s[0];
148 }
149 //else: Unlike above it's perfectly fine for the thousands separator to
150 // be empty if grouping is not used, so just leave it as 0.
6686fbad
VZ
151 }
152
153 if ( !s_thousandsSeparator )
154 return false;
155
156 if ( sep )
157 *sep = s_thousandsSeparator;
158
159 return true;
160}
161
162// ----------------------------------------------------------------------------
163// Conversion to string and helpers
164// ----------------------------------------------------------------------------
165
f2a5052b 166wxString wxNumberFormatter::PostProcessIntString(wxString s, int style)
6686fbad 167{
6686fbad
VZ
168 if ( style & Style_WithThousandsSep )
169 AddThousandsSeparators(s);
170
171 wxASSERT_MSG( !(style & Style_NoTrailingZeroes),
172 "Style_NoTrailingZeroes can't be used with integer values" );
173
174 return s;
175}
176
f2a5052b
VZ
177wxString wxNumberFormatter::ToString(long val, int style)
178{
179 return PostProcessIntString(wxString::Format("%ld", val), style);
180}
181
182#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
183
184wxString wxNumberFormatter::ToString(wxLongLong_t val, int style)
185{
186 return PostProcessIntString(wxString::Format("%" wxLongLongFmtSpec "d", val),
187 style);
188}
189
190#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
191
6686fbad
VZ
192wxString wxNumberFormatter::ToString(double val, int precision, int style)
193{
194 const wxString fmt = wxString::Format("%%.%df", precision);
195 wxString s = wxString::Format(fmt, val);
196
197 if ( style & Style_WithThousandsSep )
198 AddThousandsSeparators(s);
199
200 if ( style & Style_NoTrailingZeroes )
201 RemoveTrailingZeroes(s);
202
203 return s;
204}
205
206void wxNumberFormatter::AddThousandsSeparators(wxString& s)
207{
208 wxChar thousandsSep;
209 if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
210 return;
211
212 size_t pos = s.find(GetDecimalSeparator());
213 if ( pos == wxString::npos )
214 {
215 // Start grouping at the end of an integer number.
216 pos = s.length();
217 }
218
219 // We currently group digits by 3 independently of the locale. This is not
220 // the right thing to do and we should use lconv::grouping (under POSIX)
221 // and GetLocaleInfo(LOCALE_SGROUPING) (under MSW) to get information about
222 // the correct grouping to use. This is something that needs to be done at
223 // wxLocale level first and then used here in the future (TODO).
224 const size_t GROUP_LEN = 3;
225
226 while ( pos > GROUP_LEN )
227 {
228 pos -= GROUP_LEN;
229 s.insert(pos, thousandsSep);
230 }
231}
232
233void wxNumberFormatter::RemoveTrailingZeroes(wxString& s)
234{
235 const size_t posDecSep = s.find(GetDecimalSeparator());
a7d696f1
VZ
236 wxCHECK_RET( posDecSep != wxString::npos,
237 wxString::Format("No decimal separator in \"%s\"", s) );
6686fbad
VZ
238 wxCHECK_RET( posDecSep, "Can't start with decimal separator" );
239
240 // Find the last character to keep.
241 size_t posLastNonZero = s.find_last_not_of("0");
242
243 // If it's the decimal separator itself, don't keep it neither.
244 if ( posLastNonZero == posDecSep )
245 posLastNonZero--;
246
247 s.erase(posLastNonZero + 1);
248}
249
250// ----------------------------------------------------------------------------
251// Conversion from strings
252// ----------------------------------------------------------------------------
253
254void wxNumberFormatter::RemoveThousandsSeparators(wxString& s)
255{
256 wxChar thousandsSep;
257 if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
258 return;
259
260 s.Replace(wxString(thousandsSep), wxString());
261}
262
263bool wxNumberFormatter::FromString(wxString s, long *val)
264{
265 RemoveThousandsSeparators(s);
266 return s.ToLong(val);
267}
268
f2a5052b
VZ
269#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
270
271bool wxNumberFormatter::FromString(wxString s, wxLongLong_t *val)
272{
273 RemoveThousandsSeparators(s);
274 return s.ToLongLong(val);
275}
276
277#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
278
6686fbad
VZ
279bool wxNumberFormatter::FromString(wxString s, double *val)
280{
281 RemoveThousandsSeparators(s);
282 return s.ToDouble(val);
283}