]> git.saurik.com Git - wxWidgets.git/blame - src/common/valnum.cpp
deal with Cocoa as we do with Carbon, see #15008
[wxWidgets.git] / src / common / valnum.cpp
CommitLineData
a54cf371 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/common/valnum.cpp
a54cf371
VZ
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
39BEGIN_EVENT_TABLE(wxNumValidatorBase, wxValidator)
40 EVT_CHAR(wxNumValidatorBase::OnChar)
41 EVT_KILL_FOCUS(wxNumValidatorBase::OnKillFocus)
42END_EVENT_TABLE()
43
44int 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
55wxTextEntry *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
72void
73wxNumValidatorBase::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
104bool 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
117void 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
164void 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
191wxString wxIntegerValidatorBase::ToString(LongestValueType value) const
192{
193 return wxNumberFormatter::ToString(value, GetFormatFlags());
194}
195
196bool
197wxIntegerValidatorBase::FromString(const wxString& s, LongestValueType *value)
198{
199 return wxNumberFormatter::FromString(s, value);
200}
201
202bool
203wxIntegerValidatorBase::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
237wxString wxFloatingPointValidatorBase::ToString(LongestValueType value) const
238{
239 return wxNumberFormatter::ToString(value, m_precision, GetFormatFlags());
240}
241
242bool
243wxFloatingPointValidatorBase::FromString(const wxString& s,
244 LongestValueType *value)
245{
246 return wxNumberFormatter::FromString(s, value);
247}
248
249bool
250wxFloatingPointValidatorBase::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