]>
Commit | Line | Data |
---|---|---|
1 | ///////////////////////////////////////////////////////////////////////////// | |
2 | // Name: src/common/valnum.cpp | |
3 | // Purpose: Numeric validator classes implementation | |
4 | // Author: Vadim Zeitlin based on the submission of Fulvio Senore | |
5 | // Created: 2010-11-06 | |
6 | // Copyright: (c) 2010 wxWidgets team | |
7 | // Licence: wxWindows licence | |
8 | ///////////////////////////////////////////////////////////////////////////// | |
9 | ||
10 | // ============================================================================ | |
11 | // Declarations | |
12 | // ============================================================================ | |
13 | ||
14 | // ---------------------------------------------------------------------------- | |
15 | // headers | |
16 | // ---------------------------------------------------------------------------- | |
17 | ||
18 | // For compilers that support precompilation, includes "wx.h". | |
19 | #include "wx/wxprec.h" | |
20 | ||
21 | #ifdef __BORLANDC__ | |
22 | #pragma hdrstop | |
23 | #endif | |
24 | ||
25 | #if wxUSE_VALIDATORS && wxUSE_TEXTCTRL | |
26 | ||
27 | #ifndef WX_PRECOMP | |
28 | #include "wx/textctrl.h" | |
29 | #include "wx/combobox.h" | |
30 | #endif | |
31 | ||
32 | #include "wx/valnum.h" | |
33 | #include "wx/numformatter.h" | |
34 | ||
35 | // ============================================================================ | |
36 | // wxNumValidatorBase implementation | |
37 | // ============================================================================ | |
38 | ||
39 | BEGIN_EVENT_TABLE(wxNumValidatorBase, wxValidator) | |
40 | EVT_CHAR(wxNumValidatorBase::OnChar) | |
41 | EVT_KILL_FOCUS(wxNumValidatorBase::OnKillFocus) | |
42 | END_EVENT_TABLE() | |
43 | ||
44 | int wxNumValidatorBase::GetFormatFlags() const | |
45 | { | |
46 | int flags = wxNumberFormatter::Style_None; | |
47 | if ( m_style & wxNUM_VAL_THOUSANDS_SEPARATOR ) | |
48 | flags |= wxNumberFormatter::Style_WithThousandsSep; | |
49 | if ( m_style & wxNUM_VAL_NO_TRAILING_ZEROES ) | |
50 | flags |= wxNumberFormatter::Style_NoTrailingZeroes; | |
51 | ||
52 | return flags; | |
53 | } | |
54 | ||
55 | wxTextEntry *wxNumValidatorBase::GetTextEntry() const | |
56 | { | |
57 | #if wxUSE_TEXTCTRL | |
58 | if ( wxTextCtrl *text = wxDynamicCast(m_validatorWindow, wxTextCtrl) ) | |
59 | return text; | |
60 | #endif // wxUSE_TEXTCTRL | |
61 | ||
62 | #if wxUSE_COMBOBOX | |
63 | if ( wxComboBox *combo = wxDynamicCast(m_validatorWindow, wxComboBox) ) | |
64 | return combo; | |
65 | #endif // wxUSE_COMBOBOX | |
66 | ||
67 | wxFAIL_MSG("Can only be used with wxTextCtrl or wxComboBox"); | |
68 | ||
69 | return NULL; | |
70 | } | |
71 | ||
72 | void | |
73 | wxNumValidatorBase::GetCurrentValueAndInsertionPoint(wxString& val, | |
74 | int& pos) const | |
75 | { | |
76 | wxTextEntry * const control = GetTextEntry(); | |
77 | if ( !control ) | |
78 | return; | |
79 | ||
80 | val = control->GetValue(); | |
81 | pos = control->GetInsertionPoint(); | |
82 | ||
83 | long selFrom, selTo; | |
84 | control->GetSelection(&selFrom, &selTo); | |
85 | ||
86 | const long selLen = selTo - selFrom; | |
87 | if ( selLen ) | |
88 | { | |
89 | // Remove selected text because pressing a key would make it disappear. | |
90 | val.erase(selFrom, selLen); | |
91 | ||
92 | // And adjust the insertion point to have correct position in the new | |
93 | // string. | |
94 | if ( pos > selFrom ) | |
95 | { | |
96 | if ( pos >= selTo ) | |
97 | pos -= selLen; | |
98 | else | |
99 | pos = selFrom; | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | bool wxNumValidatorBase::IsMinusOk(const wxString& val, int pos) const | |
105 | { | |
106 | // Minus is only ever accepted in the beginning of the string. | |
107 | if ( pos != 0 ) | |
108 | return false; | |
109 | ||
110 | // And then only if there is no existing minus sign there. | |
111 | if ( !val.empty() && val[0] == '-' ) | |
112 | return false; | |
113 | ||
114 | return true; | |
115 | } | |
116 | ||
117 | void wxNumValidatorBase::OnChar(wxKeyEvent& event) | |
118 | { | |
119 | // By default we just validate this key so don't prevent the normal | |
120 | // handling from taking place. | |
121 | event.Skip(); | |
122 | ||
123 | if ( !m_validatorWindow ) | |
124 | return; | |
125 | ||
126 | #if wxUSE_UNICODE | |
127 | const int ch = event.GetUnicodeKey(); | |
128 | if ( ch == WXK_NONE ) | |
129 | { | |
130 | // It's a character without any Unicode equivalent at all, e.g. cursor | |
131 | // arrow or function key, we never filter those. | |
132 | return; | |
133 | } | |
134 | #else // !wxUSE_UNICODE | |
135 | const int ch = event.GetKeyCode(); | |
136 | if ( ch > WXK_DELETE ) | |
137 | { | |
138 | // Not a character neither. | |
139 | return; | |
140 | } | |
141 | #endif // wxUSE_UNICODE/!wxUSE_UNICODE | |
142 | ||
143 | if ( ch < WXK_SPACE || ch == WXK_DELETE ) | |
144 | { | |
145 | // Allow ASCII control characters and Delete. | |
146 | return; | |
147 | } | |
148 | ||
149 | // Check if this character is allowed in the current state. | |
150 | wxString val; | |
151 | int pos; | |
152 | GetCurrentValueAndInsertionPoint(val, pos); | |
153 | ||
154 | if ( !IsCharOk(val, pos, ch) ) | |
155 | { | |
156 | if ( !wxValidator::IsSilent() ) | |
157 | wxBell(); | |
158 | ||
159 | // Do not skip the event in this case, stop handling it here. | |
160 | event.Skip(false); | |
161 | } | |
162 | } | |
163 | ||
164 | void wxNumValidatorBase::OnKillFocus(wxFocusEvent& event) | |
165 | { | |
166 | wxTextEntry * const control = GetTextEntry(); | |
167 | if ( !control ) | |
168 | return; | |
169 | ||
170 | // When we change the control value below, its "modified" status is reset | |
171 | // so we need to explicitly keep it marked as modified if it was so in the | |
172 | // first place. | |
173 | // | |
174 | // Notice that only wxTextCtrl (and not wxTextEntry) has | |
175 | // IsModified()/MarkDirty() methods hence the need for dynamic cast. | |
176 | wxTextCtrl * const text = wxDynamicCast(m_validatorWindow, wxTextCtrl); | |
177 | const bool wasModified = text ? text->IsModified() : false; | |
178 | ||
179 | control->ChangeValue(NormalizeString(control->GetValue())); | |
180 | ||
181 | if ( wasModified ) | |
182 | text->MarkDirty(); | |
183 | ||
184 | event.Skip(); | |
185 | } | |
186 | ||
187 | // ============================================================================ | |
188 | // wxIntegerValidatorBase implementation | |
189 | // ============================================================================ | |
190 | ||
191 | wxString wxIntegerValidatorBase::ToString(LongestValueType value) const | |
192 | { | |
193 | return wxNumberFormatter::ToString(value, GetFormatFlags()); | |
194 | } | |
195 | ||
196 | bool | |
197 | wxIntegerValidatorBase::FromString(const wxString& s, LongestValueType *value) | |
198 | { | |
199 | return wxNumberFormatter::FromString(s, value); | |
200 | } | |
201 | ||
202 | bool | |
203 | wxIntegerValidatorBase::IsCharOk(const wxString& val, int pos, wxChar ch) const | |
204 | { | |
205 | // We may accept minus sign if we can represent negative numbers at all. | |
206 | if ( ch == '-' ) | |
207 | { | |
208 | // Notice that entering '-' can make our value invalid, for example if | |
209 | // we're limited to -5..15 range and the current value is 12, then the | |
210 | // new value would be (invalid) -12. We consider it better to let the | |
211 | // user do this because perhaps he is going to press Delete key next to | |
212 | // make it -2 and forcing him to delete 1 first would be unnatural. | |
213 | // | |
214 | // TODO: It would be nice to indicate that the current control contents | |
215 | // is invalid (if it's indeed going to be the case) once | |
216 | // wxValidator supports doing this non-intrusively. | |
217 | return m_min < 0 && IsMinusOk(val, pos); | |
218 | } | |
219 | ||
220 | // We only accept digits here (remember that '-' is taken care of by the | |
221 | // base class already). | |
222 | if ( ch < '0' || ch > '9' ) | |
223 | return false; | |
224 | ||
225 | // And the value after insertion needs to be in the defined range. | |
226 | LongestValueType value; | |
227 | if ( !FromString(GetValueAfterInsertingChar(val, pos, ch), &value) ) | |
228 | return false; | |
229 | ||
230 | return IsInRange(value); | |
231 | } | |
232 | ||
233 | // ============================================================================ | |
234 | // wxFloatingPointValidatorBase implementation | |
235 | // ============================================================================ | |
236 | ||
237 | wxString wxFloatingPointValidatorBase::ToString(LongestValueType value) const | |
238 | { | |
239 | return wxNumberFormatter::ToString(value, m_precision, GetFormatFlags()); | |
240 | } | |
241 | ||
242 | bool | |
243 | wxFloatingPointValidatorBase::FromString(const wxString& s, | |
244 | LongestValueType *value) | |
245 | { | |
246 | return wxNumberFormatter::FromString(s, value); | |
247 | } | |
248 | ||
249 | bool | |
250 | wxFloatingPointValidatorBase::IsCharOk(const wxString& val, | |
251 | int pos, | |
252 | wxChar ch) const | |
253 | { | |
254 | // We may accept minus sign if we can represent negative numbers at all. | |
255 | if ( ch == '-' ) | |
256 | return m_min < 0 && IsMinusOk(val, pos); | |
257 | ||
258 | const wxChar separator = wxNumberFormatter::GetDecimalSeparator(); | |
259 | if ( ch == separator ) | |
260 | { | |
261 | if ( val.find(separator) != wxString::npos ) | |
262 | { | |
263 | // There is already a decimal separator, can't insert another one. | |
264 | return false; | |
265 | } | |
266 | ||
267 | // Prepending a separator before the minus sign isn't allowed. | |
268 | if ( pos == 0 && !val.empty() && val[0] == '-' ) | |
269 | return false; | |
270 | ||
271 | // Otherwise always accept it, adding a decimal separator doesn't | |
272 | // change the number value and, in particular, can't make it invalid. | |
273 | // OTOH the checks below might not pass because strings like "." or | |
274 | // "-." are not valid numbers so parsing them would fail, hence we need | |
275 | // to treat it specially here. | |
276 | return true; | |
277 | } | |
278 | ||
279 | // Must be a digit then. | |
280 | if ( ch < '0' || ch > '9' ) | |
281 | return false; | |
282 | ||
283 | // Check whether the value we'd obtain if we accepted this key is correct. | |
284 | const wxString newval(GetValueAfterInsertingChar(val, pos, ch)); | |
285 | ||
286 | LongestValueType value; | |
287 | if ( !FromString(newval, &value) ) | |
288 | return false; | |
289 | ||
290 | // Also check that it doesn't have too many decimal digits. | |
291 | const size_t posSep = newval.find(separator); | |
292 | if ( posSep != wxString::npos && newval.length() - posSep - 1 > m_precision ) | |
293 | return false; | |
294 | ||
295 | // Finally check whether it is in the range. | |
296 | return IsInRange(value); | |
297 | } | |
298 | ||
299 | #endif // wxUSE_VALIDATORS && wxUSE_TEXTCTRL |