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.

400 lines
11 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 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, 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. ENV_VAR_NAME_VALIDATOR::ENV_VAR_NAME_VALIDATOR( wxString* aValue ) :
  50. wxTextValidator()
  51. {
  52. Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
  53. }
  54. ENV_VAR_NAME_VALIDATOR::ENV_VAR_NAME_VALIDATOR( const ENV_VAR_NAME_VALIDATOR& val )
  55. : wxTextValidator()
  56. {
  57. wxValidator::Copy( val );
  58. Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
  59. }
  60. ENV_VAR_NAME_VALIDATOR::~ENV_VAR_NAME_VALIDATOR()
  61. {
  62. Disconnect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
  63. }
  64. void ENV_VAR_NAME_VALIDATOR::OnChar( wxKeyEvent& aEvent )
  65. {
  66. if( !m_validatorWindow )
  67. {
  68. aEvent.Skip();
  69. return;
  70. }
  71. int keyCode = aEvent.GetKeyCode();
  72. // we don't filter special keys and delete
  73. if( keyCode < WXK_SPACE || keyCode == WXK_DELETE || keyCode >= WXK_START )
  74. {
  75. aEvent.Skip();
  76. return;
  77. }
  78. wxUniChar c = (wxUChar) keyCode;
  79. if( c == wxT( '_' ) )
  80. {
  81. // OK anywhere
  82. aEvent.Skip();
  83. }
  84. else if( wxIsdigit( c ) )
  85. {
  86. // not as first character
  87. long from, to;
  88. GetTextEntry()->GetSelection( &from, &to );
  89. if( from < 1 )
  90. wxBell();
  91. else
  92. aEvent.Skip();
  93. }
  94. else if( wxIsalpha( c ) )
  95. {
  96. // Capitals only.
  97. if( wxIslower( c ) )
  98. {
  99. // You may wonder why this scope is so twisted, so make yourself comfortable and read:
  100. // 1. Changing the keyCode and/or uniChar in the event and passing it on
  101. // doesn't work. Some platforms look at the original copy as long as the event
  102. // isn't vetoed.
  103. // 2. Inserting characters by hand does not move the cursor, meaning either you insert
  104. // text backwards (lp:#1798869) or always append, no matter where is the cursor.
  105. // wxTextEntry::{Get/Set}InsertionPoint() do not work at all here.
  106. // 3. There is wxTextEntry::ForceUpper(), but it is not yet available in common
  107. // wxWidgets packages.
  108. //
  109. // So here we are, with a command event handler that converts
  110. // the text to upper case upon every change.
  111. wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( GetTextEntry() );
  112. if( textCtrl )
  113. {
  114. textCtrl->Connect( textCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED,
  115. wxCommandEventHandler( ENV_VAR_NAME_VALIDATOR::OnTextChanged ) );
  116. }
  117. }
  118. aEvent.Skip();
  119. }
  120. else
  121. {
  122. wxBell();
  123. }
  124. }
  125. void ENV_VAR_NAME_VALIDATOR::OnTextChanged( wxCommandEvent& event )
  126. {
  127. wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( event.GetEventObject() );
  128. if( textCtrl )
  129. {
  130. if( !textCtrl->IsModified() )
  131. return;
  132. long insertionPoint = textCtrl->GetInsertionPoint();
  133. textCtrl->ChangeValue( textCtrl->GetValue().Upper() );
  134. textCtrl->SetInsertionPoint( insertionPoint );
  135. textCtrl->Disconnect( textCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED );
  136. }
  137. event.Skip();
  138. }
  139. NETNAME_VALIDATOR::NETNAME_VALIDATOR( wxString *aVal ) :
  140. wxTextValidator(),
  141. m_allowSpaces( false )
  142. {
  143. }
  144. NETNAME_VALIDATOR::NETNAME_VALIDATOR( const NETNAME_VALIDATOR& aValidator ) :
  145. wxTextValidator( aValidator ),
  146. m_allowSpaces( aValidator.m_allowSpaces )
  147. {
  148. }
  149. NETNAME_VALIDATOR::NETNAME_VALIDATOR( bool aAllowSpaces ) :
  150. wxTextValidator(),
  151. m_allowSpaces( aAllowSpaces )
  152. {
  153. }
  154. bool NETNAME_VALIDATOR::Validate( wxWindow *aParent )
  155. {
  156. // If window is disabled, simply return
  157. if ( !m_validatorWindow->IsEnabled() )
  158. return true;
  159. wxTextEntry * const text = GetTextEntry();
  160. if ( !text )
  161. return false;
  162. const wxString& errormsg = IsValid( text->GetValue() );
  163. if( !errormsg.empty() )
  164. {
  165. m_validatorWindow->SetFocus();
  166. wxMessageBox( errormsg, _( "Invalid signal name" ), wxOK | wxICON_EXCLAMATION, aParent );
  167. return false;
  168. }
  169. return true;
  170. }
  171. wxString NETNAME_VALIDATOR::IsValid( const wxString& str ) const
  172. {
  173. if( str.Contains( '\r' ) || str.Contains( '\n' ) )
  174. return _( "Signal names cannot contain CR or LF characters" );
  175. if( !m_allowSpaces && ( str.Contains( ' ' ) || str.Contains( '\t' ) ) )
  176. return _( "Signal names cannot contain spaces" );
  177. return wxString();
  178. }
  179. void KIUI::ValidatorTransferToWindowWithoutEvents( wxValidator& aValidator )
  180. {
  181. wxWindow* ctrl = aValidator.GetWindow();
  182. wxCHECK_RET( ctrl != nullptr, wxS( "Transferring validator data without a control" ) );
  183. wxEventBlocker orient_update_blocker( ctrl, wxEVT_ANY );
  184. aValidator.TransferToWindow();
  185. }
  186. FIELD_VALIDATOR::FIELD_VALIDATOR( FIELD_T aFieldId, wxString* aValue ) :
  187. wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, aValue ),
  188. m_fieldId( aFieldId )
  189. {
  190. // Fields cannot contain carriage returns, line feeds, or tabs.
  191. wxString excludes( wxT( "\r\n\t" ) );
  192. // The reference and sheet name fields cannot contain spaces.
  193. if( aFieldId == FIELD_T::REFERENCE )
  194. {
  195. excludes += wxT( " " );
  196. }
  197. else if( m_fieldId == FIELD_T::SHEET_NAME )
  198. {
  199. excludes += wxT( "/" );
  200. }
  201. long style = GetStyle();
  202. // The reference, sheetname and sheetfilename fields cannot be empty.
  203. if( aFieldId == FIELD_T::REFERENCE
  204. || aFieldId == FIELD_T::SHEET_NAME
  205. || aFieldId == FIELD_T::SHEET_FILENAME )
  206. {
  207. style |= wxFILTER_EMPTY;
  208. }
  209. SetStyle( style );
  210. SetCharExcludes( excludes );
  211. }
  212. FIELD_VALIDATOR::FIELD_VALIDATOR( const FIELD_VALIDATOR& aValidator ) :
  213. wxTextValidator( aValidator ),
  214. m_fieldId( aValidator.m_fieldId )
  215. {
  216. }
  217. bool FIELD_VALIDATOR::Validate( wxWindow* aParent )
  218. {
  219. // If window is disabled, simply return
  220. if( !m_validatorWindow->IsEnabled() )
  221. return true;
  222. wxTextEntry* const text = GetTextEntry();
  223. if( !text )
  224. return false;
  225. wxString val( text->GetValue() );
  226. return DoValidate( val, aParent );
  227. }
  228. bool FIELD_VALIDATOR::DoValidate( const wxString& aValue, wxWindow* aParent )
  229. {
  230. wxString msg;
  231. if( HasFlag( wxFILTER_EMPTY ) && aValue.empty() )
  232. {
  233. switch( m_fieldId )
  234. {
  235. case FIELD_T::SHEET_NAME: msg = _( "A sheet must have a name." ); break;
  236. case FIELD_T::SHEET_FILENAME: msg = _( "A sheet must have a file specified." ); break;
  237. default: msg = _( "The value of the field cannot be empty." ); break;
  238. }
  239. }
  240. if( HasFlag( wxFILTER_EXCLUDE_CHAR_LIST ) && ContainsExcludedCharacters( aValue ) )
  241. {
  242. wxArrayString badCharsFound;
  243. for( const wxUniCharRef& excludeChar : GetCharExcludes() )
  244. {
  245. if( aValue.Find( excludeChar ) != wxNOT_FOUND )
  246. {
  247. if( excludeChar == '\r' )
  248. badCharsFound.Add( _( "carriage return" ) );
  249. else if( excludeChar == '\n' )
  250. badCharsFound.Add( _( "line feed" ) );
  251. else if( excludeChar == '\t' )
  252. badCharsFound.Add( _( "tab" ) );
  253. else if( excludeChar == ' ' )
  254. badCharsFound.Add( _( "space" ) );
  255. else
  256. badCharsFound.Add( wxString::Format( wxT( "'%c'" ), excludeChar ) );
  257. }
  258. }
  259. wxString badChars;
  260. for( size_t i = 0; i < badCharsFound.GetCount(); i++ )
  261. {
  262. if( !badChars.IsEmpty() )
  263. {
  264. if( badCharsFound.GetCount() == 2 )
  265. {
  266. badChars += _( " or " );
  267. }
  268. else
  269. {
  270. if( i < badCharsFound.GetCount() - 2 )
  271. badChars += _( ", or " );
  272. else
  273. badChars += wxT( ", " );
  274. }
  275. }
  276. badChars += badCharsFound.Item( i );
  277. }
  278. switch( m_fieldId )
  279. {
  280. case FIELD_T::REFERENCE:
  281. msg.Printf( _( "The reference designator cannot contain %s character(s)." ), badChars );
  282. break;
  283. case FIELD_T::VALUE:
  284. msg.Printf( _( "The value field cannot contain %s character(s)." ), badChars );
  285. break;
  286. case FIELD_T::FOOTPRINT:
  287. msg.Printf( _( "The footprint field cannot contain %s character(s)." ), badChars );
  288. break;
  289. case FIELD_T::DATASHEET:
  290. msg.Printf( _( "The datasheet field cannot contain %s character(s)." ), badChars );
  291. break;
  292. case FIELD_T::SHEET_NAME:
  293. msg.Printf( _( "The sheet name cannot contain %s character(s)." ), badChars );
  294. break;
  295. case FIELD_T::SHEET_FILENAME:
  296. msg.Printf( _( "The sheet filename cannot contain %s character(s)." ), badChars );
  297. break;
  298. default:
  299. msg.Printf( _( "The field cannot contain %s character(s)." ), badChars );
  300. break;
  301. };
  302. }
  303. else if( m_fieldId == FIELD_T::REFERENCE && aValue.Contains( wxT( "${" ) ) )
  304. {
  305. msg.Printf( _( "The reference designator cannot contain text variable references" ) );
  306. }
  307. else if( m_fieldId == FIELD_T::REFERENCE && UTIL::GetRefDesPrefix( aValue ).IsEmpty() )
  308. {
  309. msg.Printf( _( "References must start with a letter." ) );
  310. }
  311. if( !msg.empty() )
  312. {
  313. if( m_validatorWindow )
  314. m_validatorWindow->SetFocus();
  315. wxMessageBox( msg, _( "Field Validation Error" ), wxOK | wxICON_EXCLAMATION, aParent );
  316. return false;
  317. }
  318. return true;
  319. }