]> git.saurik.com Git - wxWidgets.git/blob - src/common/numformatter.cpp
Include more information in assert in wxNumberFormatter.
[wxWidgets.git] / src / common / numformatter.cpp
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
24 // ============================================================================
25 // wxNumberFormatter implementation
26 // ============================================================================
27
28 // ----------------------------------------------------------------------------
29 // Locale information accessors
30 // ----------------------------------------------------------------------------
31
32 wxChar wxNumberFormatter::GetDecimalSeparator()
33 {
34 // Notice that while using static variable here is not MT-safe, the worst
35 // that can happen is that we redo the initialization if we're called
36 // concurrently from more than one thread so it's not a real problem.
37 static wxChar s_decimalSeparator = 0;
38
39 // Remember the locale which was current when we initialized, we must redo
40 // the initialization if the locale changed.
41 static wxLocale *s_localeUsedForInit = NULL;
42
43 if ( !s_decimalSeparator || (s_localeUsedForInit != wxGetLocale()) )
44 {
45 const wxString
46 s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
47 if ( s.empty() )
48 {
49 // We really must have something for decimal separator, so fall
50 // back to the C locale default.
51 s_decimalSeparator = '.';
52 }
53 else
54 {
55 // To the best of my knowledge there are no locales like this.
56 wxASSERT_MSG( s.length() == 1,
57 "Multi-character decimal separator?" );
58
59 s_decimalSeparator = s[0];
60 }
61
62 s_localeUsedForInit = wxGetLocale();
63 }
64
65 return s_decimalSeparator;
66 }
67
68 bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
69 {
70 static wxChar s_thousandsSeparator = 0;
71 static bool s_initialized = false;
72 static wxLocale *s_localeUsedForInit = NULL;
73
74 if ( !s_initialized || (s_localeUsedForInit != wxGetLocale()) )
75 {
76 const wxString
77 s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER);
78 if ( !s.empty() )
79 {
80 wxASSERT_MSG( s.length() == 1,
81 "Multi-character thousands separator?" );
82
83 s_thousandsSeparator = s[0];
84 }
85 //else: Unlike above it's perfectly fine for the thousands separator to
86 // be empty if grouping is not used, so just leave it as 0.
87
88 s_initialized = true;
89 s_localeUsedForInit = wxGetLocale();
90 }
91
92 if ( !s_thousandsSeparator )
93 return false;
94
95 if ( sep )
96 *sep = s_thousandsSeparator;
97
98 return true;
99 }
100
101 // ----------------------------------------------------------------------------
102 // Conversion to string and helpers
103 // ----------------------------------------------------------------------------
104
105 wxString wxNumberFormatter::PostProcessIntString(wxString s, int style)
106 {
107 if ( style & Style_WithThousandsSep )
108 AddThousandsSeparators(s);
109
110 wxASSERT_MSG( !(style & Style_NoTrailingZeroes),
111 "Style_NoTrailingZeroes can't be used with integer values" );
112
113 return s;
114 }
115
116 wxString wxNumberFormatter::ToString(long val, int style)
117 {
118 return PostProcessIntString(wxString::Format("%ld", val), style);
119 }
120
121 #ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
122
123 wxString wxNumberFormatter::ToString(wxLongLong_t val, int style)
124 {
125 return PostProcessIntString(wxString::Format("%" wxLongLongFmtSpec "d", val),
126 style);
127 }
128
129 #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
130
131 wxString wxNumberFormatter::ToString(double val, int precision, int style)
132 {
133 const wxString fmt = wxString::Format("%%.%df", precision);
134 wxString s = wxString::Format(fmt, val);
135
136 if ( style & Style_WithThousandsSep )
137 AddThousandsSeparators(s);
138
139 if ( style & Style_NoTrailingZeroes )
140 RemoveTrailingZeroes(s);
141
142 return s;
143 }
144
145 void wxNumberFormatter::AddThousandsSeparators(wxString& s)
146 {
147 wxChar thousandsSep;
148 if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
149 return;
150
151 size_t pos = s.find(GetDecimalSeparator());
152 if ( pos == wxString::npos )
153 {
154 // Start grouping at the end of an integer number.
155 pos = s.length();
156 }
157
158 // We currently group digits by 3 independently of the locale. This is not
159 // the right thing to do and we should use lconv::grouping (under POSIX)
160 // and GetLocaleInfo(LOCALE_SGROUPING) (under MSW) to get information about
161 // the correct grouping to use. This is something that needs to be done at
162 // wxLocale level first and then used here in the future (TODO).
163 const size_t GROUP_LEN = 3;
164
165 while ( pos > GROUP_LEN )
166 {
167 pos -= GROUP_LEN;
168 s.insert(pos, thousandsSep);
169 }
170 }
171
172 void wxNumberFormatter::RemoveTrailingZeroes(wxString& s)
173 {
174 const size_t posDecSep = s.find(GetDecimalSeparator());
175 wxCHECK_RET( posDecSep != wxString::npos,
176 wxString::Format("No decimal separator in \"%s\"", s) );
177 wxCHECK_RET( posDecSep, "Can't start with decimal separator" );
178
179 // Find the last character to keep.
180 size_t posLastNonZero = s.find_last_not_of("0");
181
182 // If it's the decimal separator itself, don't keep it neither.
183 if ( posLastNonZero == posDecSep )
184 posLastNonZero--;
185
186 s.erase(posLastNonZero + 1);
187 }
188
189 // ----------------------------------------------------------------------------
190 // Conversion from strings
191 // ----------------------------------------------------------------------------
192
193 void wxNumberFormatter::RemoveThousandsSeparators(wxString& s)
194 {
195 wxChar thousandsSep;
196 if ( !GetThousandsSeparatorIfUsed(&thousandsSep) )
197 return;
198
199 s.Replace(wxString(thousandsSep), wxString());
200 }
201
202 bool wxNumberFormatter::FromString(wxString s, long *val)
203 {
204 RemoveThousandsSeparators(s);
205 return s.ToLong(val);
206 }
207
208 #ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
209
210 bool wxNumberFormatter::FromString(wxString s, wxLongLong_t *val)
211 {
212 RemoveThousandsSeparators(s);
213 return s.ToLongLong(val);
214 }
215
216 #endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
217
218 bool wxNumberFormatter::FromString(wxString s, double *val)
219 {
220 RemoveThousandsSeparators(s);
221 return s.ToDouble(val);
222 }