KiCad PCB EDA Suite
Loading...
Searching...
No Matches
validators.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013 Wayne Stambaugh <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Copyright (C) 2018 CERN
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
26
27#include <string_utils.h>
28#include <confirm.h>
29#include <validators.h>
30#include <template_fieldnames.h>
31
32#include <wx/grid.h>
33#include <wx/textctrl.h>
34#include <wx/textentry.h>
35#include <wx/log.h>
36#include <wx/combo.h>
37#include <wx/msgdlg.h>
38#include <refdes_utils.h>
39
40
42 wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, aValue )
43{
44 // This list of characters follows the string from footprint.cpp which, in turn mimics the
45 // strings from lib_id.cpp
46 // TODO: Unify forbidden character lists
47 wxString illegalChars = wxS( "%$<>\t\n\r\"\\/:" );
48 SetCharExcludes( illegalChars );
49 }
50
51
53 wxTextValidator()
54{
55 Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
56}
57
58
60 : wxTextValidator()
61{
62 wxValidator::Copy( val );
63
64 Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
65}
66
67
69{
70 Disconnect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
71}
72
73
74void ENV_VAR_NAME_VALIDATOR::OnChar( wxKeyEvent& aEvent )
75{
76 if( !m_validatorWindow )
77 {
78 aEvent.Skip();
79 return;
80 }
81
82 int keyCode = aEvent.GetKeyCode();
83
84 // we don't filter special keys and delete
85 if( keyCode < WXK_SPACE || keyCode == WXK_DELETE || keyCode >= WXK_START )
86 {
87 aEvent.Skip();
88 return;
89 }
90
91 wxUniChar c = (wxUChar) keyCode;
92
93 if( c == wxT( '_' ) )
94 {
95 // OK anywhere
96 aEvent.Skip();
97 }
98 else if( wxIsdigit( c ) )
99 {
100 // not as first character
101 long from, to;
102 GetTextEntry()->GetSelection( &from, &to );
103
104 if( from < 1 )
105 wxBell();
106 else
107 aEvent.Skip();
108 }
109 else if( wxIsalpha( c ) )
110 {
111 // Capitals only.
112
113 if( wxIslower( c ) )
114 {
115 // You may wonder why this scope is so twisted, so make yourself comfortable and read:
116 // 1. Changing the keyCode and/or uniChar in the event and passing it on
117 // doesn't work. Some platforms look at the original copy as long as the event
118 // isn't vetoed.
119 // 2. Inserting characters by hand does not move the cursor, meaning either you insert
120 // text backwards (lp:#1798869) or always append, no matter where is the cursor.
121 // wxTextEntry::{Get/Set}InsertionPoint() do not work at all here.
122 // 3. There is wxTextEntry::ForceUpper(), but it is not yet available in common
123 // wxWidgets packages.
124 //
125 // So here we are, with a command event handler that converts
126 // the text to upper case upon every change.
127 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( GetTextEntry() );
128
129 if( textCtrl )
130 {
131 textCtrl->Connect( textCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED,
132 wxCommandEventHandler( ENV_VAR_NAME_VALIDATOR::OnTextChanged ) );
133 }
134 }
135
136 aEvent.Skip();
137 }
138 else
139 {
140 wxBell();
141 }
142}
143
144
145void ENV_VAR_NAME_VALIDATOR::OnTextChanged( wxCommandEvent& event )
146{
147 wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
148
149 if( textCtrl )
150 {
151 if( !textCtrl->IsModified() )
152 return;
153
154 long insertionPoint = textCtrl->GetInsertionPoint();
155 textCtrl->ChangeValue( textCtrl->GetValue().Upper() );
156 textCtrl->SetInsertionPoint( insertionPoint );
157 textCtrl->Disconnect( textCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED );
158 }
159
160 event.Skip();
161}
162
163
165 wxTextValidator(),
166 m_allowSpaces( true )
167{
168}
169
170
172 wxTextValidator( aValidator ),
173 m_allowSpaces( aValidator.m_allowSpaces )
174{
175}
176
177
179 wxTextValidator(),
180 m_allowSpaces( aAllowSpaces )
181{
182}
183
184
185bool NETNAME_VALIDATOR::Validate( wxWindow *aParent )
186{
187 // If window is disabled, simply return
188 if ( !m_validatorWindow->IsEnabled() )
189 return true;
190
191 wxTextEntry * const text = GetTextEntry();
192
193 if ( !text )
194 return false;
195
196 const wxString& errormsg = IsValid( text->GetValue() );
197
198 if( !errormsg.empty() )
199 {
200 m_validatorWindow->SetFocus();
201 wxMessageBox( errormsg, _( "Invalid signal name" ), wxOK | wxICON_EXCLAMATION, aParent );
202 return false;
203 }
204
205 return true;
206}
207
208
209wxString NETNAME_VALIDATOR::IsValid( const wxString& str ) const
210{
211 if( str.Contains( '\r' ) || str.Contains( '\n' ) )
212 return _( "Signal names cannot contain CR or LF characters" );
213
214 if( !m_allowSpaces && ( str.Contains( ' ' ) || str.Contains( '\t' ) ) )
215 return _( "Signal names cannot contain spaces" );
216
217 return wxString();
218}
219
220
221void KIUI::ValidatorTransferToWindowWithoutEvents( wxValidator& aValidator )
222{
223 wxWindow* ctrl = aValidator.GetWindow();
224
225 wxCHECK_RET( ctrl != nullptr, wxS( "Transferring validator data without a control" ) );
226
227 wxEventBlocker orient_update_blocker( ctrl, wxEVT_ANY );
228 aValidator.TransferToWindow();
229}
230
231
232FIELD_VALIDATOR::FIELD_VALIDATOR( FIELD_T aFieldId, wxString* aValue ) :
233 wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, aValue ),
234 m_fieldId( aFieldId )
235{
236 // Fields cannot contain carriage returns, line feeds, or tabs.
237 wxString excludes( wxT( "\r\n\t" ) );
238
239 // The reference and sheet name fields cannot contain spaces.
240 if( aFieldId == FIELD_T::REFERENCE )
241 {
242 excludes += wxT( " " );
243 }
244 else if( m_fieldId == FIELD_T::SHEET_NAME )
245 {
246 excludes += wxT( "/" );
247 }
248
249 long style = GetStyle();
250
251 // The reference, sheetname and sheetfilename fields cannot be empty.
252 if( aFieldId == FIELD_T::REFERENCE
253 || aFieldId == FIELD_T::SHEET_NAME
254 || aFieldId == FIELD_T::SHEET_FILENAME )
255 {
256 style |= wxFILTER_EMPTY;
257 }
258
259 SetStyle( style );
260 SetCharExcludes( excludes );
261}
262
263
265 wxTextValidator( aValidator ),
266 m_fieldId( aValidator.m_fieldId )
267{
268}
269
270
271bool FIELD_VALIDATOR::Validate( wxWindow* aParent )
272{
273 // If window is disabled, simply return
274 if( !m_validatorWindow->IsEnabled() )
275 return true;
276
277 wxTextEntry* const text = GetTextEntry();
278
279 if( !text )
280 return false;
281
282 wxString val( text->GetValue() );
283
284 return DoValidate( val, aParent );
285}
286
287
288wxString GetFieldValidationErrorMessage( FIELD_T aFieldId, const wxString& aValue )
289{
290 FIELD_VALIDATOR validator( aFieldId );
291 wxString msg;
292
293 if( validator.HasFlag( wxFILTER_EMPTY ) && aValue.empty() )
294 {
295 switch( aFieldId )
296 {
297 case FIELD_T::SHEET_NAME: msg = _( "A sheet must have a name." ); break;
298 case FIELD_T::SHEET_FILENAME: msg = _( "A sheet must have a file specified." ); break;
299 default: msg = _( "The value of the field cannot be empty." ); break;
300 }
301 }
302
303 if( msg.empty() && validator.HasFlag( wxFILTER_EXCLUDE_CHAR_LIST ) )
304 {
305 wxArrayString badCharsFound;
306
307 for( const wxUniCharRef& excludeChar : validator.GetCharExcludes() )
308 {
309 if( aValue.Find( excludeChar ) != wxNOT_FOUND )
310 {
311 if( excludeChar == '\r' )
312 badCharsFound.Add( _( "carriage return" ) );
313 else if( excludeChar == '\n' )
314 badCharsFound.Add( _( "line feed" ) );
315 else if( excludeChar == '\t' )
316 badCharsFound.Add( _( "tab" ) );
317 else if( excludeChar == ' ' )
318 badCharsFound.Add( _( "space" ) );
319 else
320 badCharsFound.Add( wxString::Format( wxT( "'%c'" ), excludeChar ) );
321 }
322 }
323
324 if( !badCharsFound.IsEmpty() )
325 {
326 wxString badChars;
327
328 for( size_t i = 0; i < badCharsFound.GetCount(); i++ )
329 {
330 if( !badChars.IsEmpty() )
331 {
332 if( badCharsFound.GetCount() == 2 )
333 {
334 badChars += _( " or " );
335 }
336 else
337 {
338 if( i < badCharsFound.GetCount() - 2 )
339 badChars += _( ", or " );
340 else
341 badChars += wxT( ", " );
342 }
343 }
344
345 badChars += badCharsFound.Item( i );
346 }
347
348 switch( aFieldId )
349 {
351 msg.Printf( _( "The reference designator cannot contain %s character(s)." ), badChars );
352 break;
353
354 case FIELD_T::VALUE:
355 msg.Printf( _( "The value field cannot contain %s character(s)." ), badChars );
356 break;
357
359 msg.Printf( _( "The footprint field cannot contain %s character(s)." ), badChars );
360 break;
361
363 msg.Printf( _( "The datasheet field cannot contain %s character(s)." ), badChars );
364 break;
365
367 msg.Printf( _( "The sheet name cannot contain %s character(s)." ), badChars );
368 break;
369
371 msg.Printf( _( "The sheet filename cannot contain %s character(s)." ), badChars );
372 break;
373
374 default:
375 msg.Printf( _( "The field cannot contain %s character(s)." ), badChars );
376 break;
377 };
378 }
379 }
380
381 if( msg.empty() )
382 {
383 if( aFieldId == FIELD_T::REFERENCE && aValue.Contains( wxT( "${" ) ) )
384 {
385 msg.Printf( _( "The reference designator cannot contain text variable references" ) );
386 }
387 else if( aFieldId == FIELD_T::REFERENCE && UTIL::GetRefDesPrefix( aValue ).IsEmpty() )
388 {
389 msg.Printf( _( "References must start with a letter." ) );
390 }
391 }
392
393 return msg;
394}
395
396
397bool FIELD_VALIDATOR::DoValidate( const wxString& aValue, wxWindow* aParent )
398{
399 wxString msg = GetFieldValidationErrorMessage( m_fieldId, aValue );
400
401 if( !msg.empty() )
402 {
403 if( m_validatorWindow )
404 m_validatorWindow->SetFocus();
405
406 wxMessageBox( msg, _( "Field Validation Error" ), wxOK | wxICON_EXCLAMATION, aParent );
407
408 return false;
409 }
410
411 return true;
412}
virtual ~ENV_VAR_NAME_VALIDATOR()
void OnChar(wxKeyEvent &event)
ENV_VAR_NAME_VALIDATOR(wxString *aValue=nullptr)
void OnTextChanged(wxCommandEvent &event)
A text control validator used for validating the text allowed in fields.
Definition validators.h:138
virtual bool Validate(wxWindow *aParent) override
Override the default Validate() function provided by wxTextValidator to provide better error messages...
bool DoValidate(const wxString &aValue, wxWindow *aParent)
FIELD_VALIDATOR(FIELD_T aFieldId, wxString *aValue=nullptr)
FIELD_T m_fieldId
Definition validators.h:158
FOOTPRINT_NAME_VALIDATOR(wxString *aValue=nullptr)
wxString IsValid(const wxString &aVal) const override
virtual bool Validate(wxWindow *aParent) override
NETNAME_VALIDATOR(wxString *aVal=nullptr)
This file is part of the common library.
#define _(s)
void ValidatorTransferToWindowWithoutEvents(wxValidator &aValidator)
Call a text validator's TransferDataToWindow method without firing a text change event.
wxString GetRefDesPrefix(const wxString &aRefDes)
Get the (non-numeric) prefix from a refdes - e.g.
Collection of utility functions for component reference designators (refdes)
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
wxString GetFieldValidationErrorMessage(FIELD_T aFieldId, const wxString &aValue)
Return the error message if aValue is invalid for aFieldId.
Custom text control validator definitions.
wxString GetFieldValidationErrorMessage(FIELD_T aFieldId, const wxString &aValue)
Return the error message if aValue is invalid for aFieldId.