1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/valtext.cpp
3 // Purpose:     wxTextValidator
4 // Author:      Julian Smart
5 // Modified by: Francesco Montorsi
6 // Created:     04/01/98
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 #ifdef __BORLANDC__
15   #pragma hdrstop
16 #endif
17 
18 #if wxUSE_VALIDATORS && (wxUSE_TEXTCTRL || wxUSE_COMBOBOX)
19 
20 #include "wx/valtext.h"
21 
22 #ifndef WX_PRECOMP
23   #include <stdio.h>
24   #include "wx/textctrl.h"
25   #include "wx/combobox.h"
26   #include "wx/utils.h"
27   #include "wx/msgdlg.h"
28   #include "wx/intl.h"
29 #endif
30 
31 #include <ctype.h>
32 #include <string.h>
33 #include <stdlib.h>
34 
35 #include "wx/combo.h"
36 
37 // ----------------------------------------------------------------------------
38 // global helpers
39 // ----------------------------------------------------------------------------
40 
wxIsNumeric(const wxString & val)41 static bool wxIsNumeric(const wxString& val)
42 {
43     for ( wxString::const_iterator i = val.begin(); i != val.end(); ++i )
44     {
45         // Allow for "," (French) as well as "." -- in future we should
46         // use wxSystemSettings or other to do better localisation
47         if ((!wxIsdigit(*i)) &&
48             (*i != wxS('.')) && (*i != wxS(',')) && (*i != wxS('e')) &&
49             (*i != wxS('E')) && (*i != wxS('+')) && (*i != wxS('-')))
50             return false;
51     }
52     return true;
53 }
54 
55 // ----------------------------------------------------------------------------
56 // wxTextValidator
57 // ----------------------------------------------------------------------------
58 
IMPLEMENT_DYNAMIC_CLASS(wxTextValidator,wxValidator)59 IMPLEMENT_DYNAMIC_CLASS(wxTextValidator, wxValidator)
60 BEGIN_EVENT_TABLE(wxTextValidator, wxValidator)
61     EVT_CHAR(wxTextValidator::OnChar)
62 END_EVENT_TABLE()
63 
64 wxTextValidator::wxTextValidator(long style, wxString *val)
65 {
66     m_stringValue = val;
67     SetStyle(style);
68 }
69 
wxTextValidator(const wxTextValidator & val)70 wxTextValidator::wxTextValidator(const wxTextValidator& val)
71     : wxValidator()
72 {
73     Copy(val);
74 }
75 
SetStyle(long style)76 void wxTextValidator::SetStyle(long style)
77 {
78     m_validatorStyle = style;
79 
80 #if wxDEBUG_LEVEL
81     int check;
82     check = (int)HasFlag(wxFILTER_ALPHA) + (int)HasFlag(wxFILTER_ALPHANUMERIC) +
83             (int)HasFlag(wxFILTER_DIGITS) + (int)HasFlag(wxFILTER_NUMERIC);
84     wxASSERT_MSG(check <= 1,
85         "It makes sense to use only one of the wxFILTER_ALPHA/wxFILTER_ALPHANUMERIC/"
86         "wxFILTER_SIMPLE_NUMBER/wxFILTER_NUMERIC styles");
87 
88     wxASSERT_MSG(((int)HasFlag(wxFILTER_INCLUDE_LIST) + (int)HasFlag(wxFILTER_INCLUDE_CHAR_LIST) <= 1) &&
89                  ((int)HasFlag(wxFILTER_EXCLUDE_LIST) + (int)HasFlag(wxFILTER_EXCLUDE_CHAR_LIST) <= 1),
90         "Using both wxFILTER_[IN|EX]CLUDE_LIST _and_ wxFILTER_[IN|EX]CLUDE_CHAR_LIST "
91         "doesn't work since wxTextValidator internally uses the same array for both");
92 
93     check = (int)HasFlag(wxFILTER_INCLUDE_LIST) + (int)HasFlag(wxFILTER_INCLUDE_CHAR_LIST) +
94             (int)HasFlag(wxFILTER_EXCLUDE_LIST) + (int)HasFlag(wxFILTER_EXCLUDE_CHAR_LIST);
95     wxASSERT_MSG(check <= 1,
96         "Using both an include/exclude list may lead to unexpected results");
97 #endif // wxDEBUG_LEVEL
98 }
99 
Copy(const wxTextValidator & val)100 bool wxTextValidator::Copy(const wxTextValidator& val)
101 {
102     wxValidator::Copy(val);
103 
104     m_validatorStyle = val.m_validatorStyle;
105     m_stringValue = val.m_stringValue;
106 
107     m_includes    = val.m_includes;
108     m_excludes    = val.m_excludes;
109 
110     return true;
111 }
112 
GetTextEntry()113 wxTextEntry *wxTextValidator::GetTextEntry()
114 {
115 #if wxUSE_TEXTCTRL
116     if (wxDynamicCast(m_validatorWindow, wxTextCtrl))
117     {
118         return (wxTextCtrl*)m_validatorWindow;
119     }
120 #endif
121 
122 #if wxUSE_COMBOBOX
123     if (wxDynamicCast(m_validatorWindow, wxComboBox))
124     {
125         return (wxComboBox*)m_validatorWindow;
126     }
127 #endif
128 
129 #if wxUSE_COMBOCTRL
130     if (wxDynamicCast(m_validatorWindow, wxComboCtrl))
131     {
132         return (wxComboCtrl*)m_validatorWindow;
133     }
134 #endif
135 
136     wxFAIL_MSG(
137         "wxTextValidator can only be used with wxTextCtrl, wxComboBox, "
138         "or wxComboCtrl"
139     );
140 
141     return NULL;
142 }
143 
144 // Called when the value in the window must be validated.
145 // This function can pop up an error message.
Validate(wxWindow * parent)146 bool wxTextValidator::Validate(wxWindow *parent)
147 {
148     // If window is disabled, simply return
149     if ( !m_validatorWindow->IsEnabled() )
150         return true;
151 
152     wxTextEntry * const text = GetTextEntry();
153     if ( !text )
154         return false;
155 
156     wxString val(text->GetValue());
157 
158     wxString errormsg;
159 
160     // We can only do some kinds of validation once the input is complete, so
161     // check for them here:
162     if ( HasFlag(wxFILTER_EMPTY) && val.empty() )
163         errormsg = _("Required information entry is empty.");
164     else if ( HasFlag(wxFILTER_INCLUDE_LIST) && m_includes.Index(val) == wxNOT_FOUND )
165         errormsg = wxString::Format(_("'%s' is invalid"), val);
166     else if ( HasFlag(wxFILTER_EXCLUDE_LIST) && m_excludes.Index(val) != wxNOT_FOUND )
167         errormsg = wxString::Format(_("'%s' is invalid"), val);
168     else if ( !(errormsg = IsValid(val)).empty() )
169     {
170         // NB: this format string should always contain exactly one '%s'
171         wxString buf;
172         buf.Printf(errormsg, val.c_str());
173         errormsg = buf;
174     }
175 
176     if ( !errormsg.empty() )
177     {
178         m_validatorWindow->SetFocus();
179         wxMessageBox(errormsg, _("Validation conflict"),
180                      wxOK | wxICON_EXCLAMATION, parent);
181 
182         return false;
183     }
184 
185     return true;
186 }
187 
188 // Called to transfer data to the window
TransferToWindow()189 bool wxTextValidator::TransferToWindow()
190 {
191     if ( m_stringValue )
192     {
193         wxTextEntry * const text = GetTextEntry();
194         if ( !text )
195             return false;
196 
197         text->SetValue(*m_stringValue);
198     }
199 
200     return true;
201 }
202 
203 // Called to transfer data to the window
TransferFromWindow()204 bool wxTextValidator::TransferFromWindow()
205 {
206     if ( m_stringValue )
207     {
208         wxTextEntry * const text = GetTextEntry();
209         if ( !text )
210             return false;
211 
212         *m_stringValue = text->GetValue();
213     }
214 
215     return true;
216 }
217 
218 // IRIX mipsPro refuses to compile wxStringCheck<func>() if func is inline so
219 // let's work around this by using this non-template function instead of
220 // wxStringCheck(). And while this might be fractionally less efficient because
221 // the function call won't be inlined like this, we don't care enough about
222 // this to add extra #ifs for non-IRIX case.
223 namespace
224 {
225 
CheckString(bool (* func)(const wxUniChar &),const wxString & str)226 bool CheckString(bool (*func)(const wxUniChar&), const wxString& str)
227 {
228     for ( wxString::const_iterator i = str.begin(); i != str.end(); ++i )
229     {
230         if ( !func(*i) )
231             return false;
232     }
233 
234     return true;
235 }
236 
237 } // anonymous namespace
238 
IsValid(const wxString & val) const239 wxString wxTextValidator::IsValid(const wxString& val) const
240 {
241     // wxFILTER_EMPTY is checked for in wxTextValidator::Validate
242 
243     if ( HasFlag(wxFILTER_ASCII) && !val.IsAscii() )
244         return _("'%s' should only contain ASCII characters.");
245     if ( HasFlag(wxFILTER_ALPHA) && !CheckString(wxIsalpha, val) )
246         return _("'%s' should only contain alphabetic characters.");
247     if ( HasFlag(wxFILTER_ALPHANUMERIC) && !CheckString(wxIsalnum, val) )
248         return _("'%s' should only contain alphabetic or numeric characters.");
249     if ( HasFlag(wxFILTER_DIGITS) && !CheckString(wxIsdigit, val) )
250         return _("'%s' should only contain digits.");
251     if ( HasFlag(wxFILTER_NUMERIC) && !wxIsNumeric(val) )
252         return _("'%s' should be numeric.");
253     if ( HasFlag(wxFILTER_INCLUDE_CHAR_LIST) && !ContainsOnlyIncludedCharacters(val) )
254         return _("'%s' is invalid");
255     if ( HasFlag(wxFILTER_EXCLUDE_CHAR_LIST) && ContainsExcludedCharacters(val) )
256         return _("'%s' is invalid");
257 
258     return wxEmptyString;
259 }
260 
ContainsOnlyIncludedCharacters(const wxString & val) const261 bool wxTextValidator::ContainsOnlyIncludedCharacters(const wxString& val) const
262 {
263     for ( wxString::const_iterator i = val.begin(); i != val.end(); ++i )
264         if (m_includes.Index((wxString) *i) == wxNOT_FOUND)
265             // one character of 'val' is NOT present in m_includes...
266             return false;
267 
268     // all characters of 'val' are present in m_includes
269     return true;
270 }
271 
ContainsExcludedCharacters(const wxString & val) const272 bool wxTextValidator::ContainsExcludedCharacters(const wxString& val) const
273 {
274     for ( wxString::const_iterator i = val.begin(); i != val.end(); ++i )
275         if (m_excludes.Index((wxString) *i) != wxNOT_FOUND)
276             // one character of 'val' is present in m_excludes...
277             return true;
278 
279     // all characters of 'val' are NOT present in m_excludes
280     return false;
281 }
282 
SetCharIncludes(const wxString & chars)283 void wxTextValidator::SetCharIncludes(const wxString& chars)
284 {
285     wxArrayString arr;
286 
287     for ( wxString::const_iterator i = chars.begin(); i != chars.end(); ++i )
288         arr.Add(*i);
289 
290     SetIncludes(arr);
291 }
292 
SetCharExcludes(const wxString & chars)293 void wxTextValidator::SetCharExcludes(const wxString& chars)
294 {
295     wxArrayString arr;
296 
297     for ( wxString::const_iterator i = chars.begin(); i != chars.end(); ++i )
298         arr.Add(*i);
299 
300     SetExcludes(arr);
301 }
302 
OnChar(wxKeyEvent & event)303 void wxTextValidator::OnChar(wxKeyEvent& event)
304 {
305     if (!m_validatorWindow)
306     {
307         event.Skip();
308         return;
309     }
310 
311     int keyCode = event.GetKeyCode();
312 
313     // we don't filter special keys and delete
314     if (keyCode < WXK_SPACE || keyCode == WXK_DELETE || keyCode >= WXK_START)
315     {
316         event.Skip();
317         return;
318     }
319 
320     wxString str((wxUniChar)keyCode, 1);
321     if (!IsValid(str).empty())
322     {
323         if ( !wxValidator::IsSilent() )
324             wxBell();
325 
326         // eat message
327         return;
328     }
329     else
330         event.Skip();
331 }
332 
333 
334 #endif
335   // wxUSE_VALIDATORS && (wxUSE_TEXTCTRL || wxUSE_COMBOBOX)
336