KiCad PCB EDA Suite
sch_sexpr_plugin.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 CERN
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Wayne Stambaugh <[email protected]>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <algorithm>
24 
25 // For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
26 // base64 code.
27 #define wxUSE_BASE64 1
28 #include <wx/base64.h>
29 #include <wx/log.h>
30 #include <wx/mstream.h>
31 #include <advanced_config.h>
32 #include <trace_helpers.h>
33 #include <locale_io.h>
34 #include <sch_bitmap.h>
35 #include <sch_bus_entry.h>
36 #include <sch_symbol.h>
37 #include <sch_edit_frame.h> // SYMBOL_ORIENTATION_T
38 #include <sch_junction.h>
39 #include <sch_line.h>
40 #include <sch_no_connect.h>
41 #include <sch_text.h>
42 #include <sch_sheet.h>
43 #include <sch_sheet_pin.h>
44 #include <schematic.h>
46 #include <sch_screen.h>
47 #include <symbol_library.h>
48 #include <lib_shape.h>
49 #include <lib_field.h>
50 #include <lib_pin.h>
51 #include <lib_text.h>
52 #include <eeschema_id.h> // for MAX_UNIT_COUNT_PER_PACKAGE definition
53 #include <sch_file_versions.h>
54 #include <schematic_lexer.h>
56 #include <symbol_lib_table.h> // for PropPowerSymsOnly definition.
57 #include <ee_selection.h>
58 #include <string_utils.h>
59 #include <wx_filename.h> // for ::ResolvePossibleSymlinks()
60 #include <progress_reporter.h>
61 
62 
63 using namespace TSCHEMATIC_T;
64 
65 
66 #define SCH_PARSE_ERROR( text, reader, pos ) \
67  THROW_PARSE_ERROR( text, reader.GetSource(), reader.Line(), \
68  reader.LineNumber(), pos - reader.Line() )
69 
70 
71 static const char* emptyString = "";
72 
76 static void formatFill( OUTPUTFORMATTER* aFormatter, int aNestLevel, FILL_T aFillMode,
77  const COLOR4D& aFillColor )
78 {
79  const char* fillType;
80 
81  switch( aFillMode )
82  {
83  default:
84  case FILL_T::NO_FILL: fillType = "none"; break;
85  case FILL_T::FILLED_SHAPE: fillType = "outline"; break;
86  case FILL_T::FILLED_WITH_BG_BODYCOLOR: fillType = "background"; break;
87  }
88 
89  aFormatter->Print( aNestLevel, "(fill (type %s))", fillType );
90 }
91 
92 
93 static const char* getPinElectricalTypeToken( ELECTRICAL_PINTYPE aType )
94 {
95  switch( aType )
96  {
98  return SCHEMATIC_LEXER::TokenName( T_input );
99 
101  return SCHEMATIC_LEXER::TokenName( T_output );
102 
104  return SCHEMATIC_LEXER::TokenName( T_bidirectional );
105 
107  return SCHEMATIC_LEXER::TokenName( T_tri_state );
108 
110  return SCHEMATIC_LEXER::TokenName( T_passive );
111 
113  return SCHEMATIC_LEXER::TokenName( T_free );
114 
116  return SCHEMATIC_LEXER::TokenName( T_unspecified );
117 
119  return SCHEMATIC_LEXER::TokenName( T_power_in );
120 
122  return SCHEMATIC_LEXER::TokenName( T_power_out );
123 
125  return SCHEMATIC_LEXER::TokenName( T_open_collector );
126 
128  return SCHEMATIC_LEXER::TokenName( T_open_emitter );
129 
131  return SCHEMATIC_LEXER::TokenName( T_no_connect );
132 
133  default:
134  wxFAIL_MSG( "Missing symbol library pin connection type" );
135  }
136 
137  return emptyString;
138 }
139 
140 
141 static const char* getPinShapeToken( GRAPHIC_PINSHAPE aShape )
142 {
143  switch( aShape )
144  {
146  return SCHEMATIC_LEXER::TokenName( T_line );
147 
149  return SCHEMATIC_LEXER::TokenName( T_inverted );
150 
152  return SCHEMATIC_LEXER::TokenName( T_clock );
153 
155  return SCHEMATIC_LEXER::TokenName( T_inverted_clock );
156 
158  return SCHEMATIC_LEXER::TokenName( T_input_low );
159 
161  return SCHEMATIC_LEXER::TokenName( T_clock_low );
162 
164  return SCHEMATIC_LEXER::TokenName( T_output_low );
165 
167  return SCHEMATIC_LEXER::TokenName( T_edge_clock_high );
168 
170  return SCHEMATIC_LEXER::TokenName( T_non_logic );
171 
172  default:
173  wxFAIL_MSG( "Missing symbol library pin shape type" );
174  }
175 
176  return emptyString;
177 }
178 
179 
180 static float getPinAngle( int aOrientation )
181 {
182  switch( aOrientation )
183  {
184  case PIN_RIGHT: return 0.0;
185  case PIN_LEFT: return 180.0;
186  case PIN_UP: return 90.0;
187  case PIN_DOWN: return 270.0;
188  default: wxFAIL_MSG( "Missing symbol library pin orientation type" ); return 0.0;
189  }
190 }
191 
192 
193 static const char* getSheetPinShapeToken( PINSHEETLABEL_SHAPE aShape )
194 {
195  switch( aShape )
196  {
197  case PINSHEETLABEL_SHAPE::PS_INPUT: return SCHEMATIC_LEXER::TokenName( T_input );
198  case PINSHEETLABEL_SHAPE::PS_OUTPUT: return SCHEMATIC_LEXER::TokenName( T_output );
199  case PINSHEETLABEL_SHAPE::PS_BIDI: return SCHEMATIC_LEXER::TokenName( T_bidirectional );
200  case PINSHEETLABEL_SHAPE::PS_TRISTATE: return SCHEMATIC_LEXER::TokenName( T_tri_state );
201  case PINSHEETLABEL_SHAPE::PS_UNSPECIFIED: return SCHEMATIC_LEXER::TokenName( T_passive );
202  default: wxFAIL; return SCHEMATIC_LEXER::TokenName( T_passive );
203  }
204 }
205 
206 
207 static double getSheetPinAngle( SHEET_SIDE aSide )
208 {
209  double retv;
210 
211  switch( aSide )
212  {
214  case SHEET_SIDE::LEFT: retv = 180.0; break;
215  case SHEET_SIDE::RIGHT: retv = 0.0; break;
216  case SHEET_SIDE::TOP: retv = 90.0; break;
217  case SHEET_SIDE::BOTTOM: retv = 270.0; break;
218  default: wxFAIL; retv = 0.0; break;
219  }
220 
221  return retv;
222 }
223 
224 
225 static wxString getLineStyleToken( PLOT_DASH_TYPE aStyle )
226 {
227  wxString token;
228 
229  switch( aStyle )
230  {
231  case PLOT_DASH_TYPE::DASH: token = "dash"; break;
232  case PLOT_DASH_TYPE::DOT: token = "dot"; break;
233  case PLOT_DASH_TYPE::DASHDOT: token = "dash_dot"; break;
234  case PLOT_DASH_TYPE::SOLID: token = "solid"; break;
235  case PLOT_DASH_TYPE::DEFAULT: token = "default"; break;
236  }
237 
238  return token;
239 }
240 
241 
242 static const char* getTextTypeToken( KICAD_T aType )
243 {
244  switch( aType )
245  {
246  case SCH_TEXT_T: return SCHEMATIC_LEXER::TokenName( T_text );
247  case SCH_LABEL_T: return SCHEMATIC_LEXER::TokenName( T_label );
248  case SCH_GLOBAL_LABEL_T: return SCHEMATIC_LEXER::TokenName( T_global_label );
249  case SCH_HIER_LABEL_T: return SCHEMATIC_LEXER::TokenName( T_hierarchical_label );
250  default: wxFAIL; return SCHEMATIC_LEXER::TokenName( T_text );
251  }
252 }
253 
254 
267 static void formatStroke( OUTPUTFORMATTER* aFormatter, int aNestLevel,
268  const STROKE_PARAMS& aStroke )
269 {
270  wxASSERT( aFormatter != nullptr );
271 
272  aFormatter->Print( aNestLevel, "(stroke (width %s) (type %s) (color %d %d %d %s))",
273  FormatInternalUnits( aStroke.GetWidth() ).c_str(),
274  TO_UTF8( getLineStyleToken( aStroke.GetPlotStyle() ) ),
275  KiROUND( aStroke.GetColor().r * 255.0 ),
276  KiROUND( aStroke.GetColor().g * 255.0 ),
277  KiROUND( aStroke.GetColor().b * 255.0 ),
278  Double2Str( aStroke.GetColor().a ).c_str() );
279 }
280 
281 
282 static void formatArc( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aArc, int x1, int x2,
283  const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
284  KIID aUuid = niluuid )
285 {
286  if( x1 > 1800 )
287  x1 -= 3600;
288 
289  if( x2 > 1800 )
290  x2 -= 3600;
291 
292  aFormatter->Print( aNestLevel, "(arc (start %s) (mid %s) (end %s)\n",
293  FormatInternalUnits( aArc->GetStart() ).c_str(),
294  FormatInternalUnits( aArc->GetArcMid() ).c_str(),
295  FormatInternalUnits( aArc->GetEnd() ).c_str() );
296 
297  formatStroke( aFormatter, aNestLevel + 1, aStroke );
298  aFormatter->Print( 0, "\n" );
299  formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
300  aFormatter->Print( 0, "\n" );
301 
302  if( aUuid != niluuid )
303  aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
304 
305  aFormatter->Print( aNestLevel, ")\n" );
306 }
307 
308 
309 static void formatCircle( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aCircle,
310  const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
311  KIID aUuid = niluuid )
312 {
313  aFormatter->Print( aNestLevel, "(circle (center %s %s) (radius %s) (stroke (width %s)) ",
314  FormatInternalUnits( aCircle->GetStart().x ).c_str(),
315  FormatInternalUnits( aCircle->GetStart().y ).c_str(),
316  FormatInternalUnits( aCircle->GetRadius() ).c_str(),
317  FormatInternalUnits( aCircle->GetWidth() ).c_str() );
318 
319  formatStroke( aFormatter, aNestLevel + 1, aStroke );
320  aFormatter->Print( 0, "\n" );
321  formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
322  aFormatter->Print( 0, "\n" );
323 
324  if( aUuid != niluuid )
325  aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
326 
327  aFormatter->Print( aNestLevel, ")\n" );
328 }
329 
330 
331 static void formatRect( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aRect,
332  const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
333  KIID aUuid = niluuid )
334 {
335  aFormatter->Print( aNestLevel, "(rectangle (start %s %s) (end %s %s)\n",
336  FormatInternalUnits( aRect->GetStart().x ).c_str(),
337  FormatInternalUnits( aRect->GetStart().y ).c_str(),
338  FormatInternalUnits( aRect->GetEnd().x ).c_str(),
339  FormatInternalUnits( aRect->GetEnd().y ).c_str() );
340  formatStroke( aFormatter, aNestLevel + 1, aStroke );
341  aFormatter->Print( 0, "\n" );
342  formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
343  aFormatter->Print( 0, "\n" );
344 
345  if( aUuid != niluuid )
346  aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
347 
348  aFormatter->Print( aNestLevel, ")\n" );
349 }
350 
351 
352 static void formatBezier( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aBezier,
353  const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
354  KIID aUuid = niluuid )
355 {
356  aFormatter->Print( aNestLevel, "(bezier (pts " );
357 
358  for( const wxPoint& pt : { aBezier->GetStart(), aBezier->GetBezierC1(),
359  aBezier->GetBezierC2(), aBezier->GetEnd() } )
360  {
361  aFormatter->Print( 0, " (xy %s %s)",
362  FormatInternalUnits( pt.x ).c_str(),
363  FormatInternalUnits( pt.y ).c_str() );
364  }
365 
366  aFormatter->Print( 0, ")\n" ); // Closes pts token on same line.
367 
368  formatStroke( aFormatter, aNestLevel + 1, aStroke );
369  aFormatter->Print( 0, "\n" );
370  formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
371  aFormatter->Print( 0, "\n" );
372 
373  if( aUuid != niluuid )
374  aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
375 
376  aFormatter->Print( aNestLevel, ")\n" );
377 }
378 
379 
380 static void formatPoly( OUTPUTFORMATTER* aFormatter, int aNestLevel, EDA_SHAPE* aPolyLine,
381  const STROKE_PARAMS& aStroke, FILL_T aFillMode, const COLOR4D& aFillColor,
382  KIID aUuid = niluuid )
383 {
384  int newLine = 0;
385  int lineCount = 1;
386  aFormatter->Print( aNestLevel, "(polyline\n" );
387  aFormatter->Print( aNestLevel + 1, "(pts" );
388 
389  for( const VECTOR2I& pt : aPolyLine->GetPolyShape().Outline( 0 ).CPoints() )
390  {
391  if( newLine == 4 || !ADVANCED_CFG::GetCfg().m_CompactSave )
392  {
393  aFormatter->Print( 0, "\n" );
394  aFormatter->Print( aNestLevel + 2, "(xy %s %s)",
395  FormatInternalUnits( pt.x ).c_str(),
396  FormatInternalUnits( pt.y ).c_str() );
397  newLine = 0;
398  lineCount += 1;
399  }
400  else
401  {
402  aFormatter->Print( 0, " (xy %s %s)",
403  FormatInternalUnits( pt.x ).c_str(),
404  FormatInternalUnits( pt.y ).c_str() );
405  }
406 
407  newLine += 1;
408  }
409 
410  if( lineCount == 1 )
411  {
412  aFormatter->Print( 0, ")\n" ); // Closes pts token on same line.
413  }
414  else
415  {
416  aFormatter->Print( 0, "\n" );
417  aFormatter->Print( aNestLevel + 1, ")\n" ); // Closes pts token with multiple lines.
418  }
419 
420  formatStroke( aFormatter, aNestLevel + 1, aStroke );
421  aFormatter->Print( 0, "\n" );
422  formatFill( aFormatter, aNestLevel + 1, aFillMode, aFillColor );
423  aFormatter->Print( 0, "\n" );
424 
425  if( aUuid != niluuid )
426  aFormatter->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aUuid.AsString() ) );
427 
428  aFormatter->Print( aNestLevel, ")\n" );
429 }
430 
431 
438 {
439  static int m_modHash; // Keep track of the modification status of the library.
440 
441  wxString m_fileName; // Absolute path and file name.
442  wxFileName m_libFileName; // Absolute path and file name is required here.
443  wxDateTime m_fileModTime;
444  LIB_SYMBOL_MAP m_symbols; // Map of names of #LIB_SYMBOL pointers.
448  SCH_LIB_TYPE m_libType; // Is this cache a symbol or symbol library.
449 
450  LIB_SYMBOL* removeSymbol( LIB_SYMBOL* aAlias );
451 
452  static void saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter,
453  int aNestLevel );
454  static void saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter, int& aNextFreeFieldId,
455  int aNestLevel );
456  static void savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
457  static void saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 );
458 
459  static void saveDcmInfoAsFields( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter,
460  int& aNextFreeFieldId, int aNestLevel );
461 
463 
464 public:
465  SCH_SEXPR_PLUGIN_CACHE( const wxString& aLibraryPath );
467 
468  int GetModifyHash() const { return m_modHash; }
469 
470  // Most all functions in this class throw IO_ERROR exceptions. There are no
471  // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects.
472  // Catch these exceptions higher up please.
473 
475  void Save();
476 
477  void Load();
478 
479  void AddSymbol( const LIB_SYMBOL* aSymbol );
480 
481  void DeleteSymbol( const wxString& aName );
482 
483  // If m_libFileName is a symlink follow it to the real source file
484  wxFileName GetRealFile() const;
485 
486  wxDateTime GetLibModificationTime();
487 
488  bool IsFile( const wxString& aFullPathAndFileName ) const;
489 
490  bool IsFileChanged() const;
491 
492  void SetModified( bool aModified = true ) { m_isModified = aModified; }
493 
494  wxString GetLogicalName() const { return m_libFileName.GetName(); }
495 
496  void SetFileName( const wxString& aFileName ) { m_libFileName = aFileName; }
497 
498  wxString GetFileName() const { return m_libFileName.GetFullPath(); }
499 
500  static void SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter,
501  int aNestLevel = 0, const wxString& aLibName = wxEmptyString );
502 };
503 
504 
506  m_progressReporter( nullptr )
507 {
508  init( nullptr );
509 }
510 
511 
513 {
514  delete m_cache;
515 }
516 
517 
518 void SCH_SEXPR_PLUGIN::init( SCHEMATIC* aSchematic, const PROPERTIES* aProperties )
519 {
520  m_version = 0;
521  m_rootSheet = nullptr;
522  m_schematic = aSchematic;
523  m_cache = nullptr;
524  m_out = nullptr;
525  m_nextFreeFieldId = 100; // number arbitrarily > MANDATORY_FIELDS or SHEET_MANDATORY_FIELDS
526 }
527 
528 
529 SCH_SHEET* SCH_SEXPR_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
530  SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties )
531 {
532  wxASSERT( !aFileName || aSchematic != nullptr );
533 
534  LOCALE_IO toggle; // toggles on, then off, the C locale.
535  SCH_SHEET* sheet;
536 
537  wxFileName fn = aFileName;
538 
539  // Unfortunately child sheet file names the legacy schematic file format are not fully
540  // qualified and are always appended to the project path. The aFileName attribute must
541  // always be an absolute path so the project path can be used for load child sheet files.
542  wxASSERT( fn.IsAbsolute() );
543 
544  if( aAppendToMe )
545  {
546  wxLogTrace( traceSchLegacyPlugin, "Append \"%s\" to sheet \"%s\".",
547  aFileName, aAppendToMe->GetFileName() );
548 
549  wxFileName normedFn = aAppendToMe->GetFileName();
550 
551  if( !normedFn.IsAbsolute() )
552  {
553  if( aFileName.Right( normedFn.GetFullPath().Length() ) == normedFn.GetFullPath() )
554  m_path = aFileName.Left( aFileName.Length() - normedFn.GetFullPath().Length() );
555  }
556 
557  if( m_path.IsEmpty() )
558  m_path = aSchematic->Prj().GetProjectPath();
559 
560  wxLogTrace( traceSchLegacyPlugin, "Normalized append path \"%s\".", m_path );
561  }
562  else
563  {
564  m_path = aSchematic->Prj().GetProjectPath();
565  }
566 
567  m_currentPath.push( m_path );
568  init( aSchematic, aProperties );
569 
570  if( aAppendToMe == nullptr )
571  {
572  // Clean up any allocated memory if an exception occurs loading the schematic.
573  std::unique_ptr<SCH_SHEET> newSheet = std::make_unique<SCH_SHEET>( aSchematic );
574 
575  wxFileName relPath( aFileName );
576  // Do not use wxPATH_UNIX as option in MakeRelativeTo(). It can create incorrect
577  // relative paths on Windows, because paths have a disk identifier (C:, D: ...)
578  relPath.MakeRelativeTo( aSchematic->Prj().GetProjectPath() );
579 
580  newSheet->SetFileName( relPath.GetFullPath() );
581  m_rootSheet = newSheet.get();
582  loadHierarchy( newSheet.get() );
583 
584  // If we got here, the schematic loaded successfully.
585  sheet = newSheet.release();
586  m_rootSheet = nullptr; // Quiet Coverity warning.
587  }
588  else
589  {
590  wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
591  m_rootSheet = &aSchematic->Root();
592  sheet = aAppendToMe;
593  loadHierarchy( sheet );
594  }
595 
596  wxASSERT( m_currentPath.size() == 1 ); // only the project path should remain
597 
598  m_currentPath.pop(); // Clear the path stack for next call to Load
599 
600  return sheet;
601 }
602 
603 
604 // Everything below this comment is recursive. Modify with care.
605 
607 {
608  SCH_SCREEN* screen = nullptr;
609 
610  if( !aSheet->GetScreen() )
611  {
612  // SCH_SCREEN objects store the full path and file name where the SCH_SHEET object only
613  // stores the file name and extension. Add the project path to the file name and
614  // extension to compare when calling SCH_SHEET::SearchHierarchy().
615  wxFileName fileName = aSheet->GetFileName();
616 
617  if( !fileName.IsAbsolute() )
618  fileName.MakeAbsolute( m_currentPath.top() );
619 
620  // Save the current path so that it gets restored when descending and ascending the
621  // sheet hierarchy which allows for sheet schematic files to be nested in folders
622  // relative to the last path a schematic was loaded from.
623  wxLogTrace( traceSchLegacyPlugin, "Saving path '%s'", m_currentPath.top() );
624  m_currentPath.push( fileName.GetPath() );
625  wxLogTrace( traceSchLegacyPlugin, "Current path '%s'", m_currentPath.top() );
626  wxLogTrace( traceSchLegacyPlugin, "Loading '%s'", fileName.GetFullPath() );
627 
628  m_rootSheet->SearchHierarchy( fileName.GetFullPath(), &screen );
629 
630  if( screen )
631  {
632  aSheet->SetScreen( screen );
633  aSheet->GetScreen()->SetParent( m_schematic );
634  // Do not need to load the sub-sheets - this has already been done.
635  }
636  else
637  {
638  aSheet->SetScreen( new SCH_SCREEN( m_schematic ) );
639  aSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
640 
641  try
642  {
643  loadFile( fileName.GetFullPath(), aSheet );
644  }
645  catch( const IO_ERROR& ioe )
646  {
647  // If there is a problem loading the root sheet, there is no recovery.
648  if( aSheet == m_rootSheet )
649  throw;
650 
651  // For all subsheets, queue up the error message for the caller.
652  if( !m_error.IsEmpty() )
653  m_error += "\n";
654 
655  m_error += ioe.What();
656  }
657 
658  aSheet->GetScreen()->SetFileReadOnly( !fileName.IsFileWritable() );
659  aSheet->GetScreen()->SetFileExists( true );
660 
661  // This was moved out of the try{} block so that any sheets definitionsthat
662  // the plugin fully parsed before the exception was raised will be loaded.
663  for( SCH_ITEM* aItem : aSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
664  {
665  wxCHECK2( aItem->Type() == SCH_SHEET_T, /* do nothing */ );
666  SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
667 
668  // Recursion starts here.
669  loadHierarchy( sheet );
670  }
671  }
672 
673  m_currentPath.pop();
674  wxLogTrace( traceSchLegacyPlugin, "Restoring path \"%s\"", m_currentPath.top() );
675  }
676 }
677 
678 
679 void SCH_SEXPR_PLUGIN::loadFile( const wxString& aFileName, SCH_SHEET* aSheet )
680 {
681  FILE_LINE_READER reader( aFileName );
682 
683  size_t lineCount = 0;
684 
685  if( m_progressReporter )
686  {
687  m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
688 
690  THROW_IO_ERROR( ( "Open cancelled by user." ) );
691 
692  while( reader.ReadLine() )
693  lineCount++;
694 
695  reader.Rewind();
696  }
697 
698  SCH_SEXPR_PARSER parser( &reader, m_progressReporter, lineCount );
699 
700  parser.ParseSchematic( aSheet );
701 }
702 
703 
704 void SCH_SEXPR_PLUGIN::LoadContent( LINE_READER& aReader, SCH_SHEET* aSheet, int aFileVersion )
705 {
706  wxCHECK( aSheet, /* void */ );
707 
708  LOCALE_IO toggle;
709  SCH_SEXPR_PARSER parser( &aReader );
710 
711  parser.ParseSchematic( aSheet, true, aFileVersion );
712 }
713 
714 
715 void SCH_SEXPR_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
716  const PROPERTIES* aProperties )
717 {
718  wxCHECK_RET( aSheet != nullptr, "NULL SCH_SHEET object." );
719  wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
720 
721  LOCALE_IO toggle; // toggles on, then off, the C locale, to write floating point values.
722 
723  init( aSchematic, aProperties );
724 
725  wxFileName fn = aFileName;
726 
727  // File names should be absolute. Don't assume everything relative to the project path
728  // works properly.
729  wxASSERT( fn.IsAbsolute() );
730 
731  FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
732 
733  m_out = &formatter; // no ownership
734 
735  Format( aSheet );
736 
737  aSheet->GetScreen()->SetFileExists( true );
738 }
739 
740 
742 {
743  wxCHECK_RET( aSheet != nullptr, "NULL SCH_SHEET* object." );
744  wxCHECK_RET( m_schematic != nullptr, "NULL SCHEMATIC* object." );
745 
746  SCH_SCREEN* screen = aSheet->GetScreen();
747 
748  wxCHECK( screen, /* void */ );
749 
750  m_out->Print( 0, "(kicad_sch (version %d) (generator eeschema)\n\n",
752 
753  m_out->Print( 1, "(uuid %s)\n\n", TO_UTF8( screen->m_uuid.AsString() ) );
754 
755  screen->GetPageSettings().Format( m_out, 1, 0 );
756  m_out->Print( 0, "\n" );
757  screen->GetTitleBlock().Format( m_out, 1, 0 );
758 
759  // Save cache library.
760  m_out->Print( 1, "(lib_symbols\n" );
761 
762  for( auto libSymbol : screen->GetLibSymbols() )
763  SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( libSymbol.second, *m_out, 2, libSymbol.first );
764 
765  m_out->Print( 1, ")\n\n" );
766 
767  for( const auto& alias : screen->GetBusAliases() )
768  {
769  saveBusAlias( alias, 1 );
770  }
771 
772  // Enforce item ordering
773  auto cmp = []( const SCH_ITEM* a, const SCH_ITEM* b )
774  {
775  return *a < *b;
776  };
777 
778  std::multiset<SCH_ITEM*, decltype( cmp )> save_map( cmp );
779 
780  for( SCH_ITEM* item : screen->Items() )
781  save_map.insert( item );
782 
783  KICAD_T itemType = TYPE_NOT_INIT;
785 
786  for( SCH_ITEM* item : save_map )
787  {
788  if( itemType != item->Type() )
789  {
790  itemType = item->Type();
791 
792  if( itemType != SCH_SYMBOL_T
793  && itemType != SCH_JUNCTION_T
794  && itemType != SCH_SHEET_T )
795  m_out->Print( 0, "\n" );
796  }
797 
798  switch( item->Type() )
799  {
800  case SCH_SYMBOL_T:
801  m_out->Print( 0, "\n" );
802  saveSymbol( static_cast<SCH_SYMBOL*>( item ), nullptr, 1 );
803  break;
804 
805  case SCH_BITMAP_T:
806  saveBitmap( static_cast<SCH_BITMAP*>( item ), 1 );
807  break;
808 
809  case SCH_SHEET_T:
810  m_out->Print( 0, "\n" );
811  saveSheet( static_cast<SCH_SHEET*>( item ), 1 );
812  break;
813 
814  case SCH_JUNCTION_T:
815  saveJunction( static_cast<SCH_JUNCTION*>( item ), 1 );
816  break;
817 
818  case SCH_NO_CONNECT_T:
819  saveNoConnect( static_cast<SCH_NO_CONNECT*>( item ), 1 );
820  break;
821 
823  case SCH_BUS_BUS_ENTRY_T:
824  saveBusEntry( static_cast<SCH_BUS_ENTRY_BASE*>( item ), 1 );
825  break;
826 
827  case SCH_LINE_T:
828  if( layer != item->GetLayer() )
829  {
830  if( layer == SCH_LAYER_ID_START )
831  {
832  layer = item->GetLayer();
833  }
834  else
835  {
836  layer = item->GetLayer();
837  m_out->Print( 0, "\n" );
838  }
839  }
840 
841  saveLine( static_cast<SCH_LINE*>( item ), 1 );
842  break;
843 
844  case SCH_TEXT_T:
845  case SCH_LABEL_T:
846  case SCH_GLOBAL_LABEL_T:
847  case SCH_HIER_LABEL_T:
848  saveText( static_cast<SCH_TEXT*>( item ), 1 );
849  break;
850 
851  default:
852  wxASSERT( "Unexpected schematic object type in SCH_SEXPR_PLUGIN::Format()" );
853  }
854  }
855 
856  // If this is the root sheet, save all of the sheet paths.
857  if( aSheet->IsRootSheet() )
858  {
859  SCH_SHEET_LIST sheetPaths( aSheet, true );
860 
861  SCH_REFERENCE_LIST symbolInstances;
862 
863  for( const SCH_SHEET_PATH& sheetPath : sheetPaths )
864  sheetPath.GetSymbols( symbolInstances, true, true );
865 
866  symbolInstances.SortByReferenceOnly();
867 
868  saveInstances( sheetPaths.GetSheetInstances(), symbolInstances.GetSymbolInstances(), 1 );
869  }
870  else
871  {
872  // Schematic files (SCH_SCREEN objects) can be shared so we have to save and restore
873  // symbol and sheet instance data even if the file being saved is not the root sheet
874  // because it is possible that the file is the root sheet of another project.
875  saveInstances( screen->m_sheetInstances, screen->m_symbolInstances, 1 );
876  }
877 
878  m_out->Print( 0, ")\n" );
879 }
880 
881 
882 void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelectionPath,
883  SCH_SHEET_LIST* aFullSheetHierarchy,
884  OUTPUTFORMATTER* aFormatter )
885 {
886  wxCHECK( aSelection && aSelectionPath && aFullSheetHierarchy && aFormatter, /* void */ );
887 
888  LOCALE_IO toggle;
889 
890  m_out = aFormatter;
891 
892  size_t i;
893  SCH_ITEM* item;
894  std::map<wxString, LIB_SYMBOL*> libSymbols;
895  SCH_SCREEN* screen = aSelection->GetScreen();
896 
897  for( i = 0; i < aSelection->GetSize(); ++i )
898  {
899  item = dynamic_cast<SCH_ITEM*>( aSelection->GetItem( i ) );
900 
901  wxCHECK2( item, continue );
902 
903  if( item->Type() != SCH_SYMBOL_T )
904  continue;
905 
906  SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
907 
908  wxCHECK2( symbol, continue );
909 
910  wxString libSymbolLookup = symbol->GetLibId().Format().wx_str();
911 
912  if( !symbol->UseLibIdLookup() )
913  libSymbolLookup = symbol->GetSchSymbolLibraryName();
914 
915  auto it = screen->GetLibSymbols().find( libSymbolLookup );
916 
917  if( it != screen->GetLibSymbols().end() )
918  libSymbols[ libSymbolLookup ] = it->second;
919  }
920 
921  if( !libSymbols.empty() )
922  {
923  m_out->Print( 0, "(lib_symbols\n" );
924 
925  for( auto libSymbol : libSymbols )
926  SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( libSymbol.second, *m_out, 1, libSymbol.first );
927 
928  m_out->Print( 0, ")\n\n" );
929  }
930 
931  // Store the selected sheets instance information
932  SCH_SHEET_LIST selectedSheets;
933  selectedSheets.push_back( *aSelectionPath ); // Include the "root" of the selection
934 
935  SCH_REFERENCE_LIST selectedSymbols;
936 
937  for( i = 0; i < aSelection->GetSize(); ++i )
938  {
939  item = (SCH_ITEM*) aSelection->GetItem( i );
940 
941  switch( item->Type() )
942  {
943  case SCH_SYMBOL_T:
944  saveSymbol( static_cast<SCH_SYMBOL*>( item ), aSelectionPath, 0 );
945 
946  aSelectionPath->AppendSymbol( selectedSymbols, static_cast<SCH_SYMBOL*>( item ),
947  true, true );
948  break;
949 
950  case SCH_BITMAP_T:
951  saveBitmap( static_cast< SCH_BITMAP* >( item ), 0 );
952  break;
953 
954  case SCH_SHEET_T:
955  saveSheet( static_cast< SCH_SHEET* >( item ), 0 );
956 
957  {
958  SCH_SHEET_PATH subSheetPath = *aSelectionPath;
959  subSheetPath.push_back( static_cast<SCH_SHEET*>( item ) );
960 
961  aFullSheetHierarchy->GetSheetsWithinPath( selectedSheets, subSheetPath );
962  aFullSheetHierarchy->GetSymbolsWithinPath( selectedSymbols, subSheetPath, true,
963  true );
964  }
965 
966  break;
967 
968  case SCH_JUNCTION_T:
969  saveJunction( static_cast< SCH_JUNCTION* >( item ), 0 );
970  break;
971 
972  case SCH_NO_CONNECT_T:
973  saveNoConnect( static_cast< SCH_NO_CONNECT* >( item ), 0 );
974  break;
975 
977  case SCH_BUS_BUS_ENTRY_T:
978  saveBusEntry( static_cast< SCH_BUS_ENTRY_BASE* >( item ), 0 );
979  break;
980 
981  case SCH_LINE_T:
982  saveLine( static_cast< SCH_LINE* >( item ), 0 );
983  break;
984 
985  case SCH_TEXT_T:
986  case SCH_LABEL_T:
987  case SCH_GLOBAL_LABEL_T:
988  case SCH_HIER_LABEL_T:
989  saveText( static_cast< SCH_TEXT* >( item ), 0 );
990  break;
991 
992  default:
993  wxASSERT( "Unexpected schematic object type in SCH_SEXPR_PLUGIN::Format()" );
994  }
995  }
996 
997  // Make all instance information relative to the selection path
998  KIID_PATH selectionPath = aSelectionPath->PathWithoutRootUuid();
999 
1000  selectedSheets.SortByPageNumbers();
1001  std::vector<SCH_SHEET_INSTANCE> sheetinstances = selectedSheets.GetSheetInstances();
1002 
1003  for( SCH_SHEET_INSTANCE& sheetInstance : sheetinstances )
1004  {
1005  wxASSERT_MSG( sheetInstance.m_Path.MakeRelativeTo( selectionPath ),
1006  "Sheet is not inside the selection path?" );
1007  }
1008 
1009 
1010  selectedSymbols.SortByReferenceOnly();
1011  std::vector<SYMBOL_INSTANCE_REFERENCE> symbolInstances = selectedSymbols.GetSymbolInstances();
1012 
1013  for( SYMBOL_INSTANCE_REFERENCE& symbolInstance : symbolInstances )
1014  {
1015  wxASSERT_MSG( symbolInstance.m_Path.MakeRelativeTo( selectionPath ),
1016  "Symbol is not inside the selection path?" );
1017  }
1018 
1019  saveInstances( sheetinstances, symbolInstances, 0 );
1020 }
1021 
1022 
1024  int aNestLevel )
1025 {
1026  wxCHECK_RET( aSymbol != nullptr && m_out != nullptr, "" );
1027 
1028  std::string libName;
1029  wxArrayString reference_fields;
1030 
1031  static wxString delimiters( wxT( " " ) );
1032 
1033  wxString symbol_name = aSymbol->GetLibId().Format();
1034 
1035  if( symbol_name.size() )
1036  {
1037  libName = toUTFTildaText( symbol_name );
1038  }
1039  else
1040  {
1041  libName = "_NONAME_";
1042  }
1043 
1044  double angle;
1045  int orientation = aSymbol->GetOrientation() & ~( SYM_MIRROR_X | SYM_MIRROR_Y );
1046 
1047  if( orientation == SYM_ORIENT_90 )
1048  angle = 90.0;
1049  else if( orientation == SYM_ORIENT_180 )
1050  angle = 180.0;
1051  else if( orientation == SYM_ORIENT_270 )
1052  angle = 270.0;
1053  else
1054  angle = 0.0;
1055 
1056  m_out->Print( aNestLevel, "(symbol" );
1057 
1058  if( !aSymbol->UseLibIdLookup() )
1059  {
1060  m_out->Print( 0, " (lib_name %s)",
1061  m_out->Quotew( aSymbol->GetSchSymbolLibraryName() ).c_str() );
1062  }
1063 
1064  m_out->Print( 0, " (lib_id %s) (at %s %s %s)",
1065  m_out->Quotew( aSymbol->GetLibId().Format().wx_str() ).c_str(),
1066  FormatInternalUnits( aSymbol->GetPosition().x ).c_str(),
1067  FormatInternalUnits( aSymbol->GetPosition().y ).c_str(),
1068  FormatAngle( angle * 10.0 ).c_str() );
1069 
1070  bool mirrorX = aSymbol->GetOrientation() & SYM_MIRROR_X;
1071  bool mirrorY = aSymbol->GetOrientation() & SYM_MIRROR_Y;
1072 
1073  if( mirrorX || mirrorY )
1074  {
1075  m_out->Print( 0, " (mirror" );
1076 
1077  if( mirrorX )
1078  m_out->Print( 0, " x" );
1079 
1080  if( mirrorY )
1081  m_out->Print( 0, " y" );
1082 
1083  m_out->Print( 0, ")" );
1084  }
1085 
1086  int unit = -1;
1087 
1088  if( !( aSymbol->GetInstanceReferences().size() > 1 ) )
1089  unit = aSymbol->GetUnit();
1090  else if( aSheetPath != nullptr )
1091  unit = aSymbol->GetUnitSelection( aSheetPath );
1092  if ( unit >= 0 )
1093  m_out->Print( 0, " (unit %d)", unit );
1094 
1095  if( aSymbol->GetConvert() == LIB_ITEM::LIB_CONVERT::DEMORGAN )
1096  m_out->Print( 0, " (convert %d)", aSymbol->GetConvert() );
1097 
1098  m_out->Print( 0, "\n" );
1099 
1100  m_out->Print( aNestLevel + 1, "(in_bom %s)", ( aSymbol->GetIncludeInBom() ) ? "yes" : "no" );
1101  m_out->Print( 0, " (on_board %s)", ( aSymbol->GetIncludeOnBoard() ) ? "yes" : "no" );
1102 
1103  if( aSymbol->GetFieldsAutoplaced() != FIELDS_AUTOPLACED_NO )
1104  m_out->Print( 0, " (fields_autoplaced)" );
1105 
1106  m_out->Print( 0, "\n" );
1107 
1108  m_out->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aSymbol->m_Uuid.AsString() ) );
1109 
1111 
1112  for( SCH_FIELD& field : aSymbol->GetFields() )
1113  {
1114  saveField( &field, aNestLevel + 1 );
1115  }
1116 
1117  for( const SCH_PIN* pin : aSymbol->GetPins() )
1118  {
1119  if( pin->GetAlt().IsEmpty() )
1120  {
1121  m_out->Print( aNestLevel + 1, "(pin %s (uuid %s))\n",
1122  m_out->Quotew( pin->GetNumber() ).c_str(),
1123  TO_UTF8( pin->m_Uuid.AsString() ) );
1124  }
1125  else
1126  {
1127  m_out->Print( aNestLevel + 1, "(pin %s (uuid %s) (alternate %s))\n",
1128  m_out->Quotew( pin->GetNumber() ).c_str(),
1129  TO_UTF8( pin->m_Uuid.AsString() ),
1130  m_out->Quotew( pin->GetAlt() ).c_str() );
1131  }
1132  }
1133 
1134  m_out->Print( aNestLevel, ")\n" );
1135 }
1136 
1137 
1138 void SCH_SEXPR_PLUGIN::saveField( SCH_FIELD* aField, int aNestLevel )
1139 {
1140  wxCHECK_RET( aField != nullptr && m_out != nullptr, "" );
1141 
1142  wxString fieldName = aField->GetName();
1143 
1144  // For some reason (bug in legacy parser?) the field ID for non-mandatory fields is -1 so
1145  // check for this in order to correctly use the field name.
1146  if( aField->GetParent()->Type() == SCH_SYMBOL_T )
1147  {
1148  if( aField->GetId() >= 0 && aField->GetId() < MANDATORY_FIELDS )
1149  fieldName = TEMPLATE_FIELDNAME::GetDefaultFieldName( aField->GetId(), false );
1150  }
1151  else if( aField->GetParent()->Type() == SCH_SHEET_T )
1152  {
1153  if( aField->GetId() >= 0 && aField->GetId() < SHEET_MANDATORY_FIELDS )
1154  fieldName = SCH_SHEET::GetDefaultFieldName( aField->GetId() );
1155  }
1156 
1157  if( aField->GetId() == -1 /* undefined ID */ )
1158  {
1159  aField->SetId( m_nextFreeFieldId );
1160  m_nextFreeFieldId += 1;
1161  }
1162  else if( aField->GetId() >= m_nextFreeFieldId )
1163  {
1164  m_nextFreeFieldId = aField->GetId() + 1;
1165  }
1166 
1167  m_out->Print( aNestLevel, "(property %s %s (id %d) (at %s %s %s)",
1168  m_out->Quotew( fieldName ).c_str(),
1169  m_out->Quotew( aField->GetText() ).c_str(),
1170  aField->GetId(),
1171  FormatInternalUnits( aField->GetPosition().x ).c_str(),
1172  FormatInternalUnits( aField->GetPosition().y ).c_str(),
1173  FormatAngle( aField->GetTextAngleDegrees() * 10.0 ).c_str() );
1174 
1175  if( !aField->IsDefaultFormatting()
1176  || ( aField->GetTextHeight() != Mils2iu( DEFAULT_SIZE_TEXT ) ) )
1177  {
1178  m_out->Print( 0, "\n" );
1179  aField->Format( m_out, aNestLevel, 0 );
1180  m_out->Print( aNestLevel, ")\n" ); // Closes property token with font effects.
1181  }
1182  else
1183  {
1184  m_out->Print( 0, ")\n" ); // Closes property token without font effects.
1185  }
1186 }
1187 
1188 
1189 void SCH_SEXPR_PLUGIN::saveBitmap( SCH_BITMAP* aBitmap, int aNestLevel )
1190 {
1191  wxCHECK_RET( aBitmap != nullptr && m_out != nullptr, "" );
1192 
1193  const wxImage* image = aBitmap->GetImage()->GetImageData();
1194 
1195  wxCHECK_RET( image != nullptr, "wxImage* is NULL" );
1196 
1197  m_out->Print( aNestLevel, "(image (at %s %s)",
1198  FormatInternalUnits( aBitmap->GetPosition().x ).c_str(),
1199  FormatInternalUnits( aBitmap->GetPosition().y ).c_str() );
1200 
1201  if( aBitmap->GetImage()->GetScale() != 1.0 )
1202  m_out->Print( 0, " (scale %g)", aBitmap->GetImage()->GetScale() );
1203 
1204  m_out->Print( 0, "\n" );
1205 
1206  m_out->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aBitmap->m_Uuid.AsString() ) );
1207 
1208  m_out->Print( aNestLevel + 1, "(data" );
1209 
1210  wxMemoryOutputStream stream;
1211 
1212  image->SaveFile( stream, wxBITMAP_TYPE_PNG );
1213 
1214  // Write binary data in hexadecimal form (ASCII)
1215  wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
1216  wxString out = wxBase64Encode( buffer->GetBufferStart(), buffer->GetBufferSize() );
1217 
1218  // Apparently the MIME standard character width for base64 encoding is 76 (unconfirmed)
1219  // so use it in a vein attempt to be standard like.
1220 #define MIME_BASE64_LENGTH 76
1221 
1222  size_t first = 0;
1223 
1224  while( first < out.Length() )
1225  {
1226  m_out->Print( 0, "\n" );
1227  m_out->Print( aNestLevel + 2, "%s", TO_UTF8( out( first, MIME_BASE64_LENGTH ) ) );
1228  first += MIME_BASE64_LENGTH;
1229  }
1230 
1231  m_out->Print( 0, "\n" );
1232  m_out->Print( aNestLevel + 1, ")\n" ); // Closes data token.
1233  m_out->Print( aNestLevel, ")\n" ); // Closes image token.
1234 }
1235 
1236 
1237 void SCH_SEXPR_PLUGIN::saveSheet( SCH_SHEET* aSheet, int aNestLevel )
1238 {
1239  wxCHECK_RET( aSheet != nullptr && m_out != nullptr, "" );
1240 
1241  m_out->Print( aNestLevel, "(sheet (at %s %s) (size %s %s)",
1242  FormatInternalUnits( aSheet->GetPosition().x ).c_str(),
1243  FormatInternalUnits( aSheet->GetPosition().y ).c_str(),
1244  FormatInternalUnits( aSheet->GetSize().GetWidth() ).c_str(),
1245  FormatInternalUnits( aSheet->GetSize().GetHeight() ).c_str() );
1246 
1247  if( aSheet->GetFieldsAutoplaced() != FIELDS_AUTOPLACED_NO )
1248  m_out->Print( 0, " (fields_autoplaced)" );
1249 
1250  m_out->Print( 0, "\n" );
1251 
1253  aSheet->GetBorderColor() );
1254 
1255  stroke.SetWidth( aSheet->GetBorderWidth() );
1256  formatStroke( m_out, aNestLevel + 1, stroke );
1257 
1258  m_out->Print( 0, "\n" );
1259 
1260  m_out->Print( aNestLevel + 1, "(fill (color %d %d %d %0.4f))\n",
1261  KiROUND( aSheet->GetBackgroundColor().r * 255.0 ),
1262  KiROUND( aSheet->GetBackgroundColor().g * 255.0 ),
1263  KiROUND( aSheet->GetBackgroundColor().b * 255.0 ),
1264  aSheet->GetBackgroundColor().a );
1265 
1266  m_out->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aSheet->m_Uuid.AsString() ) );
1267 
1269 
1270  for( SCH_FIELD& field : aSheet->GetFields() )
1271  {
1272  saveField( &field, aNestLevel + 1 );
1273  }
1274 
1275  for( const SCH_SHEET_PIN* pin : aSheet->GetPins() )
1276  {
1277  m_out->Print( aNestLevel + 1, "(pin %s %s (at %s %s %s)\n",
1278  EscapedUTF8( pin->GetText() ).c_str(),
1279  getSheetPinShapeToken( pin->GetShape() ),
1280  FormatInternalUnits( pin->GetPosition().x ).c_str(),
1281  FormatInternalUnits( pin->GetPosition().y ).c_str(),
1282  FormatAngle( getSheetPinAngle( pin->GetEdge() ) * 10.0 ).c_str() );
1283 
1284  pin->Format( m_out, aNestLevel + 1, 0 );
1285 
1286  m_out->Print( aNestLevel + 2, "(uuid %s)\n", TO_UTF8( pin->m_Uuid.AsString() ) );
1287 
1288  m_out->Print( aNestLevel + 1, ")\n" ); // Closes pin token.
1289  }
1290 
1291  m_out->Print( aNestLevel, ")\n" ); // Closes sheet token.
1292 }
1293 
1294 
1295 void SCH_SEXPR_PLUGIN::saveJunction( SCH_JUNCTION* aJunction, int aNestLevel )
1296 {
1297  wxCHECK_RET( aJunction != nullptr && m_out != nullptr, "" );
1298 
1299  m_out->Print( aNestLevel, "(junction (at %s %s) (diameter %s) (color %d %d %d %s))\n",
1300  FormatInternalUnits( aJunction->GetPosition().x ).c_str(),
1301  FormatInternalUnits( aJunction->GetPosition().y ).c_str(),
1302  FormatInternalUnits( aJunction->GetDiameter() ).c_str(),
1303  KiROUND( aJunction->GetColor().r * 255.0 ),
1304  KiROUND( aJunction->GetColor().g * 255.0 ),
1305  KiROUND( aJunction->GetColor().b * 255.0 ),
1306  Double2Str( aJunction->GetColor().a ).c_str() );
1307 }
1308 
1309 
1310 void SCH_SEXPR_PLUGIN::saveNoConnect( SCH_NO_CONNECT* aNoConnect, int aNestLevel )
1311 {
1312  wxCHECK_RET( aNoConnect != nullptr && m_out != nullptr, "" );
1313 
1314  m_out->Print( aNestLevel, "(no_connect (at %s %s) (uuid %s))\n",
1315  FormatInternalUnits( aNoConnect->GetPosition().x ).c_str(),
1316  FormatInternalUnits( aNoConnect->GetPosition().y ).c_str(),
1317  TO_UTF8( aNoConnect->m_Uuid.AsString() ) );
1318 }
1319 
1320 
1321 void SCH_SEXPR_PLUGIN::saveBusEntry( SCH_BUS_ENTRY_BASE* aBusEntry, int aNestLevel )
1322 {
1323  wxCHECK_RET( aBusEntry != nullptr && m_out != nullptr, "" );
1324 
1325  // Bus to bus entries are converted to bus line segments.
1326  if( aBusEntry->GetClass() == "SCH_BUS_BUS_ENTRY" )
1327  {
1328  SCH_LINE busEntryLine( aBusEntry->GetPosition(), LAYER_BUS );
1329 
1330  busEntryLine.SetEndPoint( aBusEntry->GetEnd() );
1331  saveLine( &busEntryLine, aNestLevel );
1332  }
1333  else
1334  {
1335  m_out->Print( aNestLevel, "(bus_entry (at %s %s) (size %s %s)\n",
1336  FormatInternalUnits( aBusEntry->GetPosition().x ).c_str(),
1337  FormatInternalUnits( aBusEntry->GetPosition().y ).c_str(),
1338  FormatInternalUnits( aBusEntry->GetSize().GetWidth() ).c_str(),
1339  FormatInternalUnits( aBusEntry->GetSize().GetHeight() ).c_str() );
1340 
1341  formatStroke( m_out, aNestLevel + 1, aBusEntry->GetStroke() );
1342 
1343  m_out->Print( 0, "\n" );
1344 
1345  m_out->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aBusEntry->m_Uuid.AsString() ) );
1346 
1347  m_out->Print( aNestLevel, ")\n" );
1348  }
1349 }
1350 
1351 
1352 void SCH_SEXPR_PLUGIN::saveLine( SCH_LINE* aLine, int aNestLevel )
1353 {
1354  wxCHECK_RET( aLine != nullptr && m_out != nullptr, "" );
1355 
1356  wxString lineType;
1357 
1358  STROKE_PARAMS line_stroke = aLine->GetStroke();
1359 
1360  switch( aLine->GetLayer() )
1361  {
1362  case LAYER_BUS: lineType = "bus"; break;
1363  case LAYER_WIRE: lineType = "wire"; break;
1364  case LAYER_NOTES:
1365  default: lineType = "polyline"; break;
1366  }
1367 
1368  m_out->Print( aNestLevel, "(%s (pts (xy %s %s) (xy %s %s))\n",
1369  TO_UTF8( lineType ),
1370  FormatInternalUnits( aLine->GetStartPoint().x ).c_str(),
1371  FormatInternalUnits( aLine->GetStartPoint().y ).c_str(),
1372  FormatInternalUnits( aLine->GetEndPoint().x ).c_str(),
1373  FormatInternalUnits( aLine->GetEndPoint().y ).c_str() );
1374 
1375  formatStroke( m_out, aNestLevel + 1, line_stroke );
1376  m_out->Print( 0, "\n" );
1377 
1378  m_out->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aLine->m_Uuid.AsString() ) );
1379 
1380  m_out->Print( aNestLevel, ")\n" );
1381 }
1382 
1383 
1384 void SCH_SEXPR_PLUGIN::saveText( SCH_TEXT* aText, int aNestLevel )
1385 {
1386  wxCHECK_RET( aText != nullptr && m_out != nullptr, "" );
1387 
1388  double angle;
1389 
1390  switch( aText->GetLabelSpinStyle() )
1391  {
1392  case LABEL_SPIN_STYLE::RIGHT: angle = 0.0; break;
1393  case LABEL_SPIN_STYLE::UP: angle = 90.0; break;
1394  case LABEL_SPIN_STYLE::LEFT: angle = 180.0; break;
1395  case LABEL_SPIN_STYLE::BOTTOM: angle = 270.0; break;
1396  default: wxFAIL; angle = 0.0; break;
1397  }
1398 
1399  m_out->Print( aNestLevel, "(%s %s",
1400  getTextTypeToken( aText->Type() ),
1401  m_out->Quotew( aText->GetText() ).c_str() );
1402 
1403  if( ( aText->Type() == SCH_GLOBAL_LABEL_T ) || ( aText->Type() == SCH_HIER_LABEL_T ) )
1404  m_out->Print( 0, " (shape %s)", getSheetPinShapeToken( aText->GetShape() ) );
1405 
1406  if( aText->GetText().Length() < 50 )
1407  {
1408  m_out->Print( 0, " (at %s %s %s)",
1409  FormatInternalUnits( aText->GetPosition().x ).c_str(),
1410  FormatInternalUnits( aText->GetPosition().y ).c_str(),
1411  FormatAngle( angle * 10.0 ).c_str() );
1412  }
1413  else
1414  {
1415  m_out->Print( 0, "\n" );
1416  m_out->Print( aNestLevel + 1, "(at %s %s %s)",
1417  FormatInternalUnits( aText->GetPosition().x ).c_str(),
1418  FormatInternalUnits( aText->GetPosition().y ).c_str(),
1419  FormatAngle( aText->GetTextAngle() ).c_str() );
1420  }
1421 
1422  if( aText->GetFieldsAutoplaced() != FIELDS_AUTOPLACED_NO )
1423  m_out->Print( 0, " (fields_autoplaced)" );
1424 
1425  m_out->Print( 0, "\n" );
1426  aText->Format( m_out, aNestLevel, 0 );
1427 
1428  m_out->Print( aNestLevel + 1, "(uuid %s)\n", TO_UTF8( aText->m_Uuid.AsString() ) );
1429 
1430  if( ( aText->Type() == SCH_GLOBAL_LABEL_T ) )
1431  {
1432  SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( aText );
1433  saveField( label->GetIntersheetRefs(), aNestLevel + 1 );
1434  }
1435 
1436  m_out->Print( aNestLevel, ")\n" ); // Closes text token.
1437 }
1438 
1439 
1440 void SCH_SEXPR_PLUGIN::saveBusAlias( std::shared_ptr<BUS_ALIAS> aAlias, int aNestLevel )
1441 {
1442  wxCHECK_RET( aAlias != nullptr, "BUS_ALIAS* is NULL" );
1443 
1444  wxString members;
1445 
1446  for( auto member : aAlias->Members() )
1447  {
1448  if( members.IsEmpty() )
1449  members = m_out->Quotew( member );
1450  else
1451  members += " " + m_out->Quotew( member );
1452  }
1453 
1454  m_out->Print( aNestLevel, "(bus_alias %s (members %s))\n",
1455  m_out->Quotew( aAlias->GetName() ).c_str(),
1456  TO_UTF8( members ) );
1457 }
1458 
1459 
1460 void SCH_SEXPR_PLUGIN::saveInstances( const std::vector<SCH_SHEET_INSTANCE>& aSheets,
1461  const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbols,
1462  int aNestLevel )
1463 {
1464  if( aSheets.size() )
1465  {
1466  m_out->Print( 0, "\n" );
1467  m_out->Print( aNestLevel, "(sheet_instances\n" );
1468 
1469  for( const SCH_SHEET_INSTANCE& instance : aSheets )
1470  {
1471  wxString path = instance.m_Path.AsString();
1472 
1473  if( path.IsEmpty() )
1474  path = wxT( "/" ); // Root path
1475 
1476  m_out->Print( aNestLevel + 1, "(path %s (page %s))\n",
1477  m_out->Quotew( path ).c_str(),
1478  m_out->Quotew( instance.m_PageNumber ).c_str() );
1479  }
1480 
1481  m_out->Print( aNestLevel, ")\n" ); // Close sheet instances token.
1482  }
1483 
1484  if( aSymbols.size() )
1485  {
1486  m_out->Print( 0, "\n" );
1487  m_out->Print( aNestLevel, "(symbol_instances\n" );
1488 
1489  for( const SYMBOL_INSTANCE_REFERENCE& instance : aSymbols )
1490  {
1491  m_out->Print( aNestLevel + 1, "(path %s\n",
1492  m_out->Quotew( instance.m_Path.AsString() ).c_str() );
1493  m_out->Print( aNestLevel + 2, "(reference %s) (unit %d) (value %s) (footprint %s)\n",
1494  m_out->Quotew( instance.m_Reference ).c_str(),
1495  instance.m_Unit,
1496  m_out->Quotew( instance.m_Value ).c_str(),
1497  m_out->Quotew( instance.m_Footprint ).c_str() );
1498  m_out->Print( aNestLevel + 1, ")\n" );
1499  }
1500 
1501  m_out->Print( aNestLevel, ")\n" ); // Close symbol instances token.
1502  }
1503 }
1504 
1505 
1506 int SCH_SEXPR_PLUGIN_CACHE::m_modHash = 1; // starts at 1 and goes up
1507 
1508 
1509 SCH_SEXPR_PLUGIN_CACHE::SCH_SEXPR_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) :
1510  m_fileName( aFullPathAndFileName ),
1511  m_libFileName( aFullPathAndFileName ),
1512  m_isWritable( true ),
1513  m_isModified( false )
1514 {
1515  m_versionMajor = -1;
1517 }
1518 
1519 
1521 {
1522  // When the cache is destroyed, all of the alias objects on the heap should be deleted.
1523  for( LIB_SYMBOL_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); ++it )
1524  delete it->second;
1525 
1526  m_symbols.clear();
1527 }
1528 
1529 
1530 // If m_libFileName is a symlink follow it to the real source file
1532 {
1533  wxFileName fn( m_libFileName );
1535  return fn;
1536 }
1537 
1538 
1540 {
1541  wxFileName fn = GetRealFile();
1542 
1543  // update the writable flag while we have a wxFileName, in a network this
1544  // is possibly quite dynamic anyway.
1545  m_isWritable = fn.IsFileWritable();
1546 
1547  return fn.GetModificationTime();
1548 }
1549 
1550 
1551 bool SCH_SEXPR_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const
1552 {
1553  return m_fileName == aFullPathAndFileName;
1554 }
1555 
1556 
1558 {
1559  wxFileName fn = GetRealFile();
1560 
1561  if( m_fileModTime.IsValid() && fn.IsOk() && fn.FileExists() )
1562  return fn.GetModificationTime() != m_fileModTime;
1563 
1564  return false;
1565 }
1566 
1567 
1569 {
1570  wxCHECK_MSG( aSymbol != nullptr, nullptr, "NULL pointer cannot be removed from library." );
1571 
1572  LIB_SYMBOL* firstChild = nullptr;
1573  LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbol->GetName() );
1574 
1575  if( it == m_symbols.end() )
1576  return nullptr;
1577 
1578  // If the entry pointer doesn't match the name it is mapped to in the library, we
1579  // have done something terribly wrong.
1580  wxCHECK_MSG( *it->second == aSymbol, nullptr,
1581  "Pointer mismatch while attempting to remove alias entry <" + aSymbol->GetName() +
1582  "> from library cache <" + m_libFileName.GetName() + ">." );
1583 
1584  // If the symbol is a root symbol used by other symbols find the first alias that uses
1585  // the root symbol and make it the new root.
1586  if( aSymbol->IsRoot() )
1587  {
1588  for( auto entry : m_symbols )
1589  {
1590  if( entry.second->IsAlias()
1591  && entry.second->GetParent().lock() == aSymbol->SharedPtr() )
1592  {
1593  firstChild = entry.second;
1594  break;
1595  }
1596  }
1597 
1598  if( firstChild )
1599  {
1600  for( LIB_ITEM& drawItem : aSymbol->GetDrawItems() )
1601  {
1602  if( drawItem.Type() == LIB_FIELD_T )
1603  {
1604  LIB_FIELD& field = static_cast<LIB_FIELD&>( drawItem );
1605 
1606  if( firstChild->FindField( field.GetCanonicalName() ) )
1607  continue;
1608  }
1609 
1610  LIB_ITEM* newItem = (LIB_ITEM*) drawItem.Clone();
1611  drawItem.SetParent( firstChild );
1612  firstChild->AddDrawItem( newItem );
1613  }
1614 
1615  // Reparent the remaining aliases.
1616  for( auto entry : m_symbols )
1617  {
1618  if( entry.second->IsAlias()
1619  && entry.second->GetParent().lock() == aSymbol->SharedPtr() )
1620  entry.second->SetParent( firstChild );
1621  }
1622  }
1623  }
1624 
1625  m_symbols.erase( it );
1626  delete aSymbol;
1627  m_isModified = true;
1628  ++m_modHash;
1629  return firstChild;
1630 }
1631 
1632 
1634 {
1635  // aSymbol is cloned in SYMBOL_LIB::AddSymbol(). The cache takes ownership of aSymbol.
1636  wxString name = aSymbol->GetName();
1637  LIB_SYMBOL_MAP::iterator it = m_symbols.find( name );
1638 
1639  if( it != m_symbols.end() )
1640  {
1641  removeSymbol( it->second );
1642  }
1643 
1644  m_symbols[ name ] = const_cast< LIB_SYMBOL* >( aSymbol );
1645  m_isModified = true;
1646  ++m_modHash;
1647 }
1648 
1649 
1651 {
1652  if( !m_libFileName.FileExists() )
1653  {
1654  THROW_IO_ERROR( wxString::Format( _( "Library file '%s' not found." ),
1655  m_libFileName.GetFullPath() ) );
1656  }
1657 
1658  wxCHECK_RET( m_libFileName.IsAbsolute(),
1659  wxString::Format( "Cannot use relative file paths in sexpr plugin to "
1660  "open library '%s'.", m_libFileName.GetFullPath() ) );
1661 
1662  // The current locale must use period as the decimal point.
1663  wxCHECK2( wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) == ".",
1664  LOCALE_IO toggle );
1665 
1666  wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library file '%s'",
1667  m_libFileName.GetFullPath() );
1668 
1669  FILE_LINE_READER reader( m_libFileName.GetFullPath() );
1670 
1671  SCH_SEXPR_PARSER parser( &reader );
1672 
1673  parser.ParseLib( m_symbols );
1674  ++m_modHash;
1675 
1676  // Remember the file modification time of library file when the
1677  // cache snapshot was made, so that in a networked environment we will
1678  // reload the cache as needed.
1680 }
1681 
1682 
1684 {
1685  if( !m_isModified )
1686  return;
1687 
1688  LOCALE_IO toggle; // toggles on, then off, the C locale.
1689 
1690  // Write through symlinks, don't replace them.
1691  wxFileName fn = GetRealFile();
1692 
1693  auto formatter = std::make_unique<FILE_OUTPUTFORMATTER>( fn.GetFullPath() );
1694 
1695  formatter->Print( 0, "(kicad_symbol_lib (version %d) (generator kicad_symbol_editor)\n",
1697 
1698  for( auto parent : m_symbols )
1699  {
1700  // Save the root symbol first so alias can inherit from them.
1701  if( parent.second->IsRoot() )
1702  {
1703  SaveSymbol( parent.second, *formatter.get(), 1 );
1704 
1705  // Save all of the aliases associated with the current root symbol.
1706  for( auto alias : m_symbols )
1707  {
1708  if( !alias.second->IsAlias() )
1709  continue;
1710 
1711  std::shared_ptr<LIB_SYMBOL> aliasParent = alias.second->GetParent().lock();
1712 
1713  if( aliasParent.get() != parent.second )
1714  continue;
1715 
1716  SaveSymbol( alias.second, *formatter.get(), 1 );
1717  }
1718  }
1719  }
1720 
1721  formatter->Print( 0, ")\n" );
1722 
1723  formatter.reset();
1724 
1725  m_fileModTime = fn.GetModificationTime();
1726  m_isModified = false;
1727 }
1728 
1729 
1731  int aNestLevel, const wxString& aLibName )
1732 {
1733  wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
1734 
1735  // The current locale must use period as the decimal point.
1736  wxCHECK2( wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) == ".",
1737  LOCALE_IO toggle );
1738 
1739  int nextFreeFieldId = MANDATORY_FIELDS;
1740  std::vector<LIB_FIELD*> fields;
1741  std::string name = aFormatter.Quotew( aSymbol->GetLibId().Format().wx_str() );
1742  std::string unitName = aSymbol->GetLibId().GetLibItemName();
1743 
1744  if( !aLibName.IsEmpty() )
1745  {
1746  name = aFormatter.Quotew( aLibName );
1747 
1748  LIB_ID unitId;
1749 
1750  wxCHECK2( unitId.Parse( aLibName ) < 0, /* do nothing */ );
1751 
1752  unitName = unitId.GetLibItemName();
1753  }
1754 
1755  if( aSymbol->IsRoot() )
1756  {
1757  aFormatter.Print( aNestLevel, "(symbol %s", name.c_str() );
1758 
1759  if( aSymbol->IsPower() )
1760  aFormatter.Print( 0, " (power)" );
1761 
1762  // TODO: add uuid token here.
1763 
1764  // TODO: add anchor position token here.
1765 
1766  if( !aSymbol->ShowPinNumbers() )
1767  aFormatter.Print( 0, " (pin_numbers hide)" );
1768 
1769  if( aSymbol->GetPinNameOffset() != Mils2iu( DEFAULT_PIN_NAME_OFFSET )
1770  || !aSymbol->ShowPinNames() )
1771  {
1772  aFormatter.Print( 0, " (pin_names" );
1773 
1774  if( aSymbol->GetPinNameOffset() != Mils2iu( DEFAULT_PIN_NAME_OFFSET ) )
1775  aFormatter.Print( 0, " (offset %s)",
1776  FormatInternalUnits( aSymbol->GetPinNameOffset() ).c_str() );
1777 
1778  if( !aSymbol->ShowPinNames() )
1779  aFormatter.Print( 0, " hide" );
1780 
1781  aFormatter.Print( 0, ")" );
1782  }
1783 
1784  aFormatter.Print( 0, " (in_bom %s)", ( aSymbol->GetIncludeInBom() ) ? "yes" : "no" );
1785  aFormatter.Print( 0, " (on_board %s)", ( aSymbol->GetIncludeOnBoard() ) ? "yes" : "no" );
1786 
1787  // TODO: add atomic token here.
1788 
1789  // TODO: add required token here."
1790 
1791  aFormatter.Print( 0, "\n" );
1792 
1793  aSymbol->GetFields( fields );
1794 
1795  for( LIB_FIELD* field : fields )
1796  saveField( field, aFormatter, nextFreeFieldId, aNestLevel + 1 );
1797 
1798  // @todo At some point in the future the lock status (all units interchangeable) should
1799  // be set deterministically. For now a custom lock property is used to preserve the
1800  // locked flag state.
1801  if( aSymbol->UnitsLocked() )
1802  {
1803  LIB_FIELD locked( -1, "ki_locked" );
1804  saveField( &locked, aFormatter, nextFreeFieldId, aNestLevel + 1 );
1805  }
1806 
1807  saveDcmInfoAsFields( aSymbol, aFormatter, nextFreeFieldId, aNestLevel );
1808 
1809  // Save the draw items grouped by units.
1810  std::vector<LIB_SYMBOL_UNITS> units = aSymbol->GetUnitDrawItems();
1811  std::sort( units.begin(), units.end(),
1812  []( const LIB_SYMBOL_UNITS& a, const LIB_SYMBOL_UNITS& b )
1813  {
1814  if( a.m_unit == b.m_unit )
1815  return a.m_convert < b.m_convert;
1816 
1817  return a.m_unit < b.m_unit;
1818  } );
1819 
1820  for( auto unit : units )
1821  {
1822  // Add quotes and escape chars like ") to the UTF8 unitName string
1823  name = aFormatter.Quotes( unitName );
1824  name.pop_back(); // Remove last char: the quote ending the string.
1825 
1826  aFormatter.Print( aNestLevel + 1, "(symbol %s_%d_%d\"\n",
1827  name.c_str(), unit.m_unit, unit.m_convert );
1828 
1829  for( auto item : unit.m_items )
1830  saveSymbolDrawItem( item, aFormatter, aNestLevel + 2 );
1831 
1832  aFormatter.Print( aNestLevel + 1, ")\n" );
1833  }
1834  }
1835  else
1836  {
1837  std::shared_ptr<LIB_SYMBOL> parent = aSymbol->GetParent().lock();
1838 
1839  wxASSERT( parent );
1840 
1841  aFormatter.Print( aNestLevel, "(symbol %s (extends %s)\n",
1842  name.c_str(),
1843  aFormatter.Quotew( parent->GetName() ).c_str() );
1844 
1845  aSymbol->GetFields( fields );
1846 
1847  for( LIB_FIELD* field : fields )
1848  saveField( field, aFormatter, nextFreeFieldId, aNestLevel + 1 );
1849 
1850  saveDcmInfoAsFields( aSymbol, aFormatter, nextFreeFieldId, aNestLevel );
1851  }
1852 
1853  aFormatter.Print( aNestLevel, ")\n" );
1854 }
1855 
1856 
1858  int& aNextFreeFieldId, int aNestLevel )
1859 {
1860  wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
1861 
1862  if( !aSymbol->GetKeyWords().IsEmpty() )
1863  {
1864  LIB_FIELD keywords( -1, wxString( "ki_keywords" ) );
1865  keywords.SetVisible( false );
1866  keywords.SetText( aSymbol->GetKeyWords() );
1867  saveField( &keywords, aFormatter, aNextFreeFieldId, aNestLevel + 1 );
1868  }
1869 
1870  if( !aSymbol->GetDescription().IsEmpty() )
1871  {
1872  LIB_FIELD description( -1, wxString( "ki_description" ) );
1873  description.SetVisible( false );
1874  description.SetText( aSymbol->GetDescription() );
1875  saveField( &description, aFormatter, aNextFreeFieldId, aNestLevel + 1 );
1876  }
1877 
1878  wxArrayString fpFilters = aSymbol->GetFPFilters();
1879 
1880  if( !fpFilters.IsEmpty() )
1881  {
1882  wxString tmp;
1883 
1884  for( auto filter : fpFilters )
1885  {
1886  // Spaces are not handled in fp filter names so escape spaces if any
1887  wxString curr_filter = EscapeString( filter, ESCAPE_CONTEXT::CTX_NO_SPACE );
1888 
1889  if( tmp.IsEmpty() )
1890  tmp = curr_filter;
1891  else
1892  tmp += " " + curr_filter;
1893  }
1894 
1895  LIB_FIELD description( -1, wxString( "ki_fp_filters" ) );
1896  description.SetVisible( false );
1897  description.SetText( tmp );
1898  saveField( &description, aFormatter, aNextFreeFieldId, aNestLevel + 1 );
1899  }
1900 }
1901 
1902 
1904  int aNestLevel )
1905 {
1906  wxCHECK_RET( aItem, "Invalid LIB_ITEM pointer." );
1907 
1908  switch( aItem->Type() )
1909  {
1910  case LIB_SHAPE_T:
1911  {
1912  LIB_SHAPE* shape = static_cast<LIB_SHAPE*>( aItem );
1913  STROKE_PARAMS stroke;
1914  FILL_T fillMode = shape->GetFillType();
1915 
1916  stroke.SetWidth( shape->GetWidth() );
1917 
1918  switch( shape->GetShape() )
1919  {
1920  case SHAPE_T::ARC:
1921  int x1;
1922  int x2;
1923 
1924  shape->CalcArcAngles( x1, x2 );
1925 
1926  formatArc( &aFormatter, aNestLevel, shape, x1, x2, stroke, fillMode, COLOR4D() );
1927  break;
1928 
1929  case SHAPE_T::CIRCLE:
1930  formatCircle( &aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
1931  break;
1932 
1933  case SHAPE_T::RECT:
1934  formatRect( &aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
1935  break;
1936 
1937  case SHAPE_T::BEZIER:
1938  formatBezier(&aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
1939  break;
1940 
1941  case SHAPE_T::POLY:
1942  formatPoly( &aFormatter, aNestLevel, shape, stroke, fillMode, COLOR4D() );
1943  break;
1944 
1945  default:
1946  UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
1947  }
1948 
1949  break;
1950  }
1951 
1952  case LIB_PIN_T:
1953  savePin( (LIB_PIN* ) aItem, aFormatter, aNestLevel );
1954  break;
1955 
1956  case LIB_TEXT_T:
1957  saveText( ( LIB_TEXT* ) aItem, aFormatter, aNestLevel );
1958  break;
1959 
1960  default:
1961  UNIMPLEMENTED_FOR( aItem->GetClass() );
1962  }
1963 }
1964 
1965 
1967  int& aNextFreeFieldId, int aNestLevel )
1968 {
1969  wxCHECK_RET( aField && aField->Type() == LIB_FIELD_T, "Invalid LIB_FIELD object." );
1970 
1971  wxString fieldName = aField->GetName();
1972 
1973  if( aField->GetId() == -1 /* undefined ID */ )
1974  {
1975  aField->SetId( aNextFreeFieldId );
1976  aNextFreeFieldId += 1;
1977  }
1978  else if( aField->GetId() >= aNextFreeFieldId )
1979  {
1980  aNextFreeFieldId = aField->GetId() + 1;
1981  }
1982 
1983  if( aField->GetId() >= 0 && aField->GetId() < MANDATORY_FIELDS )
1984  fieldName = TEMPLATE_FIELDNAME::GetDefaultFieldName( aField->GetId(), false );
1985 
1986  aFormatter.Print( aNestLevel, "(property %s %s (id %d) (at %s %s %g)\n",
1987  aFormatter.Quotew( fieldName ).c_str(),
1988  aFormatter.Quotew( aField->GetText() ).c_str(),
1989  aField->GetId(),
1990  FormatInternalUnits( aField->GetPosition().x ).c_str(),
1991  FormatInternalUnits( aField->GetPosition().y ).c_str(),
1992  static_cast<double>( aField->GetTextAngle() ) / 10.0 );
1993 
1994  aField->Format( &aFormatter, aNestLevel, 0 );
1995  aFormatter.Print( aNestLevel, ")\n" );
1996 }
1997 
1998 
1999 void SCH_SEXPR_PLUGIN_CACHE::savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel )
2000 {
2001  wxCHECK_RET( aPin && aPin->Type() == LIB_PIN_T, "Invalid LIB_PIN object." );
2002 
2003  aPin->ClearFlags( IS_CHANGED );
2004 
2005  aFormatter.Print( aNestLevel, "(pin %s %s (at %s %s %s) (length %s)",
2007  getPinShapeToken( aPin->GetShape() ),
2008  FormatInternalUnits( aPin->GetPosition().x ).c_str(),
2009  FormatInternalUnits( aPin->GetPosition().y ).c_str(),
2010  FormatAngle( getPinAngle( aPin->GetOrientation() ) * 10.0 ).c_str(),
2011  FormatInternalUnits( aPin->GetLength() ).c_str() );
2012 
2013  if( !aPin->IsVisible() )
2014  aFormatter.Print( 0, " hide\n" );
2015  else
2016  aFormatter.Print( 0, "\n" );
2017 
2018  // This follows the EDA_TEXT effects formatting for future expansion.
2019  aFormatter.Print( aNestLevel + 1, "(name %s (effects (font (size %s %s))))\n",
2020  aFormatter.Quotew( aPin->GetName() ).c_str(),
2021  FormatInternalUnits( aPin->GetNameTextSize() ).c_str(),
2022  FormatInternalUnits( aPin->GetNameTextSize() ).c_str() );
2023 
2024  aFormatter.Print( aNestLevel + 1, "(number %s (effects (font (size %s %s))))\n",
2025  aFormatter.Quotew( aPin->GetNumber() ).c_str(),
2026  FormatInternalUnits( aPin->GetNumberTextSize() ).c_str(),
2027  FormatInternalUnits( aPin->GetNumberTextSize() ).c_str() );
2028 
2029 
2030  for( const std::pair<const wxString, LIB_PIN::ALT>& alt : aPin->GetAlternates() )
2031  {
2032  aFormatter.Print( aNestLevel + 1, "(alternate %s %s %s)\n",
2033  aFormatter.Quotew( alt.second.m_Name ).c_str(),
2034  getPinElectricalTypeToken( alt.second.m_Type ),
2035  getPinShapeToken( alt.second.m_Shape ) );
2036  }
2037 
2038  aFormatter.Print( aNestLevel, ")\n" );
2039 }
2040 
2041 
2043  int aNestLevel )
2044 {
2045  wxCHECK_RET( aText && aText->Type() == LIB_TEXT_T, "Invalid LIB_TEXT object." );
2046 
2047  aFormatter.Print( aNestLevel, "(text %s (at %s %s %g)\n",
2048  aFormatter.Quotew( aText->GetText() ).c_str(),
2049  FormatInternalUnits( aText->GetPosition().x ).c_str(),
2050  FormatInternalUnits( aText->GetPosition().y ).c_str(),
2051  aText->GetTextAngle() );
2052  aText->Format( &aFormatter, aNestLevel, 0 );
2053  aFormatter.Print( aNestLevel, ")\n" );
2054 }
2055 
2056 
2057 void SCH_SEXPR_PLUGIN_CACHE::DeleteSymbol( const wxString& aSymbolName )
2058 {
2059  LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbolName );
2060 
2061  if( it == m_symbols.end() )
2062  THROW_IO_ERROR( wxString::Format( _( "library %s does not contain a symbol named %s" ),
2063  m_libFileName.GetFullName(), aSymbolName ) );
2064 
2065  LIB_SYMBOL* symbol = it->second;
2066 
2067  if( symbol->IsRoot() )
2068  {
2069  LIB_SYMBOL* rootSymbol = symbol;
2070 
2071  // Remove the root symbol and all its children.
2072  m_symbols.erase( it );
2073 
2074  LIB_SYMBOL_MAP::iterator it1 = m_symbols.begin();
2075 
2076  while( it1 != m_symbols.end() )
2077  {
2078  if( it1->second->IsAlias()
2079  && it1->second->GetParent().lock() == rootSymbol->SharedPtr() )
2080  {
2081  delete it1->second;
2082  it1 = m_symbols.erase( it1 );
2083  }
2084  else
2085  {
2086  it1++;
2087  }
2088  }
2089 
2090  delete rootSymbol;
2091  }
2092  else
2093  {
2094  // Just remove the alias.
2095  m_symbols.erase( it );
2096  delete symbol;
2097  }
2098 
2099  ++m_modHash;
2100  m_isModified = true;
2101 }
2102 
2103 
2104 void SCH_SEXPR_PLUGIN::cacheLib( const wxString& aLibraryFileName, const PROPERTIES* aProperties )
2105 {
2106  if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() )
2107  {
2108  // a spectacular episode in memory management:
2109  delete m_cache;
2110  m_cache = new SCH_SEXPR_PLUGIN_CACHE( aLibraryFileName );
2111 
2112  // Because m_cache is rebuilt, increment SYMBOL_LIBS::s_modify_generation
2113  // to modify the hash value that indicate symbol to symbol links
2114  // must be updated.
2116 
2117  if( !isBuffering( aProperties ) )
2118  m_cache->Load();
2119  }
2120 }
2121 
2122 
2123 bool SCH_SEXPR_PLUGIN::isBuffering( const PROPERTIES* aProperties )
2124 {
2125  return ( aProperties && aProperties->Exists( SCH_SEXPR_PLUGIN::PropBuffering ) );
2126 }
2127 
2128 
2130 {
2131  if( m_cache )
2132  return m_cache->GetModifyHash();
2133 
2134  // If the cache hasn't been loaded, it hasn't been modified.
2135  return 0;
2136 }
2137 
2138 
2139 void SCH_SEXPR_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
2140  const wxString& aLibraryPath,
2141  const PROPERTIES* aProperties )
2142 {
2143  LOCALE_IO toggle; // toggles on, then off, the C locale.
2144 
2145  bool powerSymbolsOnly = ( aProperties &&
2146  aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
2147 
2148  cacheLib( aLibraryPath, aProperties );
2149 
2150  const LIB_SYMBOL_MAP& symbols = m_cache->m_symbols;
2151 
2152  for( LIB_SYMBOL_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it )
2153  {
2154  if( !powerSymbolsOnly || it->second->IsPower() )
2155  aSymbolNameList.Add( it->first );
2156  }
2157 }
2158 
2159 
2160 void SCH_SEXPR_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
2161  const wxString& aLibraryPath,
2162  const PROPERTIES* aProperties )
2163 {
2164  LOCALE_IO toggle; // toggles on, then off, the C locale.
2165 
2166  bool powerSymbolsOnly = ( aProperties &&
2167  aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
2168 
2169  cacheLib( aLibraryPath, aProperties );
2170 
2171  const LIB_SYMBOL_MAP& symbols = m_cache->m_symbols;
2172 
2173  for( LIB_SYMBOL_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it )
2174  {
2175  if( !powerSymbolsOnly || it->second->IsPower() )
2176  aSymbolList.push_back( it->second );
2177  }
2178 }
2179 
2180 
2181 LIB_SYMBOL* SCH_SEXPR_PLUGIN::LoadSymbol( const wxString& aLibraryPath, const wxString& aSymbolName,
2182  const PROPERTIES* aProperties )
2183 {
2184  LOCALE_IO toggle; // toggles on, then off, the C locale.
2185 
2186  cacheLib( aLibraryPath, aProperties );
2187 
2188  LIB_SYMBOL_MAP::const_iterator it = m_cache->m_symbols.find( aSymbolName );
2189 
2190  if( it == m_cache->m_symbols.end() )
2191  return nullptr;
2192 
2193  return it->second;
2194 }
2195 
2196 
2197 void SCH_SEXPR_PLUGIN::SaveSymbol( const wxString& aLibraryPath, const LIB_SYMBOL* aSymbol,
2198  const PROPERTIES* aProperties )
2199 {
2200  LOCALE_IO toggle; // toggles on, then off, the C locale.
2201 
2202  cacheLib( aLibraryPath, aProperties );
2203 
2204  m_cache->AddSymbol( aSymbol );
2205 
2206  if( !isBuffering( aProperties ) )
2207  m_cache->Save();
2208 }
2209 
2210 
2211 void SCH_SEXPR_PLUGIN::DeleteSymbol( const wxString& aLibraryPath, const wxString& aSymbolName,
2212  const PROPERTIES* aProperties )
2213 {
2214  LOCALE_IO toggle; // toggles on, then off, the C locale.
2215 
2216  cacheLib( aLibraryPath, aProperties );
2217 
2218  m_cache->DeleteSymbol( aSymbolName );
2219 
2220  if( !isBuffering( aProperties ) )
2221  m_cache->Save();
2222 }
2223 
2224 
2225 void SCH_SEXPR_PLUGIN::CreateSymbolLib( const wxString& aLibraryPath,
2226  const PROPERTIES* aProperties )
2227 {
2228  if( wxFileExists( aLibraryPath ) )
2229  {
2230  THROW_IO_ERROR( wxString::Format( _( "Symbol library '%s' already exists." ),
2231  aLibraryPath.GetData() ) );
2232  }
2233 
2234  LOCALE_IO toggle;
2235 
2236  delete m_cache;
2237  m_cache = new SCH_SEXPR_PLUGIN_CACHE( aLibraryPath );
2238  m_cache->SetModified();
2239  m_cache->Save();
2240  m_cache->Load(); // update m_writable and m_mod_time
2241 }
2242 
2243 
2244 bool SCH_SEXPR_PLUGIN::DeleteSymbolLib( const wxString& aLibraryPath,
2245  const PROPERTIES* aProperties )
2246 {
2247  wxFileName fn = aLibraryPath;
2248 
2249  if( !fn.FileExists() )
2250  return false;
2251 
2252  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
2253  // we don't want that. we want bare metal portability with no UI here.
2254  if( wxRemove( aLibraryPath ) )
2255  {
2256  THROW_IO_ERROR( wxString::Format( _( "Symbol library '%s' cannot be deleted." ),
2257  aLibraryPath.GetData() ) );
2258  }
2259 
2260  if( m_cache && m_cache->IsFile( aLibraryPath ) )
2261  {
2262  delete m_cache;
2263  m_cache = nullptr;
2264  }
2265 
2266  return true;
2267 }
2268 
2269 
2270 void SCH_SEXPR_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const PROPERTIES* aProperties )
2271 {
2272  if( !m_cache )
2273  m_cache = new SCH_SEXPR_PLUGIN_CACHE( aLibraryPath );
2274 
2275  wxString oldFileName = m_cache->GetFileName();
2276 
2277  if( !m_cache->IsFile( aLibraryPath ) )
2278  {
2279  m_cache->SetFileName( aLibraryPath );
2280  }
2281 
2282  // This is a forced save.
2283  m_cache->SetModified();
2284  m_cache->Save();
2285  m_cache->SetFileName( oldFileName );
2286 }
2287 
2288 
2289 bool SCH_SEXPR_PLUGIN::CheckHeader( const wxString& aFileName )
2290 {
2291  // Open file and check first line
2292  wxTextFile tempFile;
2293 
2294  tempFile.Open( aFileName );
2295  wxString firstline;
2296  // read the first line
2297  firstline = tempFile.GetFirstLine();
2298  tempFile.Close();
2299 
2300  return firstline.StartsWith( "EESchema" );
2301 }
2302 
2303 
2304 bool SCH_SEXPR_PLUGIN::IsSymbolLibWritable( const wxString& aLibraryPath )
2305 {
2306  wxFileName fn( aLibraryPath );
2307 
2308  return ( fn.FileExists() && fn.IsFileWritable() ) || fn.IsDirWritable();
2309 }
2310 
2311 
2313 {
2314  LOCALE_IO toggle; // toggles on, then off, the C locale.
2315  LIB_SYMBOL_MAP map;
2316  SCH_SEXPR_PARSER parser( &aReader );
2317 
2318  parser.NeedLEFT();
2319  parser.NextTok();
2320 
2321  return parser.ParseSymbol( map, aFileVersion );
2322 }
2323 
2324 
2326 {
2327 
2328  LOCALE_IO toggle; // toggles on, then off, the C locale.
2329  SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( symbol, formatter );
2330 }
2331 
2332 
2333 const char* SCH_SEXPR_PLUGIN::PropBuffering = "buffering";
void CalcArcAngles(int &aStartAngle, int &aEndAngle) const
Definition: lib_shape.cpp:411
power input (GND, VCC for ICs). Must be connected to a power output.
static void saveField(LIB_FIELD *aField, OUTPUTFORMATTER &aFormatter, int &aNextFreeFieldId, int aNestLevel)
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
LIB_FIELD * FindField(const wxString &aFieldName)
Find a field within this symbol matching aFieldName and returns it or NULL if not found.
Definition: lib_symbol.cpp:978
SCH_LIB_TYPE
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:216
SCHEMATIC * m_schematic
#define IS_CHANGED
Item was edited, and modified.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
LIB_SYMBOL_REF & GetParent()
Definition: lib_symbol.h:124
int GetPinNameOffset() const
Definition: lib_symbol.h:561
int m_version
Version of file being loaded.
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
Symbol map used by symbol library object.
LIB_SYMBOL_SPTR SharedPtr() const
Definition: lib_symbol.h:107
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:80
void SetWidth(int aWidth)
Definition: sch_item.h:153
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:214
int GetWidth() const
Definition: eda_shape.h:89
bool m_CompactSave
Save files in compact display mode When is is not specified, points are written one per line.
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:636
#define DEFAULT_SIZE_TEXT
This is the "default-of-the-default" hardcoded text size; individual application define their own def...
Definition: eda_text.h:101
KIID niluuid(0)
void saveLine(SCH_LINE *aLine, int aNestLevel)
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
static void formatFill(OUTPUTFORMATTER *aFormatter, int aNestLevel, FILL_T aFillMode, const COLOR4D &aFillColor)
Fill token formatting helper.
void saveNoConnect(SCH_NO_CONNECT *aNoConnect, int aNestLevel)
Schematic and symbol library s-expression file format parser definitions.
static const char * getTextTypeToken(KICAD_T aType)
void Format(SCH_SHEET *aSheet)
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
SCH_SEXPR_PLUGIN_CACHE(const wxString &aLibraryPath)
SCH_SCREEN * GetScreen()
Definition: ee_selection.h:51
wxPoint GetStartPoint() const
Definition: sch_line.h:90
std::vector< SYMBOL_INSTANCE_REFERENCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition: sch_screen.h:546
void Save()
Save the entire library to file m_libFileName;.
Holds all the data relating to one schematic.
Definition: schematic.h:59
wxString m_error
For throwing exceptions or errors on partial loads.
The first 2 are mandatory, and must be instantiated in SCH_SHEET.
Definition: sch_sheet.h:47
wxPoint GetPosition() const override
Definition: lib_field.h:157
static void saveText(LIB_TEXT *aText, OUTPUTFORMATTER &aFormatter, int aNestLevel=0)
wxPoint GetPosition() const override
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:97
void saveJunction(SCH_JUNCTION *aJunction, int aNestLevel)
wxString GetName() const override
Definition: lib_symbol.h:133
Define a symbol library graphical text item.
Definition: lib_text.h:39
int GetOrientation() const
Definition: lib_pin.h:75
virtual STROKE_PARAMS GetStroke() const override
Definition: sch_bus_entry.h:69
GRAPHIC_PINSHAPE GetShape() const
Definition: lib_pin.h:78
wxString GetFileName() const
void DeleteSymbol(const wxString &aLibraryPath, const wxString &aSymbolName, const PROPERTIES *aProperties=nullptr) override
Delete the entire LIB_SYMBOL associated with aAliasName from the library aLibraryPath.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
static void formatPoly(OUTPUTFORMATTER *aFormatter, int aNestLevel, EDA_SHAPE *aPolyLine, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, KIID aUuid=niluuid)
wxPoint GetPosition() const override
Definition: sch_field.cpp:747
std::stack< wxString > m_currentPath
Stack to maintain nested sheet paths.
wxPoint GetPosition() const override
Definition: lib_text.h:91
SCH_SEXPR_PLUGIN_CACHE * m_cache
Field object used in symbol libraries.
Definition: lib_field.h:59
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:156
const wxChar *const traceSchLegacyPlugin
Flag to enable legacy schematic plugin debug output.
double GetScale() const
Definition: bitmap_base.h:79
pin for passive symbols: must be connected, and can be connected to any pin
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition: schematic.h:108
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:711
wxImage * GetImageData()
Definition: bitmap_base.h:70
bool ShowPinNumbers() const
Definition: lib_symbol.h:577
void SetFileName(const wxString &aFileName)
SCH_SHEET * m_rootSheet
The root sheet of the schematic being loaded.
void Save(const wxString &aFileName, SCH_SHEET *aSheet, SCHEMATIC *aSchematic, const PROPERTIES *aProperties=nullptr) override
Write aSchematic to a storage file in a format that this SCH_PLUGIN implementation knows about,...
LIB_SYMBOL * ParseSymbol(LIB_SYMBOL_MAP &aSymbolLibMap, int aFileVersion=SEXPR_SYMBOL_LIB_FILE_VERSION)
bool GetIncludeInBom() const
Definition: sch_symbol.h:662
std::vector< SYMBOL_INSTANCE_REFERENCE > GetSymbolInstances() const
double GetTextAngle() const
Definition: eda_text.h:195
bool GetIncludeOnBoard() const
Definition: lib_symbol.h:593
int GetId() const
Definition: sch_field.h:113
static void formatCircle(OUTPUTFORMATTER *aFormatter, int aNestLevel, EDA_SHAPE *aCircle, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, KIID aUuid=niluuid)
unknown electrical properties: creates always a warning when connected
void saveText(SCH_TEXT *aText, int aNestLevel)
void saveInstances(const std::vector< SCH_SHEET_INSTANCE > &aSheets, const std::vector< SYMBOL_INSTANCE_REFERENCE > &aSymbols, int aNestLevel)
wxString AsString() const
Definition: kiid.cpp:218
const TITLE_BLOCK & GetTitleBlock() const
Definition: sch_screen.h:156
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:309
int GetDiameter() const
Definition: sch_junction.h:99
double g
Green component.
Definition: color4d.h:378
static const char * emptyString
KIGFX::COLOR4D GetBorderColor() const
Definition: sch_sheet.h:111
bool IsPower() const
Definition: lib_symbol.cpp:408
void DeleteSymbol(const wxString &aName)
virtual std::string Quotes(const std::string &aWrapee) const
Check aWrapee input string for a need to be quoted (e.g.
Definition: richio.cpp:455
void saveField(SCH_FIELD *aField, int aNestLevel)
void AddSymbol(const LIB_SYMBOL *aSymbol)
Definition: lib_pin.h:48
int GetId() const
Definition: lib_field.h:115
void SetFileExists(bool aFileExists)
Definition: sch_screen.h:150
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
static float getPinAngle(int aOrientation)
void SaveSymbol(const wxString &aLibraryPath, const LIB_SYMBOL *aSymbol, const PROPERTIES *aProperties=nullptr) override
Write aSymbol to an existing library located at aLibraryPath.
wxPoint GetPosition() const override
Definition: sch_bitmap.h:136
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
GRAPHIC_PINSHAPE
Definition: pin_type.h:55
void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControlBits) const
Output the page class to aFormatter in s-expression form.
Definition: page_info.cpp:271
LIB_SYMBOL * removeSymbol(LIB_SYMBOL *aAlias)
A name/value tuple with unique names and optional values.
Definition: properties.h:33
std::string toUTFTildaText(const wxString &txt)
Convert a wxString to UTF8 and replace any control characters with a ~, where a control character is ...
Definition: sch_symbol.cpp:46
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:103
Define a library symbol object.
Definition: lib_symbol.h:96
const PAGE_INFO & GetPageSettings() const
Definition: sch_screen.h:132
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:122
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:116
FIELDS_AUTOPLACED GetFieldsAutoplaced() const
Return whether the fields have been automatically placed.
Definition: sch_item.h:418
wxString GetName(bool aUseDefaultName=true) const
Return the field name.
Definition: lib_field.cpp:362
FILL_T
Definition: eda_shape.h:53
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition: eda_item.cpp:83
The base class for drawable items used by schematic library symbols.
Definition: lib_item.h:61
double b
Blue component.
Definition: color4d.h:379
void GetFields(std::vector< LIB_FIELD * > &aList)
Return a list of fields within this symbol.
Definition: lib_symbol.cpp:930
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:94
int GetTextHeight() const
Definition: eda_text.h:265
KIGFX::COLOR4D GetBackgroundColor() const
Definition: sch_sheet.h:114
Base class for a bus or wire entry.
Definition: sch_bus_entry.h:42
const std::vector< SYMBOL_INSTANCE_REFERENCE > & GetInstanceReferences()
Definition: sch_symbol.h:123
static const char * getPinShapeToken(GRAPHIC_PINSHAPE aShape)
void Rewind()
Rewind the file and resets the line number back to zero.
Definition: richio.h:222
std::vector< SCH_FIELD > & GetFields()
Definition: sch_sheet.h:88
A cache assistant for the symbol library portion of the SCH_PLUGIN API, and only for the SCH_SEXPR_PL...
const wxPoint & GetBezierC1() const
Definition: eda_shape.h:145
void SetFileReadOnly(bool aIsReadOnly)
Definition: sch_screen.h:147
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
COLOR4D GetColor() const
Definition: sch_junction.h:104
not internally connected (may be connected to anything)
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:206
Definition: kiid.h:44
void saveBusEntry(SCH_BUS_ENTRY_BASE *aBusEntry, int aNestLevel)
void SetModified(bool aModified=true)
std::string FormatAngle(double aAngle)
Function FormatAngle converts aAngle from board units to a string appropriate for writing to file.
Definition: base_units.cpp:520
int GetBorderWidth() const
Definition: sch_sheet.h:108
std::vector< struct LIB_SYMBOL_UNITS > GetUnitDrawItems()
Return a list of LIB_ITEM objects separated by unit and convert number.
double a
Alpha component.
Definition: color4d.h:380
A LINE_READER that reads from an open file.
Definition: richio.h:172
static LIB_SYMBOL * ParseLibSymbol(LINE_READER &aReader, int aVersion=SEXPR_SCHEMATIC_FILE_VERSION)
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:122
void saveBusAlias(std::shared_ptr< BUS_ALIAS > aAlias, int aNestLevel)
wxFileName GetRealFile() const
void CreateSymbolLib(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Create a new empty symbol library at aLibraryPath.
bool IsFile(const wxString &aFullPathAndFileName) const
wxString GetKeyWords() const
Definition: lib_symbol.h:155
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:124
static void FormatLibSymbol(LIB_SYMBOL *aPart, OUTPUTFORMATTER &aFormatter)
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
const std::vector< VECTOR2I > & CPoints() const
BITMAP_BASE * GetImage() const
Definition: sch_bitmap.h:54
SCH_LAYER_ID
Eeschema drawing layers.
Definition: layer_ids.h:324
wxString GetLogicalName() const
wxPoint GetPosition() const override
Definition: sch_junction.h:92
LIB_ID GetLibId() const override
Definition: lib_symbol.h:135
const wxString & GetName() const
Definition: lib_pin.h:106
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition: sch_screen.h:547
SHAPE_LINE_CHAIN & Outline(int aIndex)
wxString GetSchSymbolLibraryName() const
Definition: sch_symbol.cpp:248
void cacheLib(const wxString &aLibraryFileName, const PROPERTIES *aProperties)
bool IsRootSheet() const
Definition: sch_sheet.cpp:188
static const char * getPinElectricalTypeToken(ELECTRICAL_PINTYPE aType)
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:531
void SaveLibrary(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
static void ResolvePossibleSymlinks(wxFileName &aFilename)
Definition: wx_filename.cpp:85
const wxPoint & GetBezierC2() const
Definition: eda_shape.h:148
static double getSheetPinAngle(SHEET_SIDE aSide)
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
Definition: lib_field.cpp:371
static void IncrementModifyGeneration()
#define MIME_BASE64_LENGTH
static wxString getLineStyleToken(PLOT_DASH_TYPE aStyle)
int m_unit
The unit number.
Definition: lib_symbol.h:84
A simple container for schematic symbol instance information.
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
Definition for symbol library class.
PLOT_DASH_TYPE GetPlotStyle() const
Definition: sch_item.h:155
EDA_ITEM * GetParent() const
Definition: eda_item.h:115
bool ShowPinNames() const
Definition: lib_symbol.h:569
#define _(s)
std::vector< SCH_SHEET_INSTANCE > GetSheetInstances() const
static void savePin(LIB_PIN *aPin, OUTPUTFORMATTER &aFormatter, int aNestLevel=0)
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:154
wxLogTrace helper definitions.
bool isBuffering(const PROPERTIES *aProperties)
std::map< wxString, LIB_SYMBOL * > & GetLibSymbols()
Fetch a list of unique LIB_SYMBOL object pointers required to properly render each SCH_SYMBOL in this...
Definition: sch_screen.h:430
void AddDrawItem(LIB_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
Definition: lib_symbol.cpp:666
virtual void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControlBits) const
Output the object to aFormatter in s-expression form.
Definition: title_block.cpp:29
static const wxString GetDefaultFieldName(int aFieldNdx)
Definition: sch_sheet.cpp:46
static void formatBezier(OUTPUTFORMATTER *aFormatter, int aNestLevel, EDA_SHAPE *aBezier, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, KIID aUuid=niluuid)
LABEL_SPIN_STYLE GetLabelSpinStyle() const
Definition: sch_text.h:159
virtual void Format(OUTPUTFORMATTER *aFormatter, int aNestLevel, int aControlBits) const
Output the object to aFormatter in s-expression form.
Definition: eda_text.cpp:536
bool IsVisible() const
Definition: lib_pin.h:97
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:65
int GetWidth() const
Definition: sch_item.h:152
static void formatStroke(OUTPUTFORMATTER *aFormatter, int aNestLevel, const STROKE_PARAMS &aStroke)
Write stroke definition to aFormatter.
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslate=true)
Return a default symbol field name for field aFieldNdx for all components.
Object to parser s-expression symbol library and schematic file formats.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
int GetNameTextSize() const
Definition: lib_pin.h:126
#define DEFAULT_PIN_NAME_OFFSET
The intersheets references prefix string.
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:315
bool IsDefaultFormatting() const
Definition: eda_text.cpp:522
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:70
void AppendSymbol(SCH_REFERENCE_LIST &aReferences, SCH_SYMBOL *aSymbol, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Append a SCH_REFERENCE object to aReferences based on aSymbol.
wxPoint GetPosition() const override
Definition: sch_text.h:239
wxPoint GetPosition() const override
Definition: sch_sheet.h:380
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition: lib_symbol.h:473
void loadFile(const wxString &aFileName, SCH_SHEET *aSheet)
Object to handle a bitmap image that can be inserted in a schematic.
Definition: sch_bitmap.h:40
KIID_PATH PathWithoutRootUuid() const
Get the sheet path as an KIID_PATH without the root sheet UUID prefix.
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:207
UTF8 Format() const
Definition: lib_id.cpp:116
bool UnitsLocked() const
Check whether symbol units are interchangeable.
Definition: lib_symbol.h:241
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
PLOT_DASH_TYPE
Dashed line types.
Definition: plotter.h:104
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:75
int m_convert
The alternate body style of the unit.
Definition: lib_symbol.h:85
const KIID m_Uuid
Definition: eda_item.h:475
wxPoint GetPosition() const override
Definition: lib_pin.h:210
PROGRESS_REPORTER * m_progressReporter
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:88
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:257
bool IsSymbolLibWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:184
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
The first 4 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
SCH_SHEET * Load(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const PROPERTIES *aProperties=nullptr) override
Load information from some input file format that this SCH_PLUGIN implementation knows about,...
OUTPUTFORMATTER * m_out
The formatter for saving SCH_SCREEN objects.
SCH_FIELD * GetIntersheetRefs()
Definition: sch_text.h:413
const wxString & GetNumber() const
Definition: lib_pin.h:116
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:84
void LoadContent(LINE_READER &aReader, SCH_SHEET *aSheet, int aVersion=SEXPR_SCHEMATIC_FILE_VERSION)
COLOR4D GetColor() const
Definition: sch_item.h:158
std::unordered_set< std::shared_ptr< BUS_ALIAS > > GetBusAliases() const
Return a list of bus aliases defined in this screen.
Definition: sch_screen.h:460
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
std::map< wxString, ALT > & GetAlternates()
Definition: lib_pin.h:132
wxSize GetSize() const
Definition: sch_bus_entry.h:63
PINSHEETLABEL_SHAPE
Definition: sch_text.h:95
double GetTextAngleDegrees() const
Definition: eda_text.h:197
SCH_SHEET & Root() const
Definition: schematic.h:92
A simple container for sheet instance information.
wxString m_path
Root project path for loading child sheets.
static void formatArc(OUTPUTFORMATTER *aFormatter, int aNestLevel, EDA_SHAPE *aArc, int x1, int x2, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, KIID aUuid=niluuid)
int GetNumberTextSize() const
Definition: lib_pin.h:129
bool Exists(const std::string &aProperty) const
Definition: properties.h:41
Schematic symbol object.
Definition: sch_symbol.h:78
const char * name
Definition: DXF_plotter.cpp:56
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
bool CheckHeader(const wxString &aFileName) override
Return true if the first line in aFileName begins with the expected header.
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:35
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.cpp:53
void SortByReferenceOnly()
Sort the list of references by reference.
int GetLength() const
Definition: lib_pin.h:81
Simple container to manage line stroke parameters.
Definition: sch_item.h:140
usual pin input: must be connected
wxSize GetSize() const
Definition: sch_sheet.h:105
KIID m_uuid
A unique identifier for each schematic file.
Definition: sch_screen.h:555
static void saveDcmInfoAsFields(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter, int &aNextFreeFieldId, int aNestLevel)
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
wxString GetName(bool aUseDefaultName=true) const
Return the field name.
Definition: sch_field.cpp:582
wxString GetClass() const override
Return the class name.
Definition: lib_text.h:48
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:110
wxPoint GetArcMid() const
Definition: eda_shape.cpp:436
void SortByPageNumbers(bool aUpdateVirtualPageNums=true)
Sort the list of sheets by page number.
wxString wx_str() const
Definition: utf8.cpp:46
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
wxPoint GetPosition() const override
Definition: sch_symbol.h:644
std::string Double2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 We want to avoid scientific ...
bool GetIncludeOnBoard() const
Definition: sch_symbol.h:665
void saveSheet(SCH_SHEET *aSheet, int aNestLevel)
wxArrayString GetFPFilters() const
Definition: lib_symbol.h:183
virtual STROKE_PARAMS GetStroke() const override
Definition: sch_line.h:124
Used for text file output.
Definition: richio.h:456
virtual wxString GetClass() const override
Return the class name.
Definition: sch_item.h:191
void ParseSchematic(SCH_SHEET *aSheet, bool aIsCopyablyOnly=false, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Parse the internal LINE_READER object into aSheet.
void loadHierarchy(SCH_SHEET *aSheet)
int GetOrientation()
Get the display symbol orientation.
static void SaveSymbol(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter, int aNestLevel=0, const wxString &aLibName=wxEmptyString)
KICAD_PLUGIN_EXPORT SCENEGRAPH * Load(char const *aFileName)
reads a model file and creates a generic display structure
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
input or output (like port for a microprocessor)
static void formatRect(OUTPUTFORMATTER *aFormatter, int aNestLevel, EDA_SHAPE *aRect, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, KIID aUuid=niluuid)
bool IsRoot() const override
For symbols derived from other symbols, IsRoot() indicates no derivation.
Definition: lib_symbol.h:171
std::string EscapedUTF8(const wxString &aString)
Return an 8 bit UTF8 string given aString in Unicode form.
SHEET_SIDE
Define the edge of the sheet that the sheet pin is positioned.
Definition: sch_sheet_pin.h:45
void saveSymbol(SCH_SYMBOL *aSymbol, SCH_SHEET_PATH *aSheetPath, int aNestLevel)
void GetSymbolsWithinPath(SCH_REFERENCE_LIST &aReferences, const SCH_SHEET_PATH &aSheetPath, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets that are contained wi...
SHAPE_T GetShape() const
Definition: eda_shape.h:92
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:110
double r
Red component.
Definition: color4d.h:377
void SetId(int aId)
Definition: sch_field.cpp:80
void init(SCHEMATIC *aSchematic, const PROPERTIES *aProperties=nullptr)
initialize PLUGIN like a constructor would.
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:426
int GetUnit() const
Definition: sch_symbol.h:195
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
int GetRadius() const
Definition: eda_shape.cpp:465
static void saveSymbolDrawItem(LIB_ITEM *aItem, OUTPUTFORMATTER &aFormatter, int aNestLevel)
not connected (must be left open)
wxString GetDescription() override
Definition: lib_symbol.h:142
int GetModifyHash() const override
Return the modification hash from the library cache.
int GetConvert() const
Definition: sch_symbol.h:223
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void SetId(int aId)
Definition: lib_field.h:116
output of a regulator: intended to be connected to power input pins
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:154
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
static const char * PropPowerSymsOnly
FILL_T GetFillType() const
Definition: eda_shape.h:79
std::string FormatInternalUnits(int aValue)
Function FormatInternalUnits converts aValue from internal units to a string appropriate for writing ...
Definition: base_units.cpp:485
void saveBitmap(SCH_BITMAP *aBitmap, int aNestLevel)
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve a list of the SCH_PINs for the given sheet path.
Definition: sch_symbol.cpp:866
bool DeleteSymbolLib(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Delete an existing symbol library and returns true if successful, or if library does not exist return...
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const PROPERTIES *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
static const char * getSheetPinShapeToken(PINSHEETLABEL_SHAPE aShape)
PINSHEETLABEL_SHAPE GetShape() const
Definition: sch_text.h:161
bool UseLibIdLookup() const
Definition: sch_symbol.h:162
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
std::string Quotew(const wxString &aWrapee) const
Definition: richio.cpp:494
wxPoint GetEnd() const
bool GetIncludeInBom() const
Definition: lib_symbol.h:585
const LIB_ID & GetLibId() const
Definition: sch_symbol.h:147
void GetSheetsWithinPath(SCH_SHEET_PATHS &aSheets, const SCH_SHEET_PATH &aSheetPath) const
Add a SCH_SHEET_PATH object to aSheets for each sheet in the list that are contained within aSheetPat...
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
wxPoint GetPosition() const override
wxPoint GetEndPoint() const
Definition: sch_line.h:93