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.

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