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