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.

315 lines
8.5 KiB

2 years ago
  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 (C) 2015-2023 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 <mutex>
  25. #include <template_fieldnames.h>
  26. #include <pgm_base.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. static wxString s_CanonicalReference( REFERENCE_CANONICAL );
  36. static wxString s_CanonicalValue( VALUE_CANONICAL );
  37. static wxString s_CanonicalFootprint( FOOTPRINT_CANONICAL );
  38. static wxString s_CanonicalDatasheet( DATASHEET_CANONICAL );
  39. static wxString s_CanonicalDescription( DESCRIPTION_CANONICAL );
  40. const wxString TEMPLATE_FIELDNAME::GetDefaultFieldName( int aFieldNdx, bool aTranslateForHI )
  41. {
  42. if( !aTranslateForHI )
  43. {
  44. switch( aFieldNdx )
  45. {
  46. case REFERENCE_FIELD: return s_CanonicalReference; // The symbol reference, R1, C1, etc.
  47. case VALUE_FIELD: return s_CanonicalValue; // The symbol value
  48. case FOOTPRINT_FIELD: return s_CanonicalFootprint; // The footprint for use with Pcbnew
  49. case DATASHEET_FIELD: return s_CanonicalDatasheet; // Link to a datasheet for symbol
  50. case DESCRIPTION_FIELD: return s_CanonicalDescription; // The symbol description
  51. default: break;
  52. }
  53. wxString str( wxS( "Field" ) );
  54. #if wxUSE_UNICODE_WCHAR
  55. str << std::to_wstring( aFieldNdx );
  56. #else
  57. str << std::to_string( aFieldNdx );
  58. #endif
  59. return str;
  60. }
  61. switch( aFieldNdx )
  62. {
  63. case REFERENCE_FIELD: return _( REFERENCE_CANONICAL ); // The symbol reference, R1, C1, etc.
  64. case VALUE_FIELD: return _( VALUE_CANONICAL ); // The symbol value
  65. case FOOTPRINT_FIELD: return _( FOOTPRINT_CANONICAL ); // The footprint for use with Pcbnew
  66. case DATASHEET_FIELD: return _( DATASHEET_CANONICAL ); // Link to a datasheet for symbol
  67. case DESCRIPTION_FIELD: return _( DESCRIPTION_CANONICAL ); // The symbol description
  68. default: return wxString::Format( _( "Field%d" ), aFieldNdx );
  69. }
  70. }
  71. void TEMPLATE_FIELDNAME::Format( OUTPUTFORMATTER* out, int nestLevel ) const
  72. {
  73. out->Print( nestLevel, "(field (name %s)", out->Quotew( m_Name ).c_str() );
  74. if( m_Visible )
  75. out->Print( 0, " visible" );
  76. if( m_URL )
  77. out->Print( 0, " url" );
  78. out->Print( 0, ")\n" );
  79. }
  80. void TEMPLATE_FIELDNAME::Parse( TEMPLATE_FIELDNAMES_LEXER* in )
  81. {
  82. T tok;
  83. in->NeedLEFT(); // begin (name ...)
  84. if( ( tok = in->NextTok() ) != T_name )
  85. in->Expecting( T_name );
  86. in->NeedSYMBOLorNUMBER();
  87. m_Name = From_UTF8( in->CurText() );
  88. in->NeedRIGHT(); // end (name ...)
  89. while( (tok = in->NextTok() ) != T_RIGHT && tok != T_EOF )
  90. {
  91. // "visible" has no '(' prefix, "value" does, so T_LEFT is optional.
  92. if( tok == T_LEFT )
  93. tok = in->NextTok();
  94. switch( tok )
  95. {
  96. case T_value:
  97. // older format; silently skip
  98. in->NeedSYMBOLorNUMBER();
  99. in->NeedRIGHT();
  100. break;
  101. case T_visible:
  102. m_Visible = true;
  103. break;
  104. case T_url:
  105. m_URL = true;
  106. break;
  107. default:
  108. in->Expecting( "value|url|visible" );
  109. break;
  110. }
  111. }
  112. }
  113. void TEMPLATES::Format( OUTPUTFORMATTER* out, int nestLevel, bool aGlobal ) const
  114. {
  115. // We'll keep this general, and include the \n, even though the only known
  116. // use at this time will not want the newlines or the indentation.
  117. out->Print( nestLevel, "(templatefields" );
  118. const TEMPLATE_FIELDNAMES& source = aGlobal ? m_globals : m_project;
  119. for( const TEMPLATE_FIELDNAME& temp : source )
  120. {
  121. if( !temp.m_Name.IsEmpty() )
  122. temp.Format( out, nestLevel+1 );
  123. }
  124. out->Print( 0, ")\n" );
  125. }
  126. void TEMPLATES::parse( TEMPLATE_FIELDNAMES_LEXER* in, bool aGlobal )
  127. {
  128. T tok;
  129. while( ( tok = in->NextTok() ) != T_RIGHT && tok != T_EOF )
  130. {
  131. if( tok == T_LEFT )
  132. tok = in->NextTok();
  133. switch( tok )
  134. {
  135. case T_templatefields: // a token indicating class TEMPLATES.
  136. // Be flexible regarding the starting point of the TEMPLATE_FIELDNAMES_LEXER
  137. // stream. Caller may not have read the first two tokens out of the
  138. // stream: T_LEFT and T_templatefields, so ignore them if seen here.
  139. break;
  140. case T_field:
  141. {
  142. // instantiate on stack, so if exception is thrown,
  143. // destructor runs
  144. TEMPLATE_FIELDNAME field;
  145. field.Parse( in );
  146. // add the field
  147. if( !field.m_Name.IsEmpty() )
  148. AddTemplateFieldName( field, aGlobal );
  149. }
  150. break;
  151. default:
  152. in->Unexpected( in->CurText() );
  153. break;
  154. }
  155. }
  156. }
  157. /*
  158. * Flatten project and global templates into a single list. (Project templates take
  159. * precedence.)
  160. */
  161. void TEMPLATES::resolveTemplates()
  162. {
  163. m_resolved = m_project;
  164. // Note: order N^2 algorithm. Would need changing if fieldname template sets ever
  165. // get large.
  166. for( const TEMPLATE_FIELDNAME& global : m_globals )
  167. {
  168. for( const TEMPLATE_FIELDNAME& project : m_project )
  169. {
  170. if( global.m_Name == project.m_Name )
  171. continue;
  172. }
  173. m_resolved.push_back( global );
  174. }
  175. m_resolvedDirty = false;
  176. }
  177. void TEMPLATES::AddTemplateFieldName( const TEMPLATE_FIELDNAME& aFieldName, bool aGlobal )
  178. {
  179. // Ensure that the template fieldname does not match a fixed fieldname.
  180. for( int i = 0; i < MANDATORY_FIELDS; ++i )
  181. {
  182. if( GetCanonicalFieldName( i ) == aFieldName.m_Name )
  183. return;
  184. }
  185. TEMPLATE_FIELDNAMES& target = aGlobal ? m_globals : m_project;
  186. // ensure uniqueness, overwrite any template fieldname by the same name.
  187. for( TEMPLATE_FIELDNAME& temp : target )
  188. {
  189. if( temp.m_Name == aFieldName.m_Name )
  190. {
  191. temp = aFieldName;
  192. m_resolvedDirty = true;
  193. return;
  194. }
  195. }
  196. // the name is legal and not previously added to the config container, append
  197. // it and return its index within the container.
  198. target.push_back( aFieldName );
  199. m_resolvedDirty = true;
  200. }
  201. void TEMPLATES::AddTemplateFieldNames( const wxString& aSerializedFieldNames )
  202. {
  203. TEMPLATE_FIELDNAMES_LEXER field_lexer( TO_UTF8( aSerializedFieldNames ) );
  204. try
  205. {
  206. parse( &field_lexer, true );
  207. }
  208. catch( const IO_ERROR& )
  209. {
  210. }
  211. }
  212. void TEMPLATES::DeleteAllFieldNameTemplates( bool aGlobal )
  213. {
  214. if( aGlobal )
  215. {
  216. m_globals.clear();
  217. m_resolved = m_project;
  218. }
  219. else
  220. {
  221. m_project.clear();
  222. m_resolved = m_globals;
  223. }
  224. m_resolvedDirty = false;
  225. }
  226. const TEMPLATE_FIELDNAMES& TEMPLATES::GetTemplateFieldNames()
  227. {
  228. if( m_resolvedDirty )
  229. resolveTemplates();
  230. return m_resolved;
  231. }
  232. const TEMPLATE_FIELDNAMES& TEMPLATES::GetTemplateFieldNames( bool aGlobal )
  233. {
  234. if( aGlobal )
  235. return m_globals;
  236. else
  237. return m_project;
  238. }
  239. const TEMPLATE_FIELDNAME* TEMPLATES::GetFieldName( const wxString& aName )
  240. {
  241. if( m_resolvedDirty )
  242. resolveTemplates();
  243. for( const TEMPLATE_FIELDNAME& field : m_resolved )
  244. {
  245. if( field.m_Name == aName )
  246. return &field;
  247. }
  248. return nullptr;
  249. }