You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

481 lines
13 KiB

3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
  5. * Copyright (C) 2004-2019 KiCad Developers, see change_log.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, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file validators.cpp
  27. * @brief Custom text control validator implementations.
  28. */
  29. #include <string_utils.h>
  30. #include <confirm.h>
  31. #include <validators.h>
  32. #include <template_fieldnames.h>
  33. #include <wx/grid.h>
  34. #include <wx/textctrl.h>
  35. #include <wx/textentry.h>
  36. #include <wx/log.h>
  37. #include <wx/combo.h>
  38. GRID_CELL_TEXT_EDITOR::GRID_CELL_TEXT_EDITOR() : wxGridCellTextEditor()
  39. {
  40. }
  41. void GRID_CELL_TEXT_EDITOR::SetValidator( const wxValidator& validator )
  42. {
  43. // keep our own copy because wxGridCellTextEditor's is annoyingly private
  44. m_validator.reset( static_cast<wxValidator*>( validator.Clone() ) );
  45. wxGridCellTextEditor::SetValidator( *m_validator );
  46. }
  47. void GRID_CELL_TEXT_EDITOR::StartingKey( wxKeyEvent& event )
  48. {
  49. if( m_validator )
  50. {
  51. m_validator.get()->SetWindow( Text() );
  52. m_validator.get()->ProcessEvent( event );
  53. }
  54. if( event.GetSkipped() )
  55. {
  56. wxGridCellTextEditor::StartingKey( event );
  57. event.Skip( false );
  58. }
  59. }
  60. FOOTPRINT_NAME_VALIDATOR::FOOTPRINT_NAME_VALIDATOR( wxString* aValue ) :
  61. wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, aValue )
  62. {
  63. // This list of characters follows the string from footprint.cpp which, in turn mimics the
  64. // strings from lib_id.cpp
  65. // TODO: Unify forbidden character lists
  66. wxString illegalChars = wxS( "%$<>\t\n\r\"\\/:" );
  67. SetCharExcludes( illegalChars );
  68. }
  69. FILE_NAME_WITH_PATH_CHAR_VALIDATOR::FILE_NAME_WITH_PATH_CHAR_VALIDATOR( wxString* aValue ) :
  70. wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST | wxFILTER_EMPTY, aValue )
  71. {
  72. // The Windows (DOS) file system forbidden characters already include the forbidden
  73. // file name characters for both Posix and OSX systems. The characters *?|"<> are
  74. // illegal and filtered by the validator, but /\: are valid (\ and : only on Windows.
  75. wxString illegalChars = wxFileName::GetForbiddenChars( wxPATH_DOS );
  76. wxTextValidator nameValidator( wxFILTER_EXCLUDE_CHAR_LIST );
  77. wxArrayString illegalCharList;
  78. for( unsigned i = 0; i < illegalChars.size(); i++ )
  79. {
  80. if( illegalChars[i] == '/' )
  81. continue;
  82. #if defined (__WINDOWS__)
  83. if( illegalChars[i] == '\\' || illegalChars[i] == ':' )
  84. continue;
  85. #endif
  86. illegalCharList.Add( wxString( illegalChars[i] ) );
  87. }
  88. SetExcludes( illegalCharList );
  89. }
  90. ENV_VAR_NAME_VALIDATOR::ENV_VAR_NAME_VALIDATOR( wxString* aValue ) :
  91. wxTextValidator()
  92. {
  93. Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
  94. }
  95. ENV_VAR_NAME_VALIDATOR::ENV_VAR_NAME_VALIDATOR( const ENV_VAR_NAME_VALIDATOR& val )
  96. : wxTextValidator()
  97. {
  98. wxValidator::Copy( val );
  99. Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
  100. }
  101. ENV_VAR_NAME_VALIDATOR::~ENV_VAR_NAME_VALIDATOR()
  102. {
  103. Disconnect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
  104. }
  105. void ENV_VAR_NAME_VALIDATOR::OnChar( wxKeyEvent& aEvent )
  106. {
  107. if( !m_validatorWindow )
  108. {
  109. aEvent.Skip();
  110. return;
  111. }
  112. int keyCode = aEvent.GetKeyCode();
  113. // we don't filter special keys and delete
  114. if( keyCode < WXK_SPACE || keyCode == WXK_DELETE || keyCode >= WXK_START )
  115. {
  116. aEvent.Skip();
  117. return;
  118. }
  119. wxUniChar c = (wxUChar) keyCode;
  120. if( c == wxT( '_' ) )
  121. {
  122. // OK anywhere
  123. aEvent.Skip();
  124. }
  125. else if( wxIsdigit( c ) )
  126. {
  127. // not as first character
  128. long from, to;
  129. GetTextEntry()->GetSelection( &from, &to );
  130. if( from < 1 )
  131. wxBell();
  132. else
  133. aEvent.Skip();
  134. }
  135. else if( wxIsalpha( c ) )
  136. {
  137. // Capitals only.
  138. if( wxIslower( c ) )
  139. {
  140. // You may wonder why this scope is so twisted, so make yourself comfortable and read:
  141. // 1. Changing the keyCode and/or uniChar in the event and passing it on
  142. // doesn't work. Some platforms look at the original copy as long as the event
  143. // isn't vetoed.
  144. // 2. Inserting characters by hand does not move the cursor, meaning either you insert
  145. // text backwards (lp:#1798869) or always append, no matter where is the cursor.
  146. // wxTextEntry::{Get/Set}InsertionPoint() do not work at all here.
  147. // 3. There is wxTextEntry::ForceUpper(), but it is not yet available in common
  148. // wxWidgets packages.
  149. //
  150. // So here we are, with a command event handler that converts
  151. // the text to upper case upon every change.
  152. wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( GetTextEntry() );
  153. if( textCtrl )
  154. {
  155. textCtrl->Connect( textCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED,
  156. wxCommandEventHandler( ENV_VAR_NAME_VALIDATOR::OnTextChanged ) );
  157. }
  158. }
  159. aEvent.Skip();
  160. }
  161. else
  162. {
  163. wxBell();
  164. }
  165. }
  166. void ENV_VAR_NAME_VALIDATOR::OnTextChanged( wxCommandEvent& event )
  167. {
  168. wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
  169. if( textCtrl )
  170. {
  171. if( !textCtrl->IsModified() )
  172. return;
  173. long insertionPoint = textCtrl->GetInsertionPoint();
  174. textCtrl->ChangeValue( textCtrl->GetValue().Upper() );
  175. textCtrl->SetInsertionPoint( insertionPoint );
  176. textCtrl->Disconnect( textCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED );
  177. }
  178. event.Skip();
  179. }
  180. bool REGEX_VALIDATOR::Validate( wxWindow* aParent )
  181. {
  182. // If window is disabled, simply return
  183. if( !m_validatorWindow->IsEnabled() )
  184. return true;
  185. wxTextEntry* const textEntry = GetTextEntry();
  186. if( !textEntry )
  187. return false;
  188. bool valid = true;
  189. const wxString& value = textEntry->GetValue();
  190. if( m_regEx.Matches( value ) )
  191. {
  192. size_t start, len;
  193. m_regEx.GetMatch( &start, &len );
  194. if( start != 0 || len != value.Length() ) // whole string must match
  195. valid = false;
  196. }
  197. else // no match at all
  198. {
  199. valid = false;
  200. }
  201. if( !valid )
  202. {
  203. m_validatorWindow->SetFocus();
  204. DisplayError( aParent, wxString::Format( _( "Incorrect value: %s" ), value ) );
  205. return false;
  206. }
  207. return true;
  208. }
  209. void REGEX_VALIDATOR::compileRegEx( const wxString& aRegEx, int aFlags )
  210. {
  211. if( !m_regEx.Compile( aRegEx, aFlags ) )
  212. {
  213. throw std::runtime_error( "REGEX_VALIDATOR: Invalid regular expression: "
  214. + aRegEx.ToStdString() );
  215. }
  216. m_regExString = aRegEx;
  217. m_regExFlags = aFlags;
  218. }
  219. NETNAME_VALIDATOR::NETNAME_VALIDATOR( wxString *aVal ) :
  220. wxTextValidator(),
  221. m_allowSpaces( false )
  222. {
  223. }
  224. NETNAME_VALIDATOR::NETNAME_VALIDATOR( const NETNAME_VALIDATOR& aValidator ) :
  225. wxTextValidator( aValidator ),
  226. m_allowSpaces( aValidator.m_allowSpaces )
  227. {
  228. }
  229. NETNAME_VALIDATOR::NETNAME_VALIDATOR( bool aAllowSpaces ) :
  230. wxTextValidator(),
  231. m_allowSpaces( aAllowSpaces )
  232. {
  233. }
  234. bool NETNAME_VALIDATOR::Validate( wxWindow *aParent )
  235. {
  236. // If window is disabled, simply return
  237. if ( !m_validatorWindow->IsEnabled() )
  238. return true;
  239. wxTextEntry * const text = GetTextEntry();
  240. if ( !text )
  241. return false;
  242. const wxString& errormsg = IsValid( text->GetValue() );
  243. if( !errormsg.empty() )
  244. {
  245. m_validatorWindow->SetFocus();
  246. wxMessageBox( errormsg, _( "Invalid signal name" ), wxOK | wxICON_EXCLAMATION, aParent );
  247. return false;
  248. }
  249. return true;
  250. }
  251. wxString NETNAME_VALIDATOR::IsValid( const wxString& str ) const
  252. {
  253. if( str.Contains( '\r' ) || str.Contains( '\n' ) )
  254. return _( "Signal names cannot contain CR or LF characters" );
  255. if( !m_allowSpaces && ( str.Contains( ' ' ) || str.Contains( '\t' ) ) )
  256. return _( "Signal names cannot contain spaces" );
  257. return wxString();
  258. }
  259. void KIUI::ValidatorTransferToWindowWithoutEvents( wxValidator& aValidator )
  260. {
  261. wxWindow* ctrl = aValidator.GetWindow();
  262. wxCHECK_RET( ctrl != nullptr, wxS( "Transferring validator data without a control" ) );
  263. wxEventBlocker orient_update_blocker( ctrl, wxEVT_ANY );
  264. aValidator.TransferToWindow();
  265. }
  266. FIELD_VALIDATOR::FIELD_VALIDATOR( int aFieldId, wxString* aValue ) :
  267. wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, aValue ), m_fieldId( aFieldId )
  268. {
  269. // Fields cannot contain carriage returns, line feeds, or tabs.
  270. wxString excludes( wxT( "\r\n\t" ) );
  271. // The reference and sheet name fields cannot contain spaces.
  272. if( aFieldId == REFERENCE_FIELD )
  273. {
  274. excludes += wxT( " " );
  275. }
  276. else if( m_fieldId == SHEETNAME_V )
  277. {
  278. excludes += wxT( "/" );
  279. }
  280. long style = GetStyle();
  281. // The reference, sheetname and sheetfilename fields cannot be empty.
  282. if( aFieldId == REFERENCE_FIELD || aFieldId == SHEETNAME_V || aFieldId == SHEETFILENAME_V )
  283. {
  284. style |= wxFILTER_EMPTY;
  285. }
  286. SetStyle( style );
  287. SetCharExcludes( excludes );
  288. }
  289. FIELD_VALIDATOR::FIELD_VALIDATOR( const FIELD_VALIDATOR& aValidator ) :
  290. wxTextValidator( aValidator ), m_fieldId( aValidator.m_fieldId )
  291. {
  292. }
  293. bool FIELD_VALIDATOR::Validate( wxWindow* aParent )
  294. {
  295. // If window is disabled, simply return
  296. if( !m_validatorWindow->IsEnabled() )
  297. return true;
  298. wxTextEntry* const text = GetTextEntry();
  299. if( !text )
  300. return false;
  301. wxString val( text->GetValue() );
  302. wxString msg;
  303. if( HasFlag( wxFILTER_EMPTY ) && val.empty() )
  304. msg.Printf( _( "The value of the field cannot be empty." ) );
  305. if( HasFlag( wxFILTER_EXCLUDE_CHAR_LIST ) && ContainsExcludedCharacters( val ) )
  306. {
  307. wxArrayString badCharsFound;
  308. for( const wxUniCharRef& excludeChar : GetCharExcludes() )
  309. {
  310. if( val.Find( excludeChar ) != wxNOT_FOUND )
  311. {
  312. if( excludeChar == '\r' )
  313. badCharsFound.Add( _( "carriage return" ) );
  314. else if( excludeChar == '\n' )
  315. badCharsFound.Add( _( "line feed" ) );
  316. else if( excludeChar == '\t' )
  317. badCharsFound.Add( _( "tab" ) );
  318. else if( excludeChar == ' ' )
  319. badCharsFound.Add( _( "space" ) );
  320. else
  321. badCharsFound.Add( wxString::Format( wxT( "'%c'" ), excludeChar ) );
  322. }
  323. }
  324. wxString badChars;
  325. for( size_t i = 0; i < badCharsFound.GetCount(); i++ )
  326. {
  327. if( !badChars.IsEmpty() )
  328. {
  329. if( badCharsFound.GetCount() == 2 )
  330. {
  331. badChars += _( " or " );
  332. }
  333. else
  334. {
  335. if( i < badCharsFound.GetCount() - 2 )
  336. badChars += _( ", or " );
  337. else
  338. badChars += wxT( ", " );
  339. }
  340. }
  341. badChars += badCharsFound.Item( i );
  342. }
  343. switch( m_fieldId )
  344. {
  345. case REFERENCE_FIELD:
  346. msg.Printf( _( "The reference designator cannot contain %s character(s)." ), badChars );
  347. break;
  348. case VALUE_FIELD:
  349. msg.Printf( _( "The value field cannot contain %s character(s)." ), badChars );
  350. break;
  351. case FOOTPRINT_FIELD:
  352. msg.Printf( _( "The footprint field cannot contain %s character(s)." ), badChars );
  353. break;
  354. case DATASHEET_FIELD:
  355. msg.Printf( _( "The datasheet field cannot contain %s character(s)." ), badChars );
  356. break;
  357. case SHEETNAME_V:
  358. msg.Printf( _( "The sheet name cannot contain %s character(s)." ), badChars );
  359. break;
  360. case SHEETFILENAME_V:
  361. msg.Printf( _( "The sheet filename cannot contain %s character(s)." ), badChars );
  362. break;
  363. default:
  364. msg.Printf( _( "The field cannot contain %s character(s)." ), badChars );
  365. break;
  366. };
  367. }
  368. else if( m_fieldId == REFERENCE_FIELD && val.Contains( wxT( "${" ) ) )
  369. {
  370. msg.Printf( _( "The reference designator cannot contain text variable references" ) );
  371. }
  372. if( !msg.empty() )
  373. {
  374. m_validatorWindow->SetFocus();
  375. wxMessageBox( msg, _( "Field Validation Error" ), wxOK | wxICON_EXCLAMATION, aParent );
  376. return false;
  377. }
  378. return true;
  379. }