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.

339 lines
9.7 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include "template_fieldnames.h"
  25. #include <mutex>
  26. #include <template_fieldnames_lexer.h>
  27. #include <string_utils.h>
  28. using namespace TFIELD_T;
  29. // N.B. Do not change these values without transitioning the file format
  30. #define REFERENCE_CANONICAL "Reference"
  31. #define VALUE_CANONICAL "Value"
  32. #define FOOTPRINT_CANONICAL "Footprint"
  33. #define DATASHEET_CANONICAL "Datasheet"
  34. #define DESCRIPTION_CANONICAL "Description"
  35. #define SHEET_NAME_CANONICAL "Sheetname"
  36. #define SHEET_FILE_CANONICAL "Sheetfile"
  37. #define INTERSHEET_REFS_CANONICAL "Intersheetrefs"
  38. #define USER_FIELD_CANONICAL "Field%d"
  39. static wxString s_CanonicalReference( REFERENCE_CANONICAL );
  40. static wxString s_CanonicalValue( VALUE_CANONICAL );
  41. static wxString s_CanonicalFootprint( FOOTPRINT_CANONICAL );
  42. static wxString s_CanonicalDatasheet( DATASHEET_CANONICAL );
  43. static wxString s_CanonicalDescription( DESCRIPTION_CANONICAL );
  44. static wxString s_CanonicalSheetName( SHEET_NAME_CANONICAL );
  45. static wxString s_CanonicalSheetFile( SHEET_FILE_CANONICAL );
  46. static wxString s_CanonicalIntersheetRefs( INTERSHEET_REFS_CANONICAL );
  47. wxString GetDefaultFieldName( FIELD_T aFieldId, bool aTranslateForHI )
  48. {
  49. if( !aTranslateForHI )
  50. {
  51. switch( aFieldId )
  52. {
  53. case FIELD_T::REFERENCE: return s_CanonicalReference; // The symbol reference, R1, C1, etc.
  54. case FIELD_T::VALUE: return s_CanonicalValue; // The symbol value
  55. case FIELD_T::FOOTPRINT: return s_CanonicalFootprint; // The footprint for use with Pcbnew
  56. case FIELD_T::DATASHEET: return s_CanonicalDatasheet; // Link to a datasheet for symbol
  57. case FIELD_T::DESCRIPTION: return s_CanonicalDescription; // The symbol description
  58. case FIELD_T::SHEET_NAME: return s_CanonicalSheetName;
  59. case FIELD_T::SHEET_FILENAME: return s_CanonicalSheetFile;
  60. case FIELD_T::INTERSHEET_REFS: return s_CanonicalIntersheetRefs;
  61. default: return GetUserFieldName( 42, aTranslateForHI );
  62. }
  63. }
  64. else
  65. {
  66. switch( aFieldId )
  67. {
  68. case FIELD_T::REFERENCE: return _( REFERENCE_CANONICAL ); // The symbol reference, R1, C1, etc.
  69. case FIELD_T::VALUE: return _( VALUE_CANONICAL ); // The symbol value
  70. case FIELD_T::FOOTPRINT: return _( FOOTPRINT_CANONICAL ); // The footprint for use with Pcbnew
  71. case FIELD_T::DATASHEET: return _( DATASHEET_CANONICAL ); // Link to a datasheet for symbol
  72. case FIELD_T::DESCRIPTION: return _( DESCRIPTION_CANONICAL ); // The symbol description
  73. case FIELD_T::SHEET_NAME: return _( SHEET_NAME_CANONICAL );
  74. case FIELD_T::SHEET_FILENAME: return _( SHEET_FILE_CANONICAL );
  75. case FIELD_T::INTERSHEET_REFS: return _( INTERSHEET_REFS_CANONICAL );
  76. default: return GetUserFieldName( 42, aTranslateForHI );
  77. }
  78. }
  79. }
  80. wxString GetUserFieldName( int aFieldNdx, bool aTranslateForHI )
  81. {
  82. if( !aTranslateForHI )
  83. return wxString::Format( wxS( USER_FIELD_CANONICAL ), aFieldNdx );
  84. else
  85. return wxString::Format( _( USER_FIELD_CANONICAL ), aFieldNdx );
  86. }
  87. void TEMPLATE_FIELDNAME::Format( OUTPUTFORMATTER* out ) const
  88. {
  89. out->Print( "(field (name %s)", out->Quotew( m_Name ).c_str() );
  90. if( m_Visible )
  91. out->Print( " visible" );
  92. if( m_URL )
  93. out->Print( " url" );
  94. out->Print( ")" );
  95. }
  96. void TEMPLATE_FIELDNAME::Parse( TEMPLATE_FIELDNAMES_LEXER* in )
  97. {
  98. T tok;
  99. in->NeedLEFT(); // begin (name ...)
  100. if( ( tok = in->NextTok() ) != T_name )
  101. in->Expecting( T_name );
  102. in->NeedSYMBOLorNUMBER();
  103. m_Name = From_UTF8( in->CurText() );
  104. in->NeedRIGHT(); // end (name ...)
  105. while( (tok = in->NextTok() ) != T_RIGHT && tok != T_EOF )
  106. {
  107. // "visible" has no '(' prefix, "value" does, so T_LEFT is optional.
  108. if( tok == T_LEFT )
  109. tok = in->NextTok();
  110. switch( tok )
  111. {
  112. case T_value:
  113. // older format; silently skip
  114. in->NeedSYMBOLorNUMBER();
  115. in->NeedRIGHT();
  116. break;
  117. case T_visible:
  118. m_Visible = true;
  119. break;
  120. case T_url:
  121. m_URL = true;
  122. break;
  123. default:
  124. in->Expecting( "value|url|visible" );
  125. break;
  126. }
  127. }
  128. }
  129. void TEMPLATES::Format( OUTPUTFORMATTER* out, bool aGlobal ) const
  130. {
  131. // We'll keep this general, and include the \n, even though the only known
  132. // use at this time will not want the newlines or the indentation.
  133. out->Print( "(templatefields" );
  134. const std::vector<TEMPLATE_FIELDNAME>& source = aGlobal ? m_globals : m_project;
  135. for( const TEMPLATE_FIELDNAME& temp : source )
  136. {
  137. if( !temp.m_Name.IsEmpty() )
  138. temp.Format( out );
  139. }
  140. out->Print( ")" );
  141. }
  142. void TEMPLATES::parse( TEMPLATE_FIELDNAMES_LEXER* in, bool aGlobal )
  143. {
  144. T tok;
  145. while( ( tok = in->NextTok() ) != T_RIGHT && tok != T_EOF )
  146. {
  147. if( tok == T_LEFT )
  148. tok = in->NextTok();
  149. switch( tok )
  150. {
  151. case T_templatefields: // a token indicating class TEMPLATES.
  152. // Be flexible regarding the starting point of the TEMPLATE_FIELDNAMES_LEXER
  153. // stream. Caller may not have read the first two tokens out of the
  154. // stream: T_LEFT and T_templatefields, so ignore them if seen here.
  155. break;
  156. case T_field:
  157. {
  158. // instantiate on stack, so if exception is thrown,
  159. // destructor runs
  160. TEMPLATE_FIELDNAME field;
  161. field.Parse( in );
  162. // add the field
  163. if( !field.m_Name.IsEmpty() )
  164. AddTemplateFieldName( field, aGlobal );
  165. }
  166. break;
  167. default:
  168. in->Unexpected( in->CurText() );
  169. break;
  170. }
  171. }
  172. }
  173. /**
  174. * Flatten project and global templates into a single list. (Project templates take
  175. * precedence.)
  176. */
  177. void TEMPLATES::resolveTemplates()
  178. {
  179. m_resolved = m_project;
  180. // Note: order N^2 algorithm. Would need changing if fieldname template sets ever
  181. // get large.
  182. for( const TEMPLATE_FIELDNAME& global : m_globals )
  183. {
  184. bool overriddenInProject = false;
  185. for( const TEMPLATE_FIELDNAME& project : m_project )
  186. {
  187. if( global.m_Name == project.m_Name )
  188. {
  189. overriddenInProject = true;
  190. break;
  191. }
  192. }
  193. if( !overriddenInProject )
  194. m_resolved.push_back( global );
  195. }
  196. m_resolvedDirty = false;
  197. }
  198. void TEMPLATES::AddTemplateFieldName( const TEMPLATE_FIELDNAME& aFieldName, bool aGlobal )
  199. {
  200. // Ensure that the template fieldname does not match a fixed fieldname.
  201. for( FIELD_T fieldId : MANDATORY_FIELDS )
  202. {
  203. if( GetCanonicalFieldName( fieldId ) == aFieldName.m_Name )
  204. return;
  205. }
  206. std::vector<TEMPLATE_FIELDNAME>& target = aGlobal ? m_globals : m_project;
  207. // ensure uniqueness, overwrite any template fieldname by the same name.
  208. for( TEMPLATE_FIELDNAME& temp : target )
  209. {
  210. if( temp.m_Name == aFieldName.m_Name )
  211. {
  212. temp = aFieldName;
  213. m_resolvedDirty = true;
  214. return;
  215. }
  216. }
  217. // the name is legal and not previously added to the config container, append
  218. // it and return its index within the container.
  219. target.push_back( aFieldName );
  220. m_resolvedDirty = true;
  221. }
  222. void TEMPLATES::AddTemplateFieldNames( const wxString& aSerializedFieldNames )
  223. {
  224. TEMPLATE_FIELDNAMES_LEXER field_lexer( TO_UTF8( aSerializedFieldNames ) );
  225. try
  226. {
  227. parse( &field_lexer, true );
  228. }
  229. catch( const IO_ERROR& )
  230. {
  231. }
  232. }
  233. void TEMPLATES::DeleteAllFieldNameTemplates( bool aGlobal )
  234. {
  235. if( aGlobal )
  236. {
  237. m_globals.clear();
  238. m_resolved = m_project;
  239. }
  240. else
  241. {
  242. m_project.clear();
  243. m_resolved = m_globals;
  244. }
  245. m_resolvedDirty = false;
  246. }
  247. const std::vector<TEMPLATE_FIELDNAME>& TEMPLATES::GetTemplateFieldNames()
  248. {
  249. if( m_resolvedDirty )
  250. resolveTemplates();
  251. return m_resolved;
  252. }
  253. const std::vector<TEMPLATE_FIELDNAME>& TEMPLATES::GetTemplateFieldNames( bool aGlobal )
  254. {
  255. if( aGlobal )
  256. return m_globals;
  257. else
  258. return m_project;
  259. }
  260. const TEMPLATE_FIELDNAME* TEMPLATES::GetFieldName( const wxString& aName )
  261. {
  262. if( m_resolvedDirty )
  263. resolveTemplates();
  264. for( const TEMPLATE_FIELDNAME& field : m_resolved )
  265. {
  266. if( field.m_Name == aName )
  267. return &field;
  268. }
  269. return nullptr;
  270. }