KiCad PCB EDA Suite
sch_legacy_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) 2016 CERN
5  * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Wayne Stambaugh <[email protected]>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <algorithm>
24 #include <boost/algorithm/string/join.hpp>
25 #include <cctype>
26 #include <mutex>
27 #include <set>
28 
29 #include <wx/mstream.h>
30 #include <wx/filename.h>
31 #include <wx/log.h>
32 #include <wx/textfile.h>
33 #include <wx/tokenzr.h>
34 #include <wx_filename.h> // For ::ResolvePossibleSymlinks()
35 
36 #include <kiway.h>
37 #include <string_utils.h>
38 #include <locale_io.h>
39 #include <richio.h>
40 #include <trace_helpers.h>
41 #include <trigo.h>
42 #include <progress_reporter.h>
43 #include <general.h>
44 #include <sch_bitmap.h>
45 #include <sch_bus_entry.h>
46 #include <sch_symbol.h>
47 #include <sch_junction.h>
48 #include <sch_line.h>
49 #include <sch_marker.h>
50 #include <sch_no_connect.h>
51 #include <sch_text.h>
52 #include <sch_sheet.h>
53 #include <sch_sheet_pin.h>
54 #include <bus_alias.h>
56 #include <sch_screen.h>
57 #include <schematic.h>
58 #include <symbol_library.h>
59 #include <lib_shape.h>
60 #include <lib_field.h>
61 #include <lib_pin.h>
62 #include <lib_text.h>
63 #include <eeschema_id.h> // for MAX_UNIT_COUNT_PER_PACKAGE definition
64 #include <tool/selection.h>
65 
66 
67 #define Mils2Iu( x ) Mils2iu( x )
68 
69 
70 // Must be the first line of symbol library document (.dcm) files.
71 #define DOCFILE_IDENT "EESchema-DOCLIB Version 2.0"
72 
73 #define SCH_PARSE_ERROR( text, reader, pos ) \
74  THROW_PARSE_ERROR( text, reader.GetSource(), reader.Line(), \
75  reader.LineNumber(), pos - reader.Line() )
76 
77 
78 // Token delimiters.
79 const char* delims = " \t\r\n";
80 
81 // Tokens to read/save graphic lines style
82 #define T_STYLE "style"
83 #define T_COLOR "rgb" // cannot be modified (used by wxWidgets)
84 #define T_COLORA "rgba" // cannot be modified (used by wxWidgets)
85 #define T_WIDTH "width"
86 
87 
88 static bool is_eol( char c )
89 {
90  // The default file eol character used internally by KiCad.
91  // |
92  // | Possible eol if someone edited the file by hand on certain platforms.
93  // | |
94  // | | May have gone past eol with strtok().
95  // | | |
96  if( c == '\n' || c == '\r' || c == 0 )
97  return true;
98 
99  return false;
100 }
101 
102 
112 static bool strCompare( const char* aString, const char* aLine, const char** aOutput = nullptr )
113 {
114  size_t len = strlen( aString );
115  bool retv = ( strncasecmp( aLine, aString, len ) == 0 ) &&
116  ( isspace( aLine[ len ] ) || aLine[ len ] == 0 );
117 
118  if( retv && aOutput )
119  {
120  const char* tmp = aLine;
121 
122  // Move past the end of the token.
123  tmp += len;
124 
125  // Move to the beginning of the next token.
126  while( *tmp && isspace( *tmp ) )
127  tmp++;
128 
129  *aOutput = tmp;
130  }
131 
132  return retv;
133 }
134 
135 
149 static int parseInt( LINE_READER& aReader, const char* aLine, const char** aOutput = nullptr )
150 {
151  if( !*aLine )
152  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
153 
154  // Clear errno before calling strtol() in case some other crt call set it.
155  errno = 0;
156 
157  long retv = strtol( aLine, (char**) aOutput, 10 );
158 
159  // Make sure no error occurred when calling strtol().
160  if( errno == ERANGE )
161  SCH_PARSE_ERROR( "invalid integer value", aReader, aLine );
162 
163  // strtol does not strip off whitespace before the next token.
164  if( aOutput )
165  {
166  const char* next = *aOutput;
167 
168  while( *next && isspace( *next ) )
169  next++;
170 
171  *aOutput = next;
172  }
173 
174  return (int) retv;
175 }
176 
177 
191 static uint32_t parseHex( LINE_READER& aReader, const char* aLine, const char** aOutput = nullptr )
192 {
193  if( !*aLine )
194  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
195 
196  // Due to some issues between some files created by a 64 bits version and those
197  // created by a 32 bits version, we use here a temporary at least 64 bits storage:
198  unsigned long long retv;
199 
200  // Clear errno before calling strtoull() in case some other crt call set it.
201  errno = 0;
202  retv = strtoull( aLine, (char**) aOutput, 16 );
203 
204  // Make sure no error occurred when calling strtoull().
205  if( errno == ERANGE )
206  SCH_PARSE_ERROR( "invalid hexadecimal number", aReader, aLine );
207 
208  // Strip off whitespace before the next token.
209  if( aOutput )
210  {
211  const char* next = *aOutput;
212 
213  while( *next && isspace( *next ) )
214  next++;
215 
216  *aOutput = next;
217  }
218 
219  return (uint32_t)retv;
220 }
221 
222 
236 static double parseDouble( LINE_READER& aReader, const char* aLine,
237  const char** aOutput = nullptr )
238 {
239  if( !*aLine )
240  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
241 
242  // Clear errno before calling strtod() in case some other crt call set it.
243  errno = 0;
244 
245  double retv = strtod( aLine, (char**) aOutput );
246 
247  // Make sure no error occurred when calling strtod().
248  if( errno == ERANGE )
249  SCH_PARSE_ERROR( "invalid floating point number", aReader, aLine );
250 
251  // strtod does not strip off whitespace before the next token.
252  if( aOutput )
253  {
254  const char* next = *aOutput;
255 
256  while( *next && isspace( *next ) )
257  next++;
258 
259  *aOutput = next;
260  }
261 
262  return retv;
263 }
264 
265 
277 static char parseChar( LINE_READER& aReader, const char* aCurrentToken,
278  const char** aNextToken = nullptr )
279 {
280  while( *aCurrentToken && isspace( *aCurrentToken ) )
281  aCurrentToken++;
282 
283  if( !*aCurrentToken )
284  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
285 
286  if( !isspace( *( aCurrentToken + 1 ) ) )
287  SCH_PARSE_ERROR( "expected single character token", aReader, aCurrentToken );
288 
289  if( aNextToken )
290  {
291  const char* next = aCurrentToken + 2;
292 
293  while( *next && isspace( *next ) )
294  next++;
295 
296  *aNextToken = next;
297  }
298 
299  return *aCurrentToken;
300 }
301 
302 
317 static void parseUnquotedString( wxString& aString, LINE_READER& aReader,
318  const char* aCurrentToken, const char** aNextToken = nullptr,
319  bool aCanBeEmpty = false )
320 {
321  if( !*aCurrentToken )
322  {
323  if( aCanBeEmpty )
324  return;
325  else
326  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
327  }
328 
329  const char* tmp = aCurrentToken;
330 
331  while( *tmp && isspace( *tmp ) )
332  tmp++;
333 
334  if( !*tmp )
335  {
336  if( aCanBeEmpty )
337  return;
338  else
339  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
340  }
341 
342  std::string utf8;
343 
344  while( *tmp && !isspace( *tmp ) )
345  utf8 += *tmp++;
346 
347  aString = FROM_UTF8( utf8.c_str() );
348 
349  if( aString.IsEmpty() && !aCanBeEmpty )
350  SCH_PARSE_ERROR( _( "expected unquoted string" ), aReader, aCurrentToken );
351 
352  if( aNextToken )
353  {
354  const char* next = tmp;
355 
356  while( *next && isspace( *next ) )
357  next++;
358 
359  *aNextToken = next;
360  }
361 }
362 
363 
379 static void parseQuotedString( wxString& aString, LINE_READER& aReader,
380  const char* aCurrentToken, const char** aNextToken = nullptr,
381  bool aCanBeEmpty = false )
382 {
383  if( !*aCurrentToken )
384  {
385  if( aCanBeEmpty )
386  return;
387  else
388  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
389  }
390 
391  const char* tmp = aCurrentToken;
392 
393  while( *tmp && isspace( *tmp ) )
394  tmp++;
395 
396  if( !*tmp )
397  {
398  if( aCanBeEmpty )
399  return;
400  else
401  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
402  }
403 
404  // Verify opening quote.
405  if( *tmp != '"' )
406  SCH_PARSE_ERROR( "expecting opening quote", aReader, aCurrentToken );
407 
408  tmp++;
409 
410  std::string utf8; // utf8 without escapes and quotes.
411 
412  // Fetch everything up to closing quote.
413  while( *tmp )
414  {
415  if( *tmp == '\\' )
416  {
417  tmp++;
418 
419  if( !*tmp )
420  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
421 
422  // Do not copy the escape byte if it is followed by \ or "
423  if( *tmp != '"' && *tmp != '\\' )
424  utf8 += '\\';
425 
426  utf8 += *tmp;
427  }
428  else if( *tmp == '"' ) // Closing double quote.
429  {
430  break;
431  }
432  else
433  {
434  utf8 += *tmp;
435  }
436 
437  tmp++;
438  }
439 
440  aString = FROM_UTF8( utf8.c_str() );
441 
442  if( aString.IsEmpty() && !aCanBeEmpty )
443  SCH_PARSE_ERROR( "expected quoted string", aReader, aCurrentToken );
444 
445  if( *tmp && *tmp != '"' )
446  SCH_PARSE_ERROR( "no closing quote for string found", aReader, tmp );
447 
448  // Move past the closing quote.
449  tmp++;
450 
451  if( aNextToken )
452  {
453  const char* next = tmp;
454 
455  while( *next == ' ' )
456  next++;
457 
458  *aNextToken = next;
459  }
460 }
461 
462 
469 {
470  static int s_modHash; // Keep track of the modification status of the library.
471  wxString m_fileName; // Absolute path and file name.
472  wxFileName m_libFileName; // Absolute path and file name is required here.
473  wxDateTime m_fileModTime;
474  LIB_SYMBOL_MAP m_symbols; // Map of names of #LIB_SYMBOL pointers.
479  SCH_LIB_TYPE m_libType; // Is this cache a symbol or symbol library.
480 
481  void loadHeader( FILE_LINE_READER& aReader );
482  static void loadAliases( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader,
483  LIB_SYMBOL_MAP* aMap = nullptr );
484  static void loadField( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader );
485  static void loadDrawEntries( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader,
486  int aMajorVersion, int aMinorVersion );
487  static void loadFootprintFilters( std::unique_ptr<LIB_SYMBOL>& aSymbol,
488  LINE_READER& aReader );
489  void loadDocs();
490  static LIB_SHAPE* loadArc( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader );
491  static LIB_SHAPE* loadCircle( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader );
492  static LIB_TEXT* loadText( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader,
493  int aMajorVersion, int aMinorVersion );
494  static LIB_SHAPE* loadRect( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader );
495  static LIB_PIN* loadPin( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader );
496  static LIB_SHAPE* loadPolyLine( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader );
497  static LIB_SHAPE* loadBezier( std::unique_ptr<LIB_SYMBOL>& aSymbol, LINE_READER& aReader );
498 
499  static FILL_T parseFillMode( LINE_READER& aReader, const char* aLine, const char** aOutput );
500  LIB_SYMBOL* removeSymbol( LIB_SYMBOL* aAlias );
501 
502  void saveDocFile();
503  static void saveArc( LIB_SHAPE* aArc, OUTPUTFORMATTER& aFormatter );
504  static void saveBezier( LIB_SHAPE* aBezier, OUTPUTFORMATTER& aFormatter );
505  static void saveCircle( LIB_SHAPE* aCircle, OUTPUTFORMATTER& aFormatter );
506  static void saveField( const LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter );
507  static void savePin( const LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter );
508  static void savePolyLine( LIB_SHAPE* aPolyLine, OUTPUTFORMATTER& aFormatter );
509  static void saveRectangle( LIB_SHAPE* aRectangle, OUTPUTFORMATTER& aFormatter );
510  static void saveText( const LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter );
511 
513 
514  static std::mutex s_modHashMutex;
515 
516 public:
517  SCH_LEGACY_PLUGIN_CACHE( const wxString& aLibraryPath );
519 
520  static void IncrementModifyHash()
521  {
522  std::lock_guard<std::mutex> mut( SCH_LEGACY_PLUGIN_CACHE::s_modHashMutex );
524  }
525 
526  static int GetModifyHash()
527  {
528  std::lock_guard<std::mutex> mut( SCH_LEGACY_PLUGIN_CACHE::s_modHashMutex );
530  }
531 
532  // Most all functions in this class throw IO_ERROR exceptions. There are no
533  // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects.
534  // Catch these exceptions higher up please.
535 
537  void Save( bool aSaveDocFile = true );
538 
539  void Load();
540 
541  void AddSymbol( const LIB_SYMBOL* aSymbol );
542 
543  void DeleteSymbol( const wxString& aName );
544 
545  // If m_libFileName is a symlink follow it to the real source file
546  wxFileName GetRealFile() const;
547 
548  wxDateTime GetLibModificationTime();
549 
550  bool IsFile( const wxString& aFullPathAndFileName ) const;
551 
552  bool IsFileChanged() const;
553 
554  void SetModified( bool aModified = true ) { m_isModified = aModified; }
555 
556  wxString GetLogicalName() const { return m_libFileName.GetName(); }
557 
558  void SetFileName( const wxString& aFileName ) { m_libFileName = aFileName; }
559 
560  wxString GetFileName() const { return m_libFileName.GetFullPath(); }
561 
562  static LIB_SYMBOL* LoadPart( LINE_READER& aReader, int aMajorVersion, int aMinorVersion,
563  LIB_SYMBOL_MAP* aMap = nullptr );
564  static void SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter,
565  LIB_SYMBOL_MAP* aMap = nullptr );
566 };
567 
568 
570  m_progressReporter( nullptr ),
571  m_lineReader( nullptr ),
572  m_lastProgressLine( 0 ),
573  m_lineCount( 0 )
574 {
575  init( nullptr );
576 }
577 
578 
580 {
581  delete m_cache;
582 }
583 
584 
585 void SCH_LEGACY_PLUGIN::init( SCHEMATIC* aSchematic, const PROPERTIES* aProperties )
586 {
587  m_version = 0;
588  m_rootSheet = nullptr;
589  m_schematic = aSchematic;
590  m_cache = nullptr;
591  m_out = nullptr;
592 }
593 
594 
596 {
597  const unsigned PROGRESS_DELTA = 250;
598 
599  if( m_progressReporter )
600  {
601  unsigned curLine = m_lineReader->LineNumber();
602 
603  if( curLine > m_lastProgressLine + PROGRESS_DELTA )
604  {
605  m_progressReporter->SetCurrentProgress( ( (double) curLine )
606  / std::max( 1U, m_lineCount ) );
607 
609  THROW_IO_ERROR( ( "Open cancelled by user." ) );
610 
611  m_lastProgressLine = curLine;
612  }
613  }
614 }
615 
616 
617 SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
618  SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties )
619 {
620  wxASSERT( !aFileName || aSchematic != nullptr );
621 
622  LOCALE_IO toggle; // toggles on, then off, the C locale.
623  SCH_SHEET* sheet;
624 
625  wxFileName fn = aFileName;
626 
627  // Unfortunately child sheet file names the legacy schematic file format are not fully
628  // qualified and are always appended to the project path. The aFileName attribute must
629  // always be an absolute path so the project path can be used for load child sheet files.
630  wxASSERT( fn.IsAbsolute() );
631 
632  if( aAppendToMe )
633  {
634  wxLogTrace( traceSchLegacyPlugin, "Append \"%s\" to sheet \"%s\".",
635  aFileName, aAppendToMe->GetFileName() );
636 
637  wxFileName normedFn = aAppendToMe->GetFileName();
638 
639  if( !normedFn.IsAbsolute() )
640  {
641  if( aFileName.Right( normedFn.GetFullPath().Length() ) == normedFn.GetFullPath() )
642  m_path = aFileName.Left( aFileName.Length() - normedFn.GetFullPath().Length() );
643  }
644 
645  if( m_path.IsEmpty() )
646  m_path = aSchematic->Prj().GetProjectPath();
647 
648  wxLogTrace( traceSchLegacyPlugin, "m_Normalized append path \"%s\".", m_path );
649  }
650  else
651  {
652  m_path = aSchematic->Prj().GetProjectPath();
653  }
654 
655  m_currentPath.push( m_path );
656  init( aSchematic, aProperties );
657 
658  if( aAppendToMe == nullptr )
659  {
660  // Clean up any allocated memory if an exception occurs loading the schematic.
661  std::unique_ptr<SCH_SHEET> newSheet = std::make_unique<SCH_SHEET>( aSchematic );
662  newSheet->SetFileName( aFileName );
663  m_rootSheet = newSheet.get();
664  loadHierarchy( newSheet.get() );
665 
666  // If we got here, the schematic loaded successfully.
667  sheet = newSheet.release();
668  m_rootSheet = nullptr; // Quiet Coverity warning.
669  }
670  else
671  {
672  wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
673  m_rootSheet = &aSchematic->Root();
674  sheet = aAppendToMe;
675  loadHierarchy( sheet );
676  }
677 
678  wxASSERT( m_currentPath.size() == 1 ); // only the project path should remain
679 
680  return sheet;
681 }
682 
683 
684 // Everything below this comment is recursive. Modify with care.
685 
687 {
688  SCH_SCREEN* screen = nullptr;
689 
690  if( !aSheet->GetScreen() )
691  {
692  // SCH_SCREEN objects store the full path and file name where the SCH_SHEET object only
693  // stores the file name and extension. Add the project path to the file name and
694  // extension to compare when calling SCH_SHEET::SearchHierarchy().
695  wxFileName fileName = aSheet->GetFileName();
696  fileName.SetExt( "sch" );
697 
698  if( !fileName.IsAbsolute() )
699  fileName.MakeAbsolute( m_currentPath.top() );
700 
701  // Save the current path so that it gets restored when descending and ascending the
702  // sheet hierarchy which allows for sheet schematic files to be nested in folders
703  // relative to the last path a schematic was loaded from.
704  wxLogTrace( traceSchLegacyPlugin, "Saving path '%s'", m_currentPath.top() );
705  m_currentPath.push( fileName.GetPath() );
706  wxLogTrace( traceSchLegacyPlugin, "Current path '%s'", m_currentPath.top() );
707  wxLogTrace( traceSchLegacyPlugin, "Loading '%s'", fileName.GetFullPath() );
708 
709  m_rootSheet->SearchHierarchy( fileName.GetFullPath(), &screen );
710 
711  if( screen )
712  {
713  aSheet->SetScreen( screen );
714  screen->SetParent( m_schematic );
715  // Do not need to load the sub-sheets - this has already been done.
716  }
717  else
718  {
719  aSheet->SetScreen( new SCH_SCREEN( m_schematic ) );
720  aSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
721 
722  try
723  {
724  loadFile( fileName.GetFullPath(), aSheet->GetScreen() );
725  }
726  catch( const IO_ERROR& ioe )
727  {
728  // If there is a problem loading the root sheet, there is no recovery.
729  if( aSheet == m_rootSheet )
730  throw( ioe );
731 
732  // For all subsheets, queue up the error message for the caller.
733  if( !m_error.IsEmpty() )
734  m_error += "\n";
735 
736  m_error += ioe.What();
737  }
738 
739  aSheet->GetScreen()->SetFileReadOnly( !fileName.IsFileWritable() );
740  aSheet->GetScreen()->SetFileExists( true );
741 
742  for( auto aItem : aSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
743  {
744  wxCHECK2( aItem->Type() == SCH_SHEET_T, continue );
745  auto sheet = static_cast<SCH_SHEET*>( aItem );
746 
747  // Set the parent to aSheet. This effectively creates a method to find
748  // the root sheet from any sheet so a pointer to the root sheet does not
749  // need to be stored globally. Note: this is not the same as a hierarchy.
750  // Complex hierarchies can have multiple copies of a sheet. This only
751  // provides a simple tree to find the root sheet.
752  sheet->SetParent( aSheet );
753 
754  // Recursion starts here.
755  loadHierarchy( sheet );
756  }
757  }
758 
759  m_currentPath.pop();
760  wxLogTrace( traceSchLegacyPlugin, "Restoring path \"%s\"", m_currentPath.top() );
761  }
762 }
763 
764 
765 void SCH_LEGACY_PLUGIN::loadFile( const wxString& aFileName, SCH_SCREEN* aScreen )
766 {
767  FILE_LINE_READER reader( aFileName );
768 
769  if( m_progressReporter )
770  {
771  m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
772 
774  THROW_IO_ERROR( ( "Open cancelled by user." ) );
775 
776  m_lineReader = &reader;
777  m_lineCount = 0;
778 
779  while( reader.ReadLine() )
780  m_lineCount++;
781 
782  reader.Rewind();
783  }
784 
785  loadHeader( reader, aScreen );
786 
787  LoadContent( reader, aScreen, m_version );
788 
789  // Unfortunately schematic files prior to version 2 are not terminated with $EndSCHEMATC
790  // so checking for its existance will fail so just exit here and take our chances. :(
791  if( m_version > 1 )
792  {
793  char* line = reader.Line();
794 
795  while( *line == ' ' )
796  line++;
797 
798  if( !strCompare( "$EndSCHEMATC", line ) )
799  THROW_IO_ERROR( "'$EndSCHEMATC' not found" );
800  }
801 }
802 
803 
804 void SCH_LEGACY_PLUGIN::LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen, int version )
805 {
806  m_version = version;
807 
808  // We cannot safely load content without a set root level.
809  wxCHECK_RET( m_rootSheet,
810  "Cannot call SCH_LEGACY_PLUGIN::LoadContent() without setting root sheet." );
811 
812  while( aReader.ReadLine() )
813  {
814  checkpoint();
815 
816  char* line = aReader.Line();
817 
818  while( *line == ' ' )
819  line++;
820 
821  // Either an object will be loaded properly or the file load will fail and raise
822  // an exception.
823  if( strCompare( "$Descr", line ) )
824  loadPageSettings( aReader, aScreen );
825  else if( strCompare( "$Comp", line ) )
826  aScreen->Append( loadSymbol( aReader ) );
827  else if( strCompare( "$Sheet", line ) )
828  aScreen->Append( loadSheet( aReader ) );
829  else if( strCompare( "$Bitmap", line ) )
830  aScreen->Append( loadBitmap( aReader ) );
831  else if( strCompare( "Connection", line ) )
832  aScreen->Append( loadJunction( aReader ) );
833  else if( strCompare( "NoConn", line ) )
834  aScreen->Append( loadNoConnect( aReader ) );
835  else if( strCompare( "Wire", line ) )
836  aScreen->Append( loadWire( aReader ) );
837  else if( strCompare( "Entry", line ) )
838  aScreen->Append( loadBusEntry( aReader ) );
839  else if( strCompare( "Text", line ) )
840  aScreen->Append( loadText( aReader ) );
841  else if( strCompare( "BusAlias", line ) )
842  aScreen->AddBusAlias( loadBusAlias( aReader, aScreen ) );
843  else if( strCompare( "$EndSCHEMATC", line ) )
844  return;
845  else
846  SCH_PARSE_ERROR( "unrecognized token", aReader, line );
847  }
848 }
849 
850 
852 {
853  const char* line = aReader.ReadLine();
854 
855  if( !line || !strCompare( "Eeschema Schematic File Version", line, &line ) )
856  {
857  m_error.Printf( _( "'%s' does not appear to be an Eeschema file." ),
858  aScreen->GetFileName() );
860  }
861 
862  // get the file version here.
863  m_version = parseInt( aReader, line, &line );
864 
865  // The next lines are the lib list section, and are mainly comments, like:
866  // LIBS:power
867  // the lib list is not used, but is in schematic file just in case.
868  // It is usually not empty, but we accept empty list.
869  // If empty, there is a legacy section, not used
870  // EELAYER i j
871  // and the last line is
872  // EELAYER END
873  // Skip all lines until the end of header "EELAYER END" is found
874  while( aReader.ReadLine() )
875  {
876  checkpoint();
877 
878  line = aReader.Line();
879 
880  while( *line == ' ' )
881  line++;
882 
883  if( strCompare( "EELAYER END", line ) )
884  return;
885  }
886 
887  THROW_IO_ERROR( _( "Missing 'EELAYER END'" ) );
888 }
889 
890 
892 {
893  wxASSERT( aScreen != nullptr );
894 
895  wxString buf;
896  const char* line = aReader.Line();
897 
898  PAGE_INFO pageInfo;
899  TITLE_BLOCK tb;
900 
901  wxCHECK_RET( strCompare( "$Descr", line, &line ), "Invalid sheet description" );
902 
903  parseUnquotedString( buf, aReader, line, &line );
904 
905  if( !pageInfo.SetType( buf ) )
906  SCH_PARSE_ERROR( "invalid page size", aReader, line );
907 
908  int pagew = parseInt( aReader, line, &line );
909  int pageh = parseInt( aReader, line, &line );
910 
911  if( buf == PAGE_INFO::Custom )
912  {
913  pageInfo.SetWidthMils( pagew );
914  pageInfo.SetHeightMils( pageh );
915  }
916  else
917  {
918  wxString orientation;
919 
920  // Non custom size, set portrait if its present. Can be empty string which defaults
921  // to landscape.
922  parseUnquotedString( orientation, aReader, line, &line, true );
923 
924  if( orientation == "portrait" )
925  pageInfo.SetPortrait( true );
926  }
927 
928  aScreen->SetPageSettings( pageInfo );
929 
930  while( line != nullptr )
931  {
932  buf.clear();
933 
934  if( !aReader.ReadLine() )
935  SCH_PARSE_ERROR( _( "unexpected end of file" ), aReader, line );
936 
937  line = aReader.Line();
938 
939  if( strCompare( "Sheet", line, &line ) )
940  {
941  aScreen->SetVirtualPageNumber( parseInt( aReader, line, &line ) );
942  aScreen->SetPageCount( parseInt( aReader, line, &line ) );
943  }
944  else if( strCompare( "Title", line, &line ) )
945  {
946  parseQuotedString( buf, aReader, line, &line, true );
947  tb.SetTitle( buf );
948  }
949  else if( strCompare( "Date", line, &line ) )
950  {
951  parseQuotedString( buf, aReader, line, &line, true );
952  tb.SetDate( buf );
953  }
954  else if( strCompare( "Rev", line, &line ) )
955  {
956  parseQuotedString( buf, aReader, line, &line, true );
957  tb.SetRevision( buf );
958  }
959  else if( strCompare( "Comp", line, &line ) )
960  {
961  parseQuotedString( buf, aReader, line, &line, true );
962  tb.SetCompany( buf );
963  }
964  else if( strCompare( "Comment1", line, &line ) )
965  {
966  parseQuotedString( buf, aReader, line, &line, true );
967  tb.SetComment( 0, buf );
968  }
969  else if( strCompare( "Comment2", line, &line ) )
970  {
971  parseQuotedString( buf, aReader, line, &line, true );
972  tb.SetComment( 1, buf );
973  }
974  else if( strCompare( "Comment3", line, &line ) )
975  {
976  parseQuotedString( buf, aReader, line, &line, true );
977  tb.SetComment( 2, buf );
978  }
979  else if( strCompare( "Comment4", line, &line ) )
980  {
981  parseQuotedString( buf, aReader, line, &line, true );
982  tb.SetComment( 3, buf );
983  }
984  else if( strCompare( "Comment5", line, &line ) )
985  {
986  parseQuotedString( buf, aReader, line, &line, true );
987  tb.SetComment( 4, buf );
988  }
989  else if( strCompare( "Comment6", line, &line ) )
990  {
991  parseQuotedString( buf, aReader, line, &line, true );
992  tb.SetComment( 5, buf );
993  }
994  else if( strCompare( "Comment7", line, &line ) )
995  {
996  parseQuotedString( buf, aReader, line, &line, true );
997  tb.SetComment( 6, buf );
998  }
999  else if( strCompare( "Comment8", line, &line ) )
1000  {
1001  parseQuotedString( buf, aReader, line, &line, true );
1002  tb.SetComment( 7, buf );
1003  }
1004  else if( strCompare( "Comment9", line, &line ) )
1005  {
1006  parseQuotedString( buf, aReader, line, &line, true );
1007  tb.SetComment( 8, buf );
1008  }
1009  else if( strCompare( "$EndDescr", line ) )
1010  {
1011  aScreen->SetTitleBlock( tb );
1012  return;
1013  }
1014  }
1015 
1016  SCH_PARSE_ERROR( "missing 'EndDescr'", aReader, line );
1017 }
1018 
1019 
1021 {
1022  std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
1023 
1024  const char* line = aReader.ReadLine();
1025 
1026  while( line != nullptr )
1027  {
1028  if( strCompare( "S", line, &line ) ) // Sheet dimensions.
1029  {
1030  wxPoint position;
1031 
1032  position.x = Mils2Iu( parseInt( aReader, line, &line ) );
1033  position.y = Mils2Iu( parseInt( aReader, line, &line ) );
1034  sheet->SetPosition( position );
1035 
1036  wxSize size;
1037 
1038  size.SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
1039  size.SetHeight( Mils2Iu( parseInt( aReader, line, &line ) ) );
1040  sheet->SetSize( size );
1041  }
1042  else if( strCompare( "U", line, &line ) ) // Sheet UUID.
1043  {
1044  wxString text;
1045  parseUnquotedString( text, aReader, line );
1046 
1047  if( text != "00000000" )
1048  const_cast<KIID&>( sheet->m_Uuid ) = KIID( text );
1049  }
1050  else if( *line == 'F' ) // Sheet field.
1051  {
1052  line++;
1053 
1054  wxString text;
1055  int size;
1056  int fieldId = parseInt( aReader, line, &line );
1057 
1058  if( fieldId == 0 || fieldId == 1 ) // Sheet name and file name.
1059  {
1060  parseQuotedString( text, aReader, line, &line );
1061  size = Mils2Iu( parseInt( aReader, line, &line ) );
1062 
1063  SCH_FIELD& field = sheet->GetFields()[ fieldId ];
1064  field.SetText( text );
1065  field.SetTextSize( wxSize( size, size ) );
1066  }
1067  else // Sheet pin.
1068  {
1069  // Use a unique_ptr so that we clean up in the case of a throw
1070  std::unique_ptr<SCH_SHEET_PIN> sheetPin = std::make_unique<SCH_SHEET_PIN>( sheet.get() );
1071 
1072  sheetPin->SetNumber( fieldId );
1073 
1074  // Can be empty fields.
1075  parseQuotedString( text, aReader, line, &line, true );
1076 
1077  sheetPin->SetText( ConvertToNewOverbarNotation( text ) );
1078 
1079  if( line == nullptr )
1080  THROW_IO_ERROR( _( "unexpected end of line" ) );
1081 
1082  switch( parseChar( aReader, line, &line ) )
1083  {
1084  case 'I': sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT ); break;
1085  case 'O': sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT ); break;
1086  case 'B': sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI ); break;
1087  case 'T': sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE ); break;
1088  case 'U': sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED ); break;
1089  default: SCH_PARSE_ERROR( "invalid sheet pin type", aReader, line );
1090  }
1091 
1092  switch( parseChar( aReader, line, &line ) )
1093  {
1094  case 'R': sheetPin->SetSide( SHEET_SIDE::RIGHT ); break;
1095  case 'T': sheetPin->SetSide( SHEET_SIDE::TOP ); break;
1096  case 'B': sheetPin->SetSide( SHEET_SIDE::BOTTOM ); break;
1097  case 'L': sheetPin->SetSide( SHEET_SIDE::LEFT ); break;
1098  default:
1099  SCH_PARSE_ERROR( "invalid sheet pin side", aReader, line );
1100  }
1101 
1102  wxPoint position;
1103 
1104  position.x = Mils2Iu( parseInt( aReader, line, &line ) );
1105  position.y = Mils2Iu( parseInt( aReader, line, &line ) );
1106  sheetPin->SetPosition( position );
1107 
1108  size = Mils2Iu( parseInt( aReader, line, &line ) );
1109 
1110  sheetPin->SetTextSize( wxSize( size, size ) );
1111 
1112  sheet->AddPin( sheetPin.release() );
1113  }
1114  }
1115  else if( strCompare( "$EndSheet", line ) )
1116  {
1117  sheet->AutoplaceFields( /* aScreen */ nullptr, /* aManual */ false );
1118  return sheet.release();
1119  }
1120 
1121  line = aReader.ReadLine();
1122  }
1123 
1124  SCH_PARSE_ERROR( "missing '$EndSheet`", aReader, line );
1125 
1126  return nullptr; // Prevents compiler warning. Should never get here.
1127 }
1128 
1129 
1131 {
1132  std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
1133 
1134  const char* line = aReader.Line();
1135 
1136  wxCHECK( strCompare( "$Bitmap", line, &line ), nullptr );
1137 
1138  line = aReader.ReadLine();
1139 
1140  while( line != nullptr )
1141  {
1142  if( strCompare( "Pos", line, &line ) )
1143  {
1144  wxPoint position;
1145 
1146  position.x = Mils2Iu( parseInt( aReader, line, &line ) );
1147  position.y = Mils2Iu( parseInt( aReader, line, &line ) );
1148  bitmap->SetPosition( position );
1149  }
1150  else if( strCompare( "Scale", line, &line ) )
1151  {
1152  auto scalefactor = parseDouble( aReader, line, &line );
1153 
1154  // Prevent scalefactor values that cannot be displayed.
1155  // In the case of a bad value, we accept that the image might be mis-scaled
1156  // rather than removing the full image. Users can then edit the scale factor in
1157  // Eeschema to the appropriate value
1158  if( !std::isnormal( scalefactor ) )
1159  scalefactor = 1.0;
1160 
1161  bitmap->GetImage()->SetScale( scalefactor );
1162  }
1163  else if( strCompare( "Data", line, &line ) )
1164  {
1165  wxMemoryOutputStream stream;
1166 
1167  while( line )
1168  {
1169  if( !aReader.ReadLine() )
1170  SCH_PARSE_ERROR( _( "Unexpected end of file" ), aReader, line );
1171 
1172  line = aReader.Line();
1173 
1174  if( strCompare( "EndData", line ) )
1175  {
1176  // all the PNG date is read.
1177  // We expect here m_image and m_bitmap are void
1178  wxImage* image = new wxImage();
1179  wxMemoryInputStream istream( stream );
1180  image->LoadFile( istream, wxBITMAP_TYPE_PNG );
1181  bitmap->GetImage()->SetImage( image );
1182  bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
1183  break;
1184  }
1185 
1186  // Read PNG data, stored in hexadecimal,
1187  // each byte = 2 hexadecimal digits and a space between 2 bytes
1188  // and put it in memory stream buffer
1189  // Note:
1190  // Some old files created bu the V4 schematic versions have a extra
1191  // "$EndBitmap" at the end of the hexadecimal data. (Probably due to
1192  // a bug). So discard it
1193  int len = strlen( line );
1194 
1195  for( ; len > 0 && !isspace( *line ) && '$' != *line; len -= 3, line += 3 )
1196  {
1197  int value = 0;
1198 
1199  if( sscanf( line, "%X", &value ) == 1 )
1200  stream.PutC( (char) value );
1201  else
1202  THROW_IO_ERROR( "invalid PNG data" );
1203  }
1204  }
1205 
1206  if( line == nullptr )
1207  THROW_IO_ERROR( _( "unexpected end of file" ) );
1208  }
1209  else if( strCompare( "$EndBitmap", line ) )
1210  return bitmap.release();
1211 
1212  line = aReader.ReadLine();
1213  }
1214 
1215  THROW_IO_ERROR( _( "unexpected end of file" ) );
1216 }
1217 
1218 
1220 {
1221  std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
1222 
1223  const char* line = aReader.Line();
1224 
1225  wxCHECK( strCompare( "Connection", line, &line ), nullptr );
1226 
1227  wxString name;
1228 
1229  parseUnquotedString( name, aReader, line, &line );
1230 
1231  wxPoint position;
1232 
1233  position.x = Mils2Iu( parseInt( aReader, line, &line ) );
1234  position.y = Mils2Iu( parseInt( aReader, line, &line ) );
1235  junction->SetPosition( position );
1236 
1237  return junction.release();
1238 }
1239 
1240 
1242 {
1243  std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
1244 
1245  const char* line = aReader.Line();
1246 
1247  wxCHECK( strCompare( "NoConn", line, &line ), nullptr );
1248 
1249  wxString name;
1250 
1251  parseUnquotedString( name, aReader, line, &line );
1252 
1253  wxPoint position;
1254 
1255  position.x = Mils2Iu( parseInt( aReader, line, &line ) );
1256  position.y = Mils2Iu( parseInt( aReader, line, &line ) );
1257  no_connect->SetPosition( position );
1258 
1259  return no_connect.release();
1260 }
1261 
1262 
1264 {
1265  std::unique_ptr<SCH_LINE> wire = std::make_unique<SCH_LINE>();
1266 
1267  const char* line = aReader.Line();
1268 
1269  wxCHECK( strCompare( "Wire", line, &line ), nullptr );
1270 
1271  if( strCompare( "Wire", line, &line ) )
1272  wire->SetLayer( LAYER_WIRE );
1273  else if( strCompare( "Bus", line, &line ) )
1274  wire->SetLayer( LAYER_BUS );
1275  else if( strCompare( "Notes", line, &line ) )
1276  wire->SetLayer( LAYER_NOTES );
1277  else
1278  SCH_PARSE_ERROR( "invalid line type", aReader, line );
1279 
1280  if( !strCompare( "Line", line, &line ) )
1281  SCH_PARSE_ERROR( "invalid wire definition", aReader, line );
1282 
1283  // Since Sept 15, 2017, a line style is alloved (width, style, color)
1284  // Only non default values are stored
1285  while( !is_eol( *line ) )
1286  {
1287  wxString buf;
1288 
1289  parseUnquotedString( buf, aReader, line, &line );
1290 
1291  if( buf == ")" )
1292  continue;
1293 
1294  else if( buf == T_WIDTH )
1295  {
1296  int size = Mils2Iu( parseInt( aReader, line, &line ) );
1297  wire->SetLineWidth( size );
1298  }
1299  else if( buf == T_STYLE )
1300  {
1301  parseUnquotedString( buf, aReader, line, &line );
1303  wire->SetLineStyle( style );
1304  }
1305  else // should be the color parameter.
1306  {
1307  // The color param is something like rgb(150, 40, 191)
1308  // and because there is no space between ( and 150
1309  // the first param is inside buf.
1310  // So break keyword and the first param into 2 separate strings.
1311  wxString prm, keyword;
1312  keyword = buf.BeforeLast( '(', &prm );
1313 
1314  if( ( keyword == T_COLOR ) || ( keyword == T_COLORA ) )
1315  {
1316  long color[4] = { 0 };
1317 
1318  int ii = 0;
1319 
1320  if( !prm.IsEmpty() )
1321  {
1322  prm.ToLong( &color[ii] );
1323  ii++;
1324  }
1325 
1326  int prm_count = ( keyword == T_COLORA ) ? 4 : 3;
1327  // fix opacity to 1.0 or 255, when not exists in file
1328  color[3] = 255;
1329 
1330  for(; ii < prm_count && !is_eol( *line ); ii++ )
1331  {
1332  color[ii] = parseInt( aReader, line, &line );
1333 
1334  // Skip the separator between values
1335  if( *line == ',' || *line == ' ')
1336  line++;
1337  }
1338 
1339  wire->SetLineColor( color[0]/255.0, color[1]/255.0, color[2]/255.0,color[3]/255.0 );
1340  }
1341  }
1342  }
1343 
1344  // Read the segment en points coordinates:
1345  line = aReader.ReadLine();
1346 
1347  wxPoint begin, end;
1348 
1349  begin.x = Mils2Iu( parseInt( aReader, line, &line ) );
1350  begin.y = Mils2Iu( parseInt( aReader, line, &line ) );
1351  end.x = Mils2Iu( parseInt( aReader, line, &line ) );
1352  end.y = Mils2Iu( parseInt( aReader, line, &line ) );
1353 
1354  wire->SetStartPoint( begin );
1355  wire->SetEndPoint( end );
1356 
1357  return wire.release();
1358 }
1359 
1360 
1362 {
1363  const char* line = aReader.Line();
1364 
1365  wxCHECK( strCompare( "Entry", line, &line ), nullptr );
1366 
1367  std::unique_ptr<SCH_BUS_ENTRY_BASE> busEntry;
1368 
1369  if( strCompare( "Wire", line, &line ) )
1370  {
1371  busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
1372 
1373  if( !strCompare( "Line", line, &line ) )
1374  SCH_PARSE_ERROR( "invalid bus entry definition expected 'Line'", aReader, line );
1375  }
1376  else if( strCompare( "Bus", line, &line ) )
1377  {
1378  busEntry = std::make_unique<SCH_BUS_BUS_ENTRY>();
1379 
1380  if( !strCompare( "Bus", line, &line ) )
1381  SCH_PARSE_ERROR( "invalid bus entry definition expected 'Bus'", aReader, line );
1382  }
1383  else
1384  SCH_PARSE_ERROR( "invalid bus entry type", aReader, line );
1385 
1386  line = aReader.ReadLine();
1387 
1388  wxPoint pos;
1389  wxSize size;
1390 
1391  pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
1392  pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
1393  size.x = Mils2Iu( parseInt( aReader, line, &line ) );
1394  size.y = Mils2Iu( parseInt( aReader, line, &line ) );
1395 
1396  size.x -= pos.x;
1397  size.y -= pos.y;
1398 
1399  busEntry->SetPosition( pos );
1400  busEntry->SetSize( size );
1401 
1402  return busEntry.release();
1403 }
1404 
1405 // clang-format off
1406 const std::map<PINSHEETLABEL_SHAPE, const char*> sheetLabelNames
1408  { PINSHEETLABEL_SHAPE::PS_INPUT, "Input" },
1409  { PINSHEETLABEL_SHAPE::PS_OUTPUT, "Output" },
1410  { PINSHEETLABEL_SHAPE::PS_BIDI, "BiDi" },
1411  { PINSHEETLABEL_SHAPE::PS_TRISTATE, "3State" },
1413 };
1414 // clang-format on
1415 
1416 
1418 {
1419  const char* line = aReader.Line();
1420 
1421  wxCHECK( strCompare( "Text", line, &line ), nullptr );
1422 
1423  std::unique_ptr<SCH_TEXT> text;
1424 
1425  if( strCompare( "Notes", line, &line ) )
1426  text.reset( new SCH_TEXT );
1427  else if( strCompare( "Label", line, &line ) )
1428  text.reset( new SCH_LABEL );
1429  else if( strCompare( "HLabel", line, &line ) )
1430  text.reset( new SCH_HIERLABEL );
1431  else if( strCompare( "GLabel", line, &line ) )
1432  {
1433  // Prior to version 2, the SCH_GLOBALLABEL object did not exist.
1434  if( m_version == 1 )
1435  text = std::make_unique<SCH_HIERLABEL>();
1436  else
1437  text = std::make_unique<SCH_GLOBALLABEL>();
1438  }
1439  else
1440  SCH_PARSE_ERROR( "unknown Text type", aReader, line );
1441 
1442  // Parse the parameters common to all text objects.
1443  wxPoint position;
1444 
1445  position.x = Mils2Iu( parseInt( aReader, line, &line ) );
1446  position.y = Mils2Iu( parseInt( aReader, line, &line ) );
1447  text->SetPosition( position );
1448 
1449  int spinStyle = parseInt( aReader, line, &line );
1450 
1451  // Sadly we store the orientation of hierarchical and global labels using a different
1452  // int encoding than that for local labels:
1453  // Global Local
1454  // Left justified 0 2
1455  // Up 1 3
1456  // Right justified 2 0
1457  // Down 3 1
1458  // So we must flip it as the enum is setup with the "global" numbering
1459  if( text->Type() != SCH_GLOBAL_LABEL_T && text->Type() != SCH_HIER_LABEL_T )
1460  {
1461  if( spinStyle == 0 )
1462  spinStyle = 2;
1463  else if( spinStyle == 2 )
1464  spinStyle = 0;
1465  }
1466 
1467  text->SetLabelSpinStyle( (LABEL_SPIN_STYLE::SPIN) spinStyle );
1468 
1469  int size = Mils2Iu( parseInt( aReader, line, &line ) );
1470 
1471  text->SetTextSize( wxSize( size, size ) );
1472 
1473  // Parse the global and hierarchical label type.
1474  if( text->Type() == SCH_HIER_LABEL_T || text->Type() == SCH_GLOBAL_LABEL_T )
1475  {
1476  auto resultIt = std::find_if( sheetLabelNames.begin(), sheetLabelNames.end(),
1477  [ &line ]( const auto& it )
1478  {
1479  return strCompare( it.second, line, &line );
1480  } );
1481 
1482  if( resultIt != sheetLabelNames.end() )
1483  text->SetShape( resultIt->first );
1484  else
1485  SCH_PARSE_ERROR( "invalid label type", aReader, line );
1486  }
1487 
1488  int penWidth = 0;
1489 
1490  // The following tokens do not exist in version 1 schematic files,
1491  // and not always in version 2 for HLabels and GLabels
1492  if( m_version > 1 )
1493  {
1494  if( m_version > 2 || *line >= ' ' )
1495  {
1496  if( strCompare( "Italic", line, &line ) )
1497  text->SetItalic( true );
1498  else if( !strCompare( "~", line, &line ) )
1499  SCH_PARSE_ERROR( _( "expected 'Italics' or '~'" ), aReader, line );
1500  }
1501 
1502  // The penWidth token does not exist in older versions of the schematic file format
1503  // so calling parseInt will be made only if the EOL is not reached.
1504  if( *line >= ' ' )
1505  penWidth = parseInt( aReader, line, &line );
1506  }
1507 
1508  text->SetBold( penWidth != 0 );
1509  text->SetTextThickness( penWidth != 0 ? GetPenSizeForBold( size ) : 0 );
1510 
1511  // Read the text string for the text.
1512  char* tmp = aReader.ReadLine();
1513 
1514  tmp = strtok( tmp, "\r\n" );
1515  wxString val = FROM_UTF8( tmp );
1516 
1517  for( ; ; )
1518  {
1519  int i = val.find( wxT( "\\n" ) );
1520 
1521  if( i == wxNOT_FOUND )
1522  break;
1523 
1524  val.erase( i, 2 );
1525  val.insert( i, wxT( "\n" ) );
1526  }
1527 
1528  text->SetText( ConvertToNewOverbarNotation( val ) );
1529 
1530  return text.release();
1531 }
1532 
1533 
1535 {
1536  const char* line = aReader.Line();
1537 
1538  wxCHECK( strCompare( "$Comp", line, &line ), nullptr );
1539 
1540  std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
1541 
1542  line = aReader.ReadLine();
1543 
1544  while( line != nullptr )
1545  {
1546  if( strCompare( "L", line, &line ) )
1547  {
1548  wxString libName;
1549  size_t pos = 2; // "X" plus ' ' space character.
1550  wxString utf8Line = wxString::FromUTF8( line );
1551  wxStringTokenizer tokens( utf8Line, " \r\n\t" );
1552 
1553  if( tokens.CountTokens() < 2 )
1554  THROW_PARSE_ERROR( "invalid symbol library definition", aReader.GetSource(),
1555  aReader.Line(), aReader.LineNumber(), pos );
1556 
1557  libName = tokens.GetNextToken();
1558  libName.Replace( "~", " " );
1559 
1560  LIB_ID libId;
1561 
1562  // Prior to schematic version 4, library IDs did not have a library nickname so
1563  // parsing the symbol name with LIB_ID::Parse() would break symbol library links
1564  // that contained '/' and ':' characters.
1565  if( m_version > 3 )
1566  libId.Parse( libName, true );
1567  else
1568  libId.SetLibItemName( libName );
1569 
1570  symbol->SetLibId( libId );
1571 
1572  wxString refDesignator = tokens.GetNextToken();
1573 
1574  refDesignator.Replace( "~", " " );
1575 
1576  wxString prefix = refDesignator;
1577 
1578  while( prefix.Length() )
1579  {
1580  if( ( prefix.Last() < '0' || prefix.Last() > '9') && prefix.Last() != '?' )
1581  break;
1582 
1583  prefix.RemoveLast();
1584  }
1585 
1586  // Avoid a prefix containing trailing/leading spaces
1587  prefix.Trim( true );
1588  prefix.Trim( false );
1589 
1590  if( prefix.IsEmpty() )
1591  symbol->SetPrefix( wxString( "U" ) );
1592  else
1593  symbol->SetPrefix( prefix );
1594  }
1595  else if( strCompare( "U", line, &line ) )
1596  {
1597  // This fixes a potentially buggy files caused by unit being set to zero which
1598  // causes netlist issues. See https://bugs.launchpad.net/kicad/+bug/1677282.
1599  int unit = parseInt( aReader, line, &line );
1600 
1601  if( unit == 0 )
1602  {
1603  unit = 1;
1604 
1605  // Set the file as modified so the user can be warned.
1606  if( m_rootSheet->GetScreen() )
1608  }
1609 
1610  symbol->SetUnit( unit );
1611 
1612  // Same can also happen with the convert parameter
1613  int convert = parseInt( aReader, line, &line );
1614 
1615  if( convert == 0 )
1616  {
1617  convert = 1;
1618 
1619  // Set the file as modified so the user can be warned.
1620  if( m_rootSheet->GetScreen() )
1622  }
1623 
1624  symbol->SetConvert( convert );
1625 
1626  wxString text;
1627  parseUnquotedString( text, aReader, line, &line );
1628 
1629  if( text != "00000000" )
1630  const_cast<KIID&>( symbol->m_Uuid ) = KIID( text );
1631  }
1632  else if( strCompare( "P", line, &line ) )
1633  {
1634  wxPoint pos;
1635 
1636  pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
1637  pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
1638  symbol->SetPosition( pos );
1639  }
1640  else if( strCompare( "AR", line, &line ) )
1641  {
1642  const char* strCompare = "Path=";
1643  int len = strlen( strCompare );
1644 
1645  if( strncasecmp( strCompare, line, len ) != 0 )
1646  SCH_PARSE_ERROR( "missing 'Path=' token", aReader, line );
1647 
1648  line += len;
1649  wxString pathStr, reference, unit;
1650 
1651  parseQuotedString( pathStr, aReader, line, &line );
1652 
1653  // Note: AR path excludes root sheet, but includes symbol. Normalize to
1654  // internal format by shifting everything down one and adding the root sheet.
1655  KIID_PATH path( pathStr );
1656 
1657  if( path.size() > 0 )
1658  {
1659  for( size_t i = path.size() - 1; i > 0; --i )
1660  path[i] = path[i-1];
1661 
1662  path[0] = m_rootSheet->m_Uuid;
1663  }
1664  else
1665  path.push_back( m_rootSheet->m_Uuid );
1666 
1667  strCompare = "Ref=";
1668  len = strlen( strCompare );
1669 
1670  if( strncasecmp( strCompare, line, len ) != 0 )
1671  SCH_PARSE_ERROR( "missing 'Ref=' token", aReader, line );
1672 
1673  line+= len;
1674  parseQuotedString( reference, aReader, line, &line );
1675 
1676  strCompare = "Part=";
1677  len = strlen( strCompare );
1678 
1679  if( strncasecmp( strCompare, line, len ) != 0 )
1680  SCH_PARSE_ERROR( "missing 'Part=' token", aReader, line );
1681 
1682  line+= len;
1683  parseQuotedString( unit, aReader, line, &line );
1684 
1685  long tmp;
1686 
1687  if( !unit.ToLong( &tmp, 10 ) )
1688  SCH_PARSE_ERROR( "expected integer value", aReader, line );
1689 
1690  if( tmp < 0 || tmp > MAX_UNIT_COUNT_PER_PACKAGE )
1691  SCH_PARSE_ERROR( "unit value out of range", aReader, line );
1692 
1693  symbol->AddHierarchicalReference( path, reference, (int)tmp );
1694  symbol->GetField( REFERENCE_FIELD )->SetText( reference );
1695 
1696  }
1697  else if( strCompare( "F", line, &line ) )
1698  {
1699  int index = parseInt( aReader, line, &line );
1700 
1701  wxString text, name;
1702 
1703  parseQuotedString( text, aReader, line, &line, true );
1704 
1705  char orientation = parseChar( aReader, line, &line );
1706  wxPoint pos;
1707  pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
1708  pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
1709  int size = Mils2Iu( parseInt( aReader, line, &line ) );
1710  int attributes = parseHex( aReader, line, &line );
1711 
1712  if( index >= symbol->GetFieldCount() )
1713  {
1714  // The first MANDATOR_FIELDS _must_ be constructed within the SCH_SYMBOL
1715  // constructor. This assert is simply here to guard against a change in that
1716  // constructor.
1717  wxASSERT( symbol->GetFieldCount() >= MANDATORY_FIELDS );
1718 
1719  // Ignore the _supplied_ fieldNdx. It is not important anymore if within the
1720  // user defined fields region (i.e. >= MANDATORY_FIELDS).
1721  // We freely renumber the index to fit the next available field slot.
1722  index = symbol->GetFieldCount(); // new has this index after insertion
1723 
1724  SCH_FIELD field( wxPoint( 0, 0 ), index, symbol.get(), name );
1725  symbol->AddField( field );
1726  }
1727 
1728  SCH_FIELD& field = symbol->GetFields()[index];
1729 
1730  // Prior to version 2 of the schematic file format, none of the following existed.
1731  if( m_version > 1 )
1732  {
1733  wxString textAttrs;
1734  char hjustify = parseChar( aReader, line, &line );
1735 
1736  parseUnquotedString( textAttrs, aReader, line, &line );
1737 
1738  // The name of the field is optional.
1739  parseQuotedString( name, aReader, line, &line, true );
1740 
1741  if( hjustify == 'L' )
1743  else if( hjustify == 'R' )
1745  else if( hjustify != 'C' )
1746  SCH_PARSE_ERROR( "symbol field text horizontal justification must be "
1747  "L, R, or C", aReader, line );
1748 
1749  // We are guaranteed to have a least one character here for older file formats
1750  // otherwise an exception would have been raised..
1751  if( textAttrs[0] == 'T' )
1753  else if( textAttrs[0] == 'B' )
1755  else if( textAttrs[0] != 'C' )
1756  SCH_PARSE_ERROR( "symbol field text vertical justification must be "
1757  "B, T, or C", aReader, line );
1758 
1759  // Newer file formats include the bold and italics text attribute.
1760  if( textAttrs.Length() > 1 )
1761  {
1762  if( textAttrs.Length() != 3 )
1763  SCH_PARSE_ERROR( _( "symbol field text attributes must be 3 characters wide" ),
1764  aReader, line );
1765 
1766  if( textAttrs[1] == 'I' )
1767  field.SetItalic( true );
1768  else if( textAttrs[1] != 'N' )
1769  SCH_PARSE_ERROR( "symbol field text italics indicator must be I or N",
1770  aReader, line );
1771 
1772  if( textAttrs[2] == 'B' )
1773  field.SetBold( true );
1774  else if( textAttrs[2] != 'N' )
1775  SCH_PARSE_ERROR( "symbol field text bold indicator must be B or N",
1776  aReader, line );
1777  }
1778  }
1779 
1780  field.SetText( text );
1781  field.SetTextPos( pos );
1782  field.SetVisible( !attributes );
1783  field.SetTextSize( wxSize( size, size ) );
1784 
1785  if( orientation == 'H' )
1786  field.SetTextAngle( TEXT_ANGLE_HORIZ );
1787  else if( orientation == 'V' )
1788  field.SetTextAngle( TEXT_ANGLE_VERT );
1789  else
1790  SCH_PARSE_ERROR( "symbol field orientation must be H or V", aReader, line );
1791 
1792  if( name.IsEmpty() )
1794 
1795  field.SetName( name );
1796  }
1797  else if( strCompare( "$EndComp", line ) )
1798  {
1799  // Ensure all flags (some are set by previous initializations) are reset:
1800  symbol->ClearFlags();
1801  return symbol.release();
1802  }
1803  else
1804  {
1805  // There are two lines that begin with a tab or spaces that includes a line with the
1806  // redundant position information and the transform matrix settings.
1807 
1808  // Parse the redundant position information just the same to check for formatting
1809  // errors.
1810  parseInt( aReader, line, &line ); // Always 1.
1811  parseInt( aReader, line, &line ); // The X coordinate.
1812  parseInt( aReader, line, &line ); // The Y coordinate.
1813 
1814  line = aReader.ReadLine();
1815 
1816  TRANSFORM transform;
1817 
1818  transform.x1 = parseInt( aReader, line, &line );
1819 
1820  if( transform.x1 < -1 || transform.x1 > 1 )
1821  SCH_PARSE_ERROR( "invalid symbol X1 transform value", aReader, line );
1822 
1823  transform.y1 = parseInt( aReader, line, &line );
1824 
1825  if( transform.y1 < -1 || transform.y1 > 1 )
1826  SCH_PARSE_ERROR( "invalid symbol Y1 transform value", aReader, line );
1827 
1828  transform.x2 = parseInt( aReader, line, &line );
1829 
1830  if( transform.x2 < -1 || transform.x2 > 1 )
1831  SCH_PARSE_ERROR( "invalid symbol X2 transform value", aReader, line );
1832 
1833  transform.y2 = parseInt( aReader, line, &line );
1834 
1835  if( transform.y2 < -1 || transform.y2 > 1 )
1836  SCH_PARSE_ERROR( "invalid symbol Y2 transform value", aReader, line );
1837 
1838  symbol->SetTransform( transform );
1839  }
1840 
1841  line = aReader.ReadLine();
1842  }
1843 
1844  SCH_PARSE_ERROR( "invalid symbol line", aReader, line );
1845 
1846  return nullptr; // Prevents compiler warning. Should never get here.
1847 }
1848 
1849 
1850 std::shared_ptr<BUS_ALIAS> SCH_LEGACY_PLUGIN::loadBusAlias( LINE_READER& aReader,
1851  SCH_SCREEN* aScreen )
1852 {
1853  auto busAlias = std::make_shared<BUS_ALIAS>( aScreen );
1854  const char* line = aReader.Line();
1855 
1856  wxCHECK( strCompare( "BusAlias", line, &line ), nullptr );
1857 
1858  wxString buf;
1859  parseUnquotedString( buf, aReader, line, &line );
1860  busAlias->SetName( buf );
1861 
1862  while( *line != '\0' )
1863  {
1864  buf.clear();
1865  parseUnquotedString( buf, aReader, line, &line, true );
1866 
1867  if( buf.Len() > 0 )
1868  {
1869  busAlias->AddMember( buf );
1870  }
1871  }
1872 
1873  return busAlias;
1874 }
1875 
1876 
1877 void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
1878  const PROPERTIES* aProperties )
1879 {
1880  wxCHECK_RET( aSheet != nullptr, "NULL SCH_SHEET object." );
1881  wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
1882 
1883  LOCALE_IO toggle; // toggles on, then off, the C locale, to write floating point values.
1884 
1885  init( aSchematic, aProperties );
1886 
1887  wxFileName fn = aFileName;
1888 
1889  // File names should be absolute. Don't assume everything relative to the project path
1890  // works properly.
1891  wxASSERT( fn.IsAbsolute() );
1892 
1893  FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
1894 
1895  m_out = &formatter; // no ownership
1896 
1897  Format( aSheet );
1898 
1899  aSheet->GetScreen()->SetFileExists( true );
1900 }
1901 
1902 
1904 {
1905  wxCHECK_RET( aSheet != nullptr, "NULL SCH_SHEET* object." );
1906  wxCHECK_RET( m_schematic != nullptr, "NULL SCHEMATIC* object." );
1907 
1908  SCH_SCREEN* screen = aSheet->GetScreen();
1909 
1910  wxCHECK( screen, /* void */ );
1911 
1912  // Write the header
1913  m_out->Print( 0, "%s %s %d\n", "EESchema", SCHEMATIC_HEAD_STRING, EESCHEMA_VERSION );
1914 
1915  // This section is not used, but written for file compatibility
1916  m_out->Print( 0, "EELAYER %d %d\n", SCH_LAYER_ID_COUNT, 0 );
1917  m_out->Print( 0, "EELAYER END\n" );
1918 
1919  /* Write page info, ScreenNumber and NumberOfScreen; not very meaningful for
1920  * SheetNumber and Sheet Count in a complex hierarchy, but useful in
1921  * simple hierarchy and flat hierarchy. Used also to search the root
1922  * sheet ( ScreenNumber = 1 ) within the files
1923  */
1924  const TITLE_BLOCK& tb = screen->GetTitleBlock();
1925  const PAGE_INFO& page = screen->GetPageSettings();
1926 
1927  m_out->Print( 0, "$Descr %s %d %d%s\n", TO_UTF8( page.GetType() ),
1928  page.GetWidthMils(),
1929  page.GetHeightMils(),
1930  !page.IsCustom() && page.IsPortrait() ? " portrait" : "" );
1931  m_out->Print( 0, "encoding utf-8\n" );
1932  m_out->Print( 0, "Sheet %d %d\n", screen->GetVirtualPageNumber(), screen->GetPageCount() );
1933  m_out->Print( 0, "Title %s\n", EscapedUTF8( tb.GetTitle() ).c_str() );
1934  m_out->Print( 0, "Date %s\n", EscapedUTF8( tb.GetDate() ).c_str() );
1935  m_out->Print( 0, "Rev %s\n", EscapedUTF8( tb.GetRevision() ).c_str() );
1936  m_out->Print( 0, "Comp %s\n", EscapedUTF8( tb.GetCompany() ).c_str() );
1937  m_out->Print( 0, "Comment1 %s\n", EscapedUTF8( tb.GetComment( 0 ) ).c_str() );
1938  m_out->Print( 0, "Comment2 %s\n", EscapedUTF8( tb.GetComment( 1 ) ).c_str() );
1939  m_out->Print( 0, "Comment3 %s\n", EscapedUTF8( tb.GetComment( 2 ) ).c_str() );
1940  m_out->Print( 0, "Comment4 %s\n", EscapedUTF8( tb.GetComment( 3 ) ).c_str() );
1941  m_out->Print( 0, "Comment5 %s\n", EscapedUTF8( tb.GetComment( 4 ) ).c_str() );
1942  m_out->Print( 0, "Comment6 %s\n", EscapedUTF8( tb.GetComment( 5 ) ).c_str() );
1943  m_out->Print( 0, "Comment7 %s\n", EscapedUTF8( tb.GetComment( 6 ) ).c_str() );
1944  m_out->Print( 0, "Comment8 %s\n", EscapedUTF8( tb.GetComment( 7 ) ).c_str() );
1945  m_out->Print( 0, "Comment9 %s\n", EscapedUTF8( tb.GetComment( 8 ) ).c_str() );
1946  m_out->Print( 0, "$EndDescr\n" );
1947 
1948  for( const auto& alias : screen->GetBusAliases() )
1949  {
1950  saveBusAlias( alias );
1951  }
1952 
1953  // Enforce item ordering
1954  auto cmp = []( const SCH_ITEM* a, const SCH_ITEM* b ) { return *a < *b; };
1955  std::multiset<SCH_ITEM*, decltype( cmp )> save_map( cmp );
1956 
1957  for( auto item : screen->Items() )
1958  save_map.insert( item );
1959 
1960 
1961  for( auto& item : save_map )
1962  {
1963  switch( item->Type() )
1964  {
1965  case SCH_SYMBOL_T:
1966  saveSymbol( static_cast<SCH_SYMBOL*>( item ) );
1967  break;
1968  case SCH_BITMAP_T:
1969  saveBitmap( static_cast<SCH_BITMAP*>( item ) );
1970  break;
1971  case SCH_SHEET_T:
1972  saveSheet( static_cast<SCH_SHEET*>( item ) );
1973  break;
1974  case SCH_JUNCTION_T:
1975  saveJunction( static_cast<SCH_JUNCTION*>( item ) );
1976  break;
1977  case SCH_NO_CONNECT_T:
1978  saveNoConnect( static_cast<SCH_NO_CONNECT*>( item ) );
1979  break;
1980  case SCH_BUS_WIRE_ENTRY_T:
1981  case SCH_BUS_BUS_ENTRY_T:
1982  saveBusEntry( static_cast<SCH_BUS_ENTRY_BASE*>( item ) );
1983  break;
1984  case SCH_LINE_T:
1985  saveLine( static_cast<SCH_LINE*>( item ) );
1986  break;
1987  case SCH_TEXT_T:
1988  case SCH_LABEL_T:
1989  case SCH_GLOBAL_LABEL_T:
1990  case SCH_HIER_LABEL_T:
1991  saveText( static_cast<SCH_TEXT*>( item ) );
1992  break;
1993  default:
1994  wxASSERT( "Unexpected schematic object type in SCH_LEGACY_PLUGIN::Format()" );
1995  }
1996  }
1997 
1998  m_out->Print( 0, "$EndSCHEMATC\n" );
1999 }
2000 
2001 
2002 void SCH_LEGACY_PLUGIN::Format( SELECTION* aSelection, OUTPUTFORMATTER* aFormatter )
2003 {
2004  m_out = aFormatter;
2005 
2006  for( unsigned i = 0; i < aSelection->GetSize(); ++i )
2007  {
2008  SCH_ITEM* item = (SCH_ITEM*) aSelection->GetItem( i );
2009 
2010  switch( item->Type() )
2011  {
2012  case SCH_SYMBOL_T:
2013  saveSymbol( static_cast< SCH_SYMBOL* >( item ) );
2014  break;
2015  case SCH_BITMAP_T:
2016  saveBitmap( static_cast< SCH_BITMAP* >( item ) );
2017  break;
2018  case SCH_SHEET_T:
2019  saveSheet( static_cast< SCH_SHEET* >( item ) );
2020  break;
2021  case SCH_JUNCTION_T:
2022  saveJunction( static_cast< SCH_JUNCTION* >( item ) );
2023  break;
2024  case SCH_NO_CONNECT_T:
2025  saveNoConnect( static_cast< SCH_NO_CONNECT* >( item ) );
2026  break;
2027  case SCH_BUS_WIRE_ENTRY_T:
2028  case SCH_BUS_BUS_ENTRY_T:
2029  saveBusEntry( static_cast< SCH_BUS_ENTRY_BASE* >( item ) );
2030  break;
2031  case SCH_LINE_T:
2032  saveLine( static_cast< SCH_LINE* >( item ) );
2033  break;
2034  case SCH_TEXT_T:
2035  case SCH_LABEL_T:
2036  case SCH_GLOBAL_LABEL_T:
2037  case SCH_HIER_LABEL_T:
2038  saveText( static_cast< SCH_TEXT* >( item ) );
2039  break;
2040  default:
2041  wxASSERT( "Unexpected schematic object type in SCH_LEGACY_PLUGIN::Format()" );
2042  }
2043  }
2044 }
2045 
2046 
2048 {
2049  std::string name1;
2050  std::string name2;
2051 
2052  static wxString delimiters( wxT( " " ) );
2053 
2054  // This is redundant with the AR entries below, but it makes the files backwards-compatible.
2055  if( aSymbol->GetInstanceReferences().size() > 0 )
2056  {
2057  const SYMBOL_INSTANCE_REFERENCE& instance = aSymbol->GetInstanceReferences()[0];
2058  name1 = toUTFTildaText( instance.m_Reference );
2059  }
2060  else
2061  {
2062  if( aSymbol->GetField( REFERENCE_FIELD )->GetText().IsEmpty() )
2063  name1 = toUTFTildaText( aSymbol->GetPrefix() );
2064  else
2065  name1 = toUTFTildaText( aSymbol->GetField( REFERENCE_FIELD )->GetText() );
2066  }
2067 
2068  wxString symbol_name = aSymbol->GetLibId().Format();
2069 
2070  if( symbol_name.size() )
2071  {
2072  name2 = toUTFTildaText( symbol_name );
2073  }
2074  else
2075  {
2076  name2 = "_NONAME_";
2077  }
2078 
2079  m_out->Print( 0, "$Comp\n" );
2080  m_out->Print( 0, "L %s %s\n", name2.c_str(), name1.c_str() );
2081 
2082  // Generate unit number, conversion and timestamp
2083  m_out->Print( 0, "U %d %d %8.8X\n",
2084  aSymbol->GetUnit(),
2085  aSymbol->GetConvert(),
2086  aSymbol->m_Uuid.AsLegacyTimestamp() );
2087 
2088  // Save the position
2089  m_out->Print( 0, "P %d %d\n",
2090  Iu2Mils( aSymbol->GetPosition().x ),
2091  Iu2Mils( aSymbol->GetPosition().y ) );
2092 
2093  /* If this is a complex hierarchy; save hierarchical references.
2094  * but for simple hierarchies it is not necessary.
2095  * the reference inf is already saved
2096  * this is useful for old Eeschema version compatibility
2097  */
2098  if( aSymbol->GetInstanceReferences().size() > 1 )
2099  {
2100  for( const SYMBOL_INSTANCE_REFERENCE& instance : aSymbol->GetInstanceReferences() )
2101  {
2102  /*format:
2103  * AR Path="/140/2" Ref="C99" Part="1"
2104  * where 140 is the uid of the containing sheet and 2 is the timestamp of this symbol.
2105  * (timestamps are actually 8 hex chars)
2106  * Ref is the conventional symbol reference designator for this 'path'
2107  * Part is the conventional symbol unit selection for this 'path'
2108  */
2109  wxString path = "/";
2110 
2111  // Skip root sheet
2112  for( int i = 1; i < (int) instance.m_Path.size(); ++i )
2113  path += instance.m_Path[i].AsLegacyTimestampString() + "/";
2114 
2115  m_out->Print( 0, "AR Path=\"%s\" Ref=\"%s\" Part=\"%d\" \n",
2116  TO_UTF8( path + aSymbol->m_Uuid.AsLegacyTimestampString() ),
2117  TO_UTF8( instance.m_Reference ),
2118  instance.m_Unit );
2119  }
2120  }
2121 
2122  // update the ugly field id, which I would like to see go away someday soon.
2123  for( int i = 0; i < aSymbol->GetFieldCount(); ++i )
2124  aSymbol->GetFields()[i].SetId( i );
2125 
2126  // Fixed fields:
2127  // Save mandatory fields even if they are blank,
2128  // because the visibility, size and orientation are set from library editor.
2129  for( unsigned i = 0; i < MANDATORY_FIELDS; ++i )
2130  saveField( &aSymbol->GetFields()[i] );
2131 
2132  // User defined fields:
2133  // The *policy* about which user defined fields are symbol of a symbol is now
2134  // only in the dialog editors. No policy should be enforced here, simply
2135  // save all the user defined fields, they are present because a dialog editor
2136  // thought they should be. If you disagree, go fix the dialog editors.
2137  for( int i = MANDATORY_FIELDS; i < aSymbol->GetFieldCount(); ++i )
2138  saveField( &aSymbol->GetFields()[i] );
2139 
2140  // Unit number, position, box ( old standard )
2141  m_out->Print( 0, "\t%-4d %-4d %-4d\n", aSymbol->GetUnit(),
2142  Iu2Mils( aSymbol->GetPosition().x ),
2143  Iu2Mils( aSymbol->GetPosition().y ) );
2144 
2145  TRANSFORM transform = aSymbol->GetTransform();
2146 
2147  m_out->Print( 0, "\t%-4d %-4d %-4d %-4d\n",
2148  transform.x1, transform.y1, transform.x2, transform.y2 );
2149  m_out->Print( 0, "$EndComp\n" );
2150 }
2151 
2152 
2154 {
2155  char hjustify = 'C';
2156 
2157  if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
2158  hjustify = 'L';
2159  else if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
2160  hjustify = 'R';
2161 
2162  char vjustify = 'C';
2163 
2164  if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
2165  vjustify = 'B';
2166  else if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
2167  vjustify = 'T';
2168 
2169  m_out->Print( 0, "F %d %s %c %-3d %-3d %-3d %4.4X %c %c%c%c",
2170  aField->GetId(),
2171  EscapedUTF8( aField->GetText() ).c_str(), // wraps in quotes too
2172  aField->GetTextAngle() == TEXT_ANGLE_HORIZ ? 'H' : 'V',
2173  Iu2Mils( aField->GetLibPosition().x ),
2174  Iu2Mils( aField->GetLibPosition().y ),
2175  Iu2Mils( aField->GetTextWidth() ),
2176  !aField->IsVisible(),
2177  hjustify, vjustify,
2178  aField->IsItalic() ? 'I' : 'N',
2179  aField->IsBold() ? 'B' : 'N' );
2180 
2181  // Save field name, if the name is user definable
2182  if( aField->GetId() >= MANDATORY_FIELDS )
2183  m_out->Print( 0, " %s", EscapedUTF8( aField->GetName() ).c_str() );
2184 
2185  m_out->Print( 0, "\n" );
2186 }
2187 
2188 
2190 {
2191  wxCHECK_RET( aBitmap != nullptr, "SCH_BITMAP* is NULL" );
2192 
2193  const wxImage* image = aBitmap->GetImage()->GetImageData();
2194 
2195  wxCHECK_RET( image != nullptr, "wxImage* is NULL" );
2196 
2197  m_out->Print( 0, "$Bitmap\n" );
2198  m_out->Print( 0, "Pos %-4d %-4d\n",
2199  Iu2Mils( aBitmap->GetPosition().x ),
2200  Iu2Mils( aBitmap->GetPosition().y ) );
2201  m_out->Print( 0, "Scale %f\n", aBitmap->GetImage()->GetScale() );
2202  m_out->Print( 0, "Data\n" );
2203 
2204  wxMemoryOutputStream stream;
2205 
2206  image->SaveFile( stream, wxBITMAP_TYPE_PNG );
2207 
2208  // Write binary data in hexadecimal form (ASCII)
2209  wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
2210  char* begin = (char*) buffer->GetBufferStart();
2211 
2212  for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
2213  {
2214  if( ii >= 32 )
2215  {
2216  ii = 0;
2217 
2218  m_out->Print( 0, "\n" );
2219  }
2220 
2221  m_out->Print( 0, "%2.2X ", *begin & 0xFF );
2222  }
2223 
2224  m_out->Print( 0, "\nEndData\n" );
2225  m_out->Print( 0, "$EndBitmap\n" );
2226 }
2227 
2228 
2230 {
2231  wxCHECK_RET( aSheet != nullptr, "SCH_SHEET* is NULL" );
2232 
2233  m_out->Print( 0, "$Sheet\n" );
2234  m_out->Print( 0, "S %-4d %-4d %-4d %-4d\n",
2235  Iu2Mils( aSheet->GetPosition().x ),
2236  Iu2Mils( aSheet->GetPosition().y ),
2237  Iu2Mils( aSheet->GetSize().x ),
2238  Iu2Mils( aSheet->GetSize().y ) );
2239 
2240  m_out->Print( 0, "U %8.8X\n", aSheet->m_Uuid.AsLegacyTimestamp() );
2241 
2242  SCH_FIELD& sheetName = aSheet->GetFields()[SHEETNAME];
2243  SCH_FIELD& fileName = aSheet->GetFields()[SHEETFILENAME];
2244 
2245  if( !sheetName.GetText().IsEmpty() )
2246  m_out->Print( 0, "F0 %s %d\n",
2247  EscapedUTF8( sheetName.GetText() ).c_str(),
2248  Iu2Mils( sheetName.GetTextSize().x ) );
2249 
2250  if( !fileName.GetText().IsEmpty() )
2251  m_out->Print( 0, "F1 %s %d\n",
2252  EscapedUTF8( fileName.GetText() ).c_str(),
2253  Iu2Mils( fileName.GetTextSize().x ) );
2254 
2255  for( const SCH_SHEET_PIN* pin : aSheet->GetPins() )
2256  {
2257  int type, side;
2258 
2259  if( pin->GetText().IsEmpty() )
2260  break;
2261 
2262  switch( pin->GetSide() )
2263  {
2264  default:
2265  case SHEET_SIDE::LEFT: side = 'L'; break;
2266  case SHEET_SIDE::RIGHT: side = 'R'; break;
2267  case SHEET_SIDE::TOP: side = 'T'; break;
2268  case SHEET_SIDE::BOTTOM: side = 'B'; break;
2269  }
2270 
2271  switch( pin->GetShape() )
2272  {
2273  default:
2274  case PINSHEETLABEL_SHAPE::PS_UNSPECIFIED: type = 'U'; break;
2275  case PINSHEETLABEL_SHAPE::PS_INPUT: type = 'I'; break;
2276  case PINSHEETLABEL_SHAPE::PS_OUTPUT: type = 'O'; break;
2277  case PINSHEETLABEL_SHAPE::PS_BIDI: type = 'B'; break;
2278  case PINSHEETLABEL_SHAPE::PS_TRISTATE: type = 'T'; break;
2279  }
2280 
2281  m_out->Print( 0, "F%d %s %c %c %-3d %-3d %-3d\n",
2282  pin->GetNumber(),
2283  EscapedUTF8( pin->GetText() ).c_str(), // supplies wrapping quotes
2284  type, side, Iu2Mils( pin->GetPosition().x ),
2285  Iu2Mils( pin->GetPosition().y ),
2286  Iu2Mils( pin->GetTextWidth() ) );
2287  }
2288 
2289  m_out->Print( 0, "$EndSheet\n" );
2290 }
2291 
2292 
2294 {
2295  wxCHECK_RET( aJunction != nullptr, "SCH_JUNCTION* is NULL" );
2296 
2297  m_out->Print( 0, "Connection ~ %-4d %-4d\n",
2298  Iu2Mils( aJunction->GetPosition().x ),
2299  Iu2Mils( aJunction->GetPosition().y ) );
2300 }
2301 
2302 
2304 {
2305  wxCHECK_RET( aNoConnect != nullptr, "SCH_NOCONNECT* is NULL" );
2306 
2307  m_out->Print( 0, "NoConn ~ %-4d %-4d\n",
2308  Iu2Mils( aNoConnect->GetPosition().x ),
2309  Iu2Mils( aNoConnect->GetPosition().y ) );
2310 }
2311 
2312 
2314 {
2315  wxCHECK_RET( aBusEntry != nullptr, "SCH_BUS_ENTRY_BASE* is NULL" );
2316 
2317  if( aBusEntry->GetLayer() == LAYER_WIRE )
2318  m_out->Print( 0, "Entry Wire Line\n\t%-4d %-4d %-4d %-4d\n",
2319  Iu2Mils( aBusEntry->GetPosition().x ),
2320  Iu2Mils( aBusEntry->GetPosition().y ),
2321  Iu2Mils( aBusEntry->GetEnd().x ), Iu2Mils( aBusEntry->GetEnd().y ) );
2322  else
2323  m_out->Print( 0, "Entry Bus Bus\n\t%-4d %-4d %-4d %-4d\n",
2324  Iu2Mils( aBusEntry->GetPosition().x ),
2325  Iu2Mils( aBusEntry->GetPosition().y ),
2326  Iu2Mils( aBusEntry->GetEnd().x ), Iu2Mils( aBusEntry->GetEnd().y ) );
2327 }
2328 
2329 
2331 {
2332  wxCHECK_RET( aLine != nullptr, "SCH_LINE* is NULL" );
2333 
2334  const char* layer = "Notes";
2335  const char* width = "Line";
2336 
2337  if( aLine->GetLayer() == LAYER_WIRE )
2338  layer = "Wire";
2339  else if( aLine->GetLayer() == LAYER_BUS )
2340  layer = "Bus";
2341 
2342  m_out->Print( 0, "Wire %s %s", layer, width );
2343 
2344  // Write line style (width, type, color) only for non default values
2345  if( aLine->IsGraphicLine() )
2346  {
2347  if( aLine->GetLineSize() != 0 )
2348  m_out->Print( 0, " %s %d", T_WIDTH, Iu2Mils( aLine->GetLineSize() ) );
2349 
2350  if( aLine->GetLineStyle() != aLine->GetDefaultStyle() )
2351  m_out->Print( 0, " %s %s", T_STYLE,
2353 
2354  if( aLine->GetLineColor() != COLOR4D::UNSPECIFIED )
2355  m_out->Print( 0, " %s",
2356  TO_UTF8( aLine->GetLineColor().ToColour().GetAsString( wxC2S_CSS_SYNTAX ) ) );
2357  }
2358 
2359  m_out->Print( 0, "\n" );
2360 
2361  m_out->Print( 0, "\t%-4d %-4d %-4d %-4d",
2362  Iu2Mils( aLine->GetStartPoint().x ), Iu2Mils( aLine->GetStartPoint().y ),
2363  Iu2Mils( aLine->GetEndPoint().x ), Iu2Mils( aLine->GetEndPoint().y ) );
2364 
2365  m_out->Print( 0, "\n");
2366 }
2367 
2368 
2370 {
2371  wxCHECK_RET( aText != nullptr, "SCH_TEXT* is NULL" );
2372 
2373  const char* italics = "~";
2374  const char* textType = "Notes";
2375 
2376  if( aText->IsItalic() )
2377  italics = "Italic";
2378 
2379  wxString text = aText->GetText();
2380 
2381  SCH_LAYER_ID layer = aText->GetLayer();
2382 
2383  if( layer == LAYER_NOTES || layer == LAYER_LOCLABEL )
2384  {
2385  if( layer == LAYER_NOTES )
2386  {
2387  // For compatibility reasons, the text must be saved in only one text line
2388  // so replace all EOLs with \\n
2389  text.Replace( wxT( "\n" ), wxT( "\\n" ) );
2390 
2391  // Here we should have no CR or LF character in line
2392  // This is not always the case if a multiline text was copied (using a copy/paste
2393  // function) from a text that uses E.O.L characters that differs from the current
2394  // EOL format. This is mainly the case under Linux using LF symbol when copying
2395  // a text from Windows (using CRLF symbol) so we must just remove the extra CR left
2396  // (or LF left under MacOSX)
2397  for( unsigned ii = 0; ii < text.Len(); )
2398  {
2399  if( text[ii] == 0x0A || text[ii] == 0x0D )
2400  text.erase( ii, 1 );
2401  else
2402  ii++;
2403  }
2404  }
2405  else
2406  {
2407  textType = "Label";
2408  }
2409 
2410  // Local labels must have their spin style inverted for left and right
2411  int spinStyle = static_cast<int>( aText->GetLabelSpinStyle() );
2412 
2413  if( spinStyle == 0 )
2414  spinStyle = 2;
2415  else if( spinStyle == 2 )
2416  spinStyle = 0;
2417 
2418  m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %d\n%s\n", textType,
2419  Iu2Mils( aText->GetPosition().x ), Iu2Mils( aText->GetPosition().y ),
2420  spinStyle,
2421  Iu2Mils( aText->GetTextWidth() ),
2422  italics, Iu2Mils( aText->GetTextThickness() ), TO_UTF8( text ) );
2423  }
2424  else if( layer == LAYER_GLOBLABEL || layer == LAYER_HIERLABEL )
2425  {
2426  textType = ( layer == LAYER_GLOBLABEL ) ? "GLabel" : "HLabel";
2427 
2428  auto shapeLabelIt = sheetLabelNames.find( aText->GetShape() );
2429  wxCHECK_RET( shapeLabelIt != sheetLabelNames.end(), "Shape not found in names list" );
2430 
2431  m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %s %d\n%s\n", textType,
2432  Iu2Mils( aText->GetPosition().x ), Iu2Mils( aText->GetPosition().y ),
2433  static_cast<int>( aText->GetLabelSpinStyle() ),
2434  Iu2Mils( aText->GetTextWidth() ),
2435  shapeLabelIt->second,
2436  italics,
2437  Iu2Mils( aText->GetTextThickness() ), TO_UTF8( text ) );
2438  }
2439 }
2440 
2441 
2442 void SCH_LEGACY_PLUGIN::saveBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
2443 {
2444  wxCHECK_RET( aAlias != nullptr, "BUS_ALIAS* is NULL" );
2445 
2446  wxString members = boost::algorithm::join( aAlias->Members(), " " );
2447 
2448  m_out->Print( 0, "BusAlias %s %s\n",
2449  TO_UTF8( aAlias->GetName() ), TO_UTF8( members ) );
2450 }
2451 
2452 
2453 int SCH_LEGACY_PLUGIN_CACHE::s_modHash = 1; // starts at 1 and goes up
2455 
2456 
2457 SCH_LEGACY_PLUGIN_CACHE::SCH_LEGACY_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) :
2458  m_fileName( aFullPathAndFileName ),
2459  m_libFileName( aFullPathAndFileName ),
2460  m_isWritable( true ),
2461  m_isModified( false )
2462 {
2463  m_versionMajor = -1;
2464  m_versionMinor = -1;
2466 }
2467 
2468 
2470 {
2471  // When the cache is destroyed, all of the alias objects on the heap should be deleted.
2472  for( auto& symbol : m_symbols )
2473  delete symbol.second;
2474 
2475  m_symbols.clear();
2476 }
2477 
2478 
2479 // If m_libFileName is a symlink follow it to the real source file
2481 {
2482  wxFileName fn( m_libFileName );
2484  return fn;
2485 }
2486 
2487 
2489 {
2490  wxFileName fn = GetRealFile();
2491 
2492  // update the writable flag while we have a wxFileName, in a network this
2493  // is possibly quite dynamic anyway.
2494  m_isWritable = fn.IsFileWritable();
2495 
2496  return fn.GetModificationTime();
2497 }
2498 
2499 
2500 bool SCH_LEGACY_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const
2501 {
2502  return m_fileName == aFullPathAndFileName;
2503 }
2504 
2505 
2507 {
2508  wxFileName fn = GetRealFile();
2509 
2510  if( m_fileModTime.IsValid() && fn.IsOk() && fn.FileExists() )
2511  return fn.GetModificationTime() != m_fileModTime;
2512 
2513  return false;
2514 }
2515 
2516 
2518 {
2519  wxCHECK_MSG( aSymbol != nullptr, nullptr, "NULL pointer cannot be removed from library." );
2520 
2521  LIB_SYMBOL* firstChild = nullptr;
2522  LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbol->GetName() );
2523 
2524  if( it == m_symbols.end() )
2525  return nullptr;
2526 
2527  // If the entry pointer doesn't match the name it is mapped to in the library, we
2528  // have done something terribly wrong.
2529  wxCHECK_MSG( *it->second == aSymbol, nullptr,
2530  "Pointer mismatch while attempting to remove alias entry <" + aSymbol->GetName() +
2531  "> from library cache <" + m_libFileName.GetName() + ">." );
2532 
2533  // If the symbol is a root symbol used by other symbols find the first alias that uses
2534  // the root symbol and make it the new root.
2535  if( aSymbol->IsRoot() )
2536  {
2537  for( auto entry : m_symbols )
2538  {
2539  if( entry.second->IsAlias()
2540  && entry.second->GetParent().lock() == aSymbol->SharedPtr() )
2541  {
2542  firstChild = entry.second;
2543  break;
2544  }
2545  }
2546 
2547  if( firstChild )
2548  {
2549  for( LIB_ITEM& drawItem : aSymbol->GetDrawItems() )
2550  {
2551  if( drawItem.Type() == LIB_FIELD_T )
2552  {
2553  LIB_FIELD& field = static_cast<LIB_FIELD&>( drawItem );
2554 
2555  if( firstChild->FindField( field.GetCanonicalName() ) )
2556  continue;
2557  }
2558 
2559  LIB_ITEM* newItem = (LIB_ITEM*) drawItem.Clone();
2560  drawItem.SetParent( firstChild );
2561  firstChild->AddDrawItem( newItem );
2562  }
2563 
2564  // Reparent the remaining aliases.
2565  for( auto entry : m_symbols )
2566  {
2567  if( entry.second->IsAlias()
2568  && entry.second->GetParent().lock() == aSymbol->SharedPtr() )
2569  entry.second->SetParent( firstChild );
2570  }
2571  }
2572  }
2573 
2574  m_symbols.erase( it );
2575  delete aSymbol;
2576  m_isModified = true;
2578  return firstChild;
2579 }
2580 
2581 
2583 {
2584  // aSymbol is cloned in SYMBOL_LIB::AddSymbol(). The cache takes ownership of aSymbol.
2585  wxString name = aSymbol->GetName();
2586  LIB_SYMBOL_MAP::iterator it = m_symbols.find( name );
2587 
2588  if( it != m_symbols.end() )
2589  {
2590  removeSymbol( it->second );
2591  }
2592 
2593  m_symbols[ name ] = const_cast< LIB_SYMBOL* >( aSymbol );
2594  m_isModified = true;
2596 }
2597 
2598 
2600 {
2601  if( !m_libFileName.FileExists() )
2602  {
2603  THROW_IO_ERROR( wxString::Format( _( "Library file '%s' not found." ),
2604  m_libFileName.GetFullPath() ) );
2605  }
2606 
2607  wxCHECK_RET( m_libFileName.IsAbsolute(),
2608  wxString::Format( "Cannot use relative file paths in legacy plugin to "
2609  "open library '%s'.", m_libFileName.GetFullPath() ) );
2610 
2611  wxLogTrace( traceSchLegacyPlugin, "Loading legacy symbol file '%s'",
2612  m_libFileName.GetFullPath() );
2613 
2614  FILE_LINE_READER reader( m_libFileName.GetFullPath() );
2615 
2616  if( !reader.ReadLine() )
2617  THROW_IO_ERROR( _( "Unexpected end of file." ) );
2618 
2619  const char* line = reader.Line();
2620 
2621  if( !strCompare( "EESchema-LIBRARY Version", line, &line ) )
2622  {
2623  // Old .sym files (which are libraries with only one symbol, used to store and reuse shapes)
2624  // EESchema-LIB Version x.x SYMBOL. They are valid files.
2625  if( !strCompare( "EESchema-LIB Version", line, &line ) )
2626  SCH_PARSE_ERROR( "file is not a valid symbol or symbol library file", reader, line );
2627  }
2628 
2629  m_versionMajor = parseInt( reader, line, &line );
2630 
2631  if( *line != '.' )
2632  SCH_PARSE_ERROR( "invalid file version formatting in header", reader, line );
2633 
2634  line++;
2635 
2636  m_versionMinor = parseInt( reader, line, &line );
2637 
2638  if( m_versionMajor < 1 || m_versionMinor < 0 || m_versionMinor > 99 )
2639  SCH_PARSE_ERROR( "invalid file version in header", reader, line );
2640 
2641  // Check if this is a symbol library which is the same as a symbol library but without
2642  // any alias, documentation, footprint filters, etc.
2643  if( strCompare( "SYMBOL", line, &line ) )
2644  {
2645  // Symbol files add date and time stamp info to the header.
2647 
2649  }
2650  else
2651  {
2653  }
2654 
2655  while( reader.ReadLine() )
2656  {
2657  line = reader.Line();
2658 
2659  if( *line == '#' || isspace( *line ) ) // Skip comments and blank lines.
2660  continue;
2661 
2662  // Headers where only supported in older library file formats.
2663  if( m_libType == SCH_LIB_TYPE::LT_EESCHEMA && strCompare( "$HEADER", line ) )
2664  loadHeader( reader );
2665 
2666  if( strCompare( "DEF", line ) )
2667  {
2668  // Read one DEF/ENDDEF symbol entry from library:
2669  LIB_SYMBOL* symbol = LoadPart( reader, m_versionMajor, m_versionMinor, &m_symbols );
2670 
2671  m_symbols[ symbol->GetName() ] = symbol;
2672  }
2673  }
2674 
2676 
2677  // Remember the file modification time of library file when the
2678  // cache snapshot was made, so that in a networked environment we will
2679  // reload the cache as needed.
2681 
2683  loadDocs();
2684 }
2685 
2686 
2688 {
2689  const char* line;
2690  wxString text;
2691  wxString aliasName;
2692  wxFileName fn = m_libFileName;
2693  LIB_SYMBOL* symbol = nullptr;;
2694 
2695  fn.SetExt( DOC_EXT );
2696 
2697  // Not all libraries will have a document file.
2698  if( !fn.FileExists() )
2699  return;
2700 
2701  if( !fn.IsFileReadable() )
2702  {
2703  THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to read library '%s'." ),
2704  fn.GetFullPath() ) );
2705  }
2706 
2707  FILE_LINE_READER reader( fn.GetFullPath() );
2708 
2709  line = reader.ReadLine();
2710 
2711  if( !line )
2712  THROW_IO_ERROR( _( "symbol document library file is empty" ) );
2713 
2714  if( !strCompare( DOCFILE_IDENT, line, &line ) )
2715  SCH_PARSE_ERROR( "invalid document library file version formatting in header",
2716  reader, line );
2717 
2718  while( reader.ReadLine() )
2719  {
2720  line = reader.Line();
2721 
2722  if( *line == '#' ) // Comment line.
2723  continue;
2724 
2725  if( !strCompare( "$CMP", line, &line ) != 0 )
2726  SCH_PARSE_ERROR( "$CMP command expected", reader, line );
2727 
2728  aliasName = wxString::FromUTF8( line );
2729  aliasName.Trim();
2730 
2731  LIB_SYMBOL_MAP::iterator it = m_symbols.find( aliasName );
2732 
2733  if( it == m_symbols.end() )
2734  wxLogWarning( "Symbol '%s' not found in library:\n\n"
2735  "'%s'\n\nat line %d offset %d", aliasName, fn.GetFullPath(),
2736  reader.LineNumber(), (int) (line - reader.Line() ) );
2737  else
2738  symbol = it->second;
2739 
2740  // Read the current alias associated doc.
2741  // if the alias does not exist, just skip the description
2742  // (Can happen if a .dcm is not synchronized with the corresponding .lib file)
2743  while( reader.ReadLine() )
2744  {
2745  line = reader.Line();
2746 
2747  if( !line )
2748  SCH_PARSE_ERROR( "unexpected end of file", reader, line );
2749 
2750  if( strCompare( "$ENDCMP", line, &line ) )
2751  break;
2752 
2753  text = FROM_UTF8( line + 2 );
2754  // Remove spaces at eol, and eol chars:
2755  text = text.Trim();
2756 
2757  switch( line[0] )
2758  {
2759  case 'D':
2760  if( symbol )
2761  symbol->SetDescription( text );
2762  break;
2763 
2764  case 'K':
2765  if( symbol )
2766  symbol->SetKeyWords( text );
2767  break;
2768 
2769  case 'F':
2770  if( symbol )
2771  symbol->GetFieldById( DATASHEET_FIELD )->SetText( text );
2772  break;
2773 
2774  case 0:
2775  case '\n':
2776  case '\r':
2777  case '#':
2778  // Empty line or commment
2779  break;
2780 
2781  default:
2782  SCH_PARSE_ERROR( "expected token in symbol definition", reader, line );
2783  }
2784  }
2785  }
2786 }
2787 
2788 
2790 {
2791  const char* line = aReader.Line();
2792 
2793  wxASSERT( strCompare( "$HEADER", line, &line ) );
2794 
2795  while( aReader.ReadLine() )
2796  {
2797  line = (char*) aReader;
2798 
2799  // The time stamp saved in old library files is not used or saved in the latest
2800  // library file version.
2801  if( strCompare( "TimeStamp", line, &line ) )
2802  continue;
2803  else if( strCompare( "$ENDHEADER", line, &line ) )
2804  return;
2805  }
2806 
2807  SCH_PARSE_ERROR( "$ENDHEADER not found", aReader, line );
2808 }
2809 
2810 
2812  int aMinorVersion, LIB_SYMBOL_MAP* aMap )
2813 {
2814  const char* line = aReader.Line();
2815 
2816  while( *line == '#' )
2817  aReader.ReadLine();
2818 
2819  if( !strCompare( "DEF", line, &line ) )
2820  SCH_PARSE_ERROR( "invalid symbol definition", aReader, line );
2821 
2822  long num;
2823  size_t pos = 4; // "DEF" plus the first space.
2824  wxString utf8Line = wxString::FromUTF8( line );
2825  wxStringTokenizer tokens( utf8Line, " \r\n\t" );
2826 
2827  if( tokens.CountTokens() < 8 )
2828  SCH_PARSE_ERROR( "invalid symbol definition", aReader, line );
2829 
2830  // Read DEF line:
2831  std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
2832 
2833  wxString name, prefix, tmp;
2834 
2835  name = tokens.GetNextToken();
2836 
2837  // Don't escape symbol library name if it's already been escaped. Given that the original
2838  // change to escape the symbol library name has resulted in rescue libraries with escaped
2839  // names, we will have to live with the consequences.
2840  if( name == UnescapeString( name ) )
2842 
2843  pos += name.size() + 1;
2844 
2845  prefix = tokens.GetNextToken();
2846  pos += prefix.size() + 1;
2847 
2848  tmp = tokens.GetNextToken();
2849  pos += tmp.size() + 1; // NumOfPins, unused.
2850 
2851  tmp = tokens.GetNextToken(); // Pin name offset.
2852 
2853  if( !tmp.ToLong( &num ) )
2854  THROW_PARSE_ERROR( "invalid pin offset", aReader.GetSource(), aReader.Line(),
2855  aReader.LineNumber(), pos );
2856 
2857  pos += tmp.size() + 1;
2858  symbol->SetPinNameOffset( Mils2Iu( (int)num ) );
2859 
2860  tmp = tokens.GetNextToken(); // Show pin numbers.
2861 
2862  if( !( tmp == "Y" || tmp == "N") )
2863  THROW_PARSE_ERROR( "expected Y or N", aReader.GetSource(), aReader.Line(),
2864  aReader.LineNumber(), pos );
2865 
2866  pos += tmp.size() + 1;
2867  symbol->SetShowPinNumbers( ( tmp == "N" ) ? false : true );
2868 
2869  tmp = tokens.GetNextToken(); // Show pin names.
2870 
2871  if( !( tmp == "Y" || tmp == "N") )
2872  THROW_PARSE_ERROR( "expected Y or N", aReader.GetSource(), aReader.Line(),
2873  aReader.LineNumber(), pos );
2874 
2875  pos += tmp.size() + 1;
2876  symbol->SetShowPinNames( ( tmp == "N" ) ? false : true );
2877 
2878  tmp = tokens.GetNextToken(); // Number of units.
2879 
2880  if( !tmp.ToLong( &num ) )
2881  THROW_PARSE_ERROR( "invalid unit count", aReader.GetSource(), aReader.Line(),
2882  aReader.LineNumber(), pos );
2883 
2884  pos += tmp.size() + 1;
2885  symbol->SetUnitCount( (int)num );
2886 
2887  // Ensure m_unitCount is >= 1. Could be read as 0 in old libraries.
2888  if( symbol->GetUnitCount() < 1 )
2889  symbol->SetUnitCount( 1 );
2890 
2891  // Copy symbol name and prefix.
2892 
2893  // The root alias is added to the alias list by SetName() which is called by SetText().
2894  if( name.IsEmpty() )
2895  {
2896  symbol->SetName( "~" );
2897  }
2898  else if( name[0] != '~' )
2899  {
2900  symbol->SetName( name );
2901  }
2902  else
2903  {
2904  symbol->SetName( name.Right( name.Length() - 1 ) );
2905  symbol->GetValueField().SetVisible( false );
2906  }
2907 
2908  // Don't set the library alias, this is determined by the symbol library table.
2909  symbol->SetLibId( LIB_ID( wxEmptyString, symbol->GetName() ) );
2910 
2911  LIB_FIELD& reference = symbol->GetReferenceField();
2912 
2913  if( prefix == "~" )
2914  {
2915  reference.Empty();
2916  reference.SetVisible( false );
2917  }
2918  else
2919  {
2920  reference.SetText( prefix );
2921  }
2922 
2923  // In version 2.2 and earlier, this parameter was a '0' which was just a place holder.
2924  // The was no concept of interchangeable multiple unit symbols.
2925  if( LIB_VERSION( aMajorVersion, aMinorVersion ) > 0
2926  && LIB_VERSION( aMajorVersion, aMinorVersion ) <= LIB_VERSION( 2, 2 ) )
2927  {
2928  // Nothing needs to be set since the default setting for symbols with multiple
2929  // units were never interchangeable. Just parse the 0 an move on.
2930  tmp = tokens.GetNextToken();
2931  pos += tmp.size() + 1;
2932  }
2933  else
2934  {
2935  tmp = tokens.GetNextToken();
2936 
2937  if( tmp == "L" )
2938  symbol->LockUnits( true );
2939  else if( tmp == "F" || tmp == "0" )
2940  symbol->LockUnits( false );
2941  else
2942  THROW_PARSE_ERROR( "expected L, F, or 0", aReader.GetSource(), aReader.Line(),
2943  aReader.LineNumber(), pos );
2944 
2945  pos += tmp.size() + 1;
2946  }
2947 
2948  // There is the optional power symbol flag.
2949  if( tokens.HasMoreTokens() )
2950  {
2951  tmp = tokens.GetNextToken();
2952 
2953  if( tmp == "P" )
2954  symbol->SetPower();
2955  else if( tmp == "N" )
2956  symbol->SetNormal();
2957  else
2958  THROW_PARSE_ERROR( "expected P or N", aReader.GetSource(), aReader.Line(),
2959  aReader.LineNumber(), pos );
2960  }
2961 
2962  line = aReader.ReadLine();
2963 
2964  // Read lines until "ENDDEF" is found.
2965  while( line )
2966  {
2967  if( *line == '#' ) // Comment
2968  ;
2969  else if( strCompare( "Ti", line, &line ) ) // Modification date is ignored.
2970  continue;
2971  else if( strCompare( "ALIAS", line, &line ) ) // Aliases
2972  loadAliases( symbol, aReader, aMap );
2973  else if( *line == 'F' ) // Fields
2974  loadField( symbol, aReader );
2975  else if( strCompare( "DRAW", line, &line ) ) // Drawing objects.
2976  loadDrawEntries( symbol, aReader, aMajorVersion, aMinorVersion );
2977  else if( strCompare( "$FPLIST", line, &line ) ) // Footprint filter list
2978  loadFootprintFilters( symbol, aReader );
2979  else if( strCompare( "ENDDEF", line, &line ) ) // End of symbol description
2980  {
2981  return symbol.release();
2982  }
2983 
2984  line = aReader.ReadLine();
2985  }
2986 
2987  SCH_PARSE_ERROR( "missing ENDDEF", aReader, line );
2988 }
2989 
2990 
2991 void SCH_LEGACY_PLUGIN_CACHE::loadAliases( std::unique_ptr<LIB_SYMBOL>& aSymbol,
2992  LINE_READER& aReader,
2993  LIB_SYMBOL_MAP* aMap )
2994 {
2995  wxString newAliasName;
2996  const char* line = aReader.Line();
2997 
2998  wxCHECK_RET( strCompare( "ALIAS", line, &line ), "Invalid ALIAS section" );
2999 
3000  wxString utf8Line = wxString::FromUTF8( line );
3001  wxStringTokenizer tokens( utf8Line, " \r\n\t" );
3002 
3003  // Parse the ALIAS list.
3004  while( tokens.HasMoreTokens() )
3005  {
3006  newAliasName = tokens.GetNextToken();
3007 
3008  if( aMap )
3009  {
3010  LIB_SYMBOL* newSymbol = new LIB_SYMBOL( newAliasName );
3011 
3012  // Inherit the parent mandatory field attributes.
3013  for( int id = 0; id < MANDATORY_FIELDS; ++id )
3014  {
3015  LIB_FIELD* field = newSymbol->GetFieldById( id );
3016 
3017  // the MANDATORY_FIELDS are exactly that in RAM.
3018  wxASSERT( field );
3019 
3020  LIB_FIELD* parentField = aSymbol->GetFieldById( id );
3021 
3022  wxASSERT( parentField );
3023 
3024  *field = *parentField;
3025 
3026  if( id == VALUE_FIELD )
3027  field->SetText( newAliasName );
3028 
3029  field->SetParent( newSymbol );
3030  }
3031 
3032  newSymbol->SetParent( aSymbol.get() );
3033 
3034  // This will prevent duplicate aliases.
3035  (*aMap)[ newSymbol->GetName() ] = newSymbol;
3036  }
3037  }
3038 }
3039 
3040 
3041 void SCH_LEGACY_PLUGIN_CACHE::loadField( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3042  LINE_READER& aReader )
3043 {
3044  const char* line = aReader.Line();
3045 
3046  wxCHECK_RET( *line == 'F', "Invalid field line" );
3047 
3048  wxString text;
3049  int id;
3050 
3051  if( sscanf( line + 1, "%d", &id ) != 1 || id < 0 )
3052  SCH_PARSE_ERROR( "invalid field ID", aReader, line + 1 );
3053 
3054  LIB_FIELD* field;
3055 
3056  if( id >= 0 && id < MANDATORY_FIELDS )
3057  {
3058  field = aSymbol->GetFieldById( id );
3059 
3060  // this will fire only if somebody broke a constructor or editor.
3061  // MANDATORY_FIELDS are always present in ram resident symbols, no
3062  // exceptions, and they always have their names set, even fixed fields.
3063  wxASSERT( field );
3064  }
3065  else
3066  {
3067  field = new LIB_FIELD( aSymbol.get(), id );
3068  aSymbol->AddDrawItem( field, false );
3069  }
3070 
3071  // Skip to the first double quote.
3072  while( *line != '"' && *line != 0 )
3073  line++;
3074 
3075  if( *line == 0 )
3076  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, line );
3077 
3078  parseQuotedString( text, aReader, line, &line, true );
3079 
3080  // The value field needs to be "special" escaped. The other fields are
3081  // escaped normally and don't need special handling
3082  if( id == VALUE_FIELD )
3084 
3085  // Doctor the *.lib file field which has a "~" in blank fields. New saves will
3086  // not save like this.
3087  if( text.size() == 1 && text[0] == '~' )
3088  field->SetText( wxEmptyString );
3089  else
3091 
3092  wxPoint pos;
3093 
3094  pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
3095  pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
3096  field->SetPosition( pos );
3097 
3098  wxSize textSize;
3099 
3100  textSize.x = textSize.y = Mils2Iu( parseInt( aReader, line, &line ) );
3101  field->SetTextSize( textSize );
3102 
3103  char textOrient = parseChar( aReader, line, &line );
3104 
3105  if( textOrient == 'H' )
3106  field->SetTextAngle( TEXT_ANGLE_HORIZ );
3107  else if( textOrient == 'V' )
3108  field->SetTextAngle( TEXT_ANGLE_VERT );
3109  else
3110  SCH_PARSE_ERROR( "invalid field text orientation parameter", aReader, line );
3111 
3112  char textVisible = parseChar( aReader, line, &line );
3113 
3114  if( textVisible == 'V' )
3115  field->SetVisible( true );
3116  else if ( textVisible == 'I' )
3117  field->SetVisible( false );
3118  else
3119  SCH_PARSE_ERROR( "invalid field text visibility parameter", aReader, line );
3120 
3121  // It may be technically correct to use the library version to determine if the field text
3122  // attributes are present. If anyone knows if that is valid and what version that would be,
3123  // please change this to test the library version rather than an EOL or the quoted string
3124  // of the field name.
3125  if( *line != 0 && *line != '"' )
3126  {
3127  char textHJustify = parseChar( aReader, line, &line );
3128 
3129  if( textHJustify == 'C' )
3131  else if( textHJustify == 'L' )
3133  else if( textHJustify == 'R' )
3135  else
3136  SCH_PARSE_ERROR( "invalid field text horizontal justification", aReader, line );
3137 
3138  wxString attributes;
3139 
3140  parseUnquotedString( attributes, aReader, line, &line );
3141 
3142  size_t attrSize = attributes.size();
3143 
3144  if( !(attrSize == 3 || attrSize == 1 ) )
3145  SCH_PARSE_ERROR( "invalid field text attributes size", aReader, line );
3146 
3147  switch( (wxChar) attributes[0] )
3148  {
3149  case 'C': field->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); break;
3150  case 'B': field->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
3151  case 'T': field->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
3152  default: SCH_PARSE_ERROR( "invalid field text vertical justification", aReader, line );
3153  }
3154 
3155  if( attrSize == 3 )
3156  {
3157  wxChar attr_1 = attributes[1];
3158  wxChar attr_2 = attributes[2];
3159 
3160  if( attr_1 == 'I' ) // Italic
3161  field->SetItalic( true );
3162  else if( attr_1 != 'N' ) // No italics is default, check for error.
3163  SCH_PARSE_ERROR( "invalid field text italic parameter", aReader, line );
3164 
3165  if ( attr_2 == 'B' ) // Bold
3166  field->SetBold( true );
3167  else if( attr_2 != 'N' ) // No bold is default, check for error.
3168  SCH_PARSE_ERROR( "invalid field text bold parameter", aReader, line );
3169  }
3170  }
3171 
3172  // Fields in RAM must always have names.
3173  if( id >= 0 && id < MANDATORY_FIELDS )
3174  {
3175  // Fields in RAM must always have names, because we are trying to get
3176  // less dependent on field ids and more dependent on names.
3177  // Plus assumptions are made in the field editors.
3179 
3180  // Ensure the VALUE field = the symbol name (can be not the case
3181  // with malformed libraries: edited by hand, or converted from other tools)
3182  if( id == VALUE_FIELD )
3183  field->SetText( aSymbol->GetName() );
3184  }
3185  else
3186  {
3187  parseQuotedString( field->m_name, aReader, line, &line, true ); // Optional.
3188  }
3189 }
3190 
3191 
3192 void SCH_LEGACY_PLUGIN_CACHE::loadDrawEntries( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3193  LINE_READER& aReader,
3194  int aMajorVersion,
3195  int aMinorVersion )
3196 {
3197  const char* line = aReader.Line();
3198 
3199  wxCHECK_RET( strCompare( "DRAW", line, &line ), "Invalid DRAW section" );
3200 
3201  line = aReader.ReadLine();
3202 
3203  while( line )
3204  {
3205  if( strCompare( "ENDDRAW", line, &line ) )
3206  {
3207  aSymbol->GetDrawItems().sort();
3208  return;
3209  }
3210 
3211  switch( line[0] )
3212  {
3213  case 'A': // Arc
3214  aSymbol->AddDrawItem( loadArc( aSymbol, aReader ), false );
3215  break;
3216 
3217  case 'C': // Circle
3218  aSymbol->AddDrawItem( loadCircle( aSymbol, aReader ), false );
3219  break;
3220 
3221  case 'T': // Text
3222  aSymbol->AddDrawItem( loadText( aSymbol, aReader, aMajorVersion,
3223  aMinorVersion ), false );
3224  break;
3225 
3226  case 'S': // Square
3227  aSymbol->AddDrawItem( loadRect( aSymbol, aReader ), false );
3228  break;
3229 
3230  case 'X': // Pin Description
3231  aSymbol->AddDrawItem( loadPin( aSymbol, aReader ), false );
3232  break;
3233 
3234  case 'P': // Polyline
3235  aSymbol->AddDrawItem( loadPolyLine( aSymbol, aReader ), false );
3236  break;
3237 
3238  case 'B': // Bezier Curves
3239  aSymbol->AddDrawItem( loadBezier( aSymbol, aReader ), false );
3240  break;
3241 
3242  case '#': // Comment
3243  case '\n': // Empty line
3244  case '\r':
3245  case 0:
3246  break;
3247 
3248  default:
3249  SCH_PARSE_ERROR( "undefined DRAW entry", aReader, line );
3250  }
3251 
3252  line = aReader.ReadLine();
3253  }
3254 
3255  SCH_PARSE_ERROR( "File ended prematurely loading symbol draw element.", aReader, line );
3256 }
3257 
3258 
3260  const char** aOutput )
3261 {
3262  switch ( parseChar( aReader, aLine, aOutput ) )
3263  {
3264  case 'F': return FILL_T::FILLED_SHAPE;
3265  case 'f': return FILL_T::FILLED_WITH_BG_BODYCOLOR;
3266  case 'N': return FILL_T::NO_FILL;
3267  default: SCH_PARSE_ERROR( "invalid fill type, expected f, F, or N", aReader, aLine );
3268  }
3269 
3270  // This will never be reached but quiets the compiler warnings
3271  return FILL_T::NO_FILL;
3272 }
3273 
3274 
3275 LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3276  LINE_READER& aReader )
3277 {
3278  const char* line = aReader.Line();
3279 
3280  wxCHECK_MSG( strCompare( "A", line, &line ), nullptr, "Invalid arc definition" );
3281 
3282  LIB_SHAPE* arc = new LIB_SHAPE( aSymbol.get(), SHAPE_T::ARC );
3283 
3284  wxPoint center;
3285 
3286  center.x = Mils2Iu( parseInt( aReader, line, &line ) );
3287  center.y = Mils2Iu( parseInt( aReader, line, &line ) );
3288 
3289  arc->SetPosition( center );
3290 
3291  int radius = Mils2Iu( parseInt( aReader, line, &line ) );
3292  int angle1 = parseInt( aReader, line, &line );
3293  int angle2 = parseInt( aReader, line, &line );
3294 
3295  NORMALIZE_ANGLE_POS( angle1 );
3296  NORMALIZE_ANGLE_POS( angle2 );
3297 
3298  arc->SetUnit( parseInt( aReader, line, &line ) );
3299  arc->SetConvert( parseInt( aReader, line, &line ) );
3300  arc->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
3301 
3302  // Old libraries (version <= 2.2) do not have always this FILL MODE param
3303  // when fill mode is no fill (default mode).
3304  if( *line != 0 )
3305  arc->SetFillMode( parseFillMode( aReader, line, &line ) );
3306 
3307  // Actual Coordinates of arc ends are read from file
3308  if( *line != 0 )
3309  {
3310  wxPoint arcStart, arcEnd;
3311 
3312  arcStart.x = Mils2Iu( parseInt( aReader, line, &line ) );
3313  arcStart.y = Mils2Iu( parseInt( aReader, line, &line ) );
3314  arcEnd.x = Mils2Iu( parseInt( aReader, line, &line ) );
3315  arcEnd.y = Mils2Iu( parseInt( aReader, line, &line ) );
3316 
3317  arc->SetStart( arcStart );
3318  arc->SetEnd( arcEnd );
3319  }
3320  else
3321  {
3322  // Actual Coordinates of arc ends are not read from file
3323  // (old library), calculate them
3324  wxPoint arcStart( radius, 0 );
3325  wxPoint arcEnd( radius, 0 );
3326 
3327  RotatePoint( &arcStart.x, &arcStart.y, -angle1 );
3328  arcStart += arc->GetCenter();
3329  arc->SetStart( arcStart );
3330  RotatePoint( &arcEnd.x, &arcEnd.y, -angle2 );
3331  arcEnd += arc->GetCenter();
3332  arc->SetEnd( arcEnd );
3333  }
3334 
3342  if( !TRANSFORM().MapAngles( &angle1, &angle2 ) )
3343  {
3344  wxPoint temp = arc->GetStart();
3345  arc->SetStart( arc->GetEnd() );
3346  arc->SetEnd( temp );
3347  }
3348 
3349  return arc;
3350 }
3351 
3352 
3353 LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadCircle( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3354  LINE_READER& aReader )
3355 {
3356  const char* line = aReader.Line();
3357 
3358  wxCHECK_MSG( strCompare( "C", line, &line ), nullptr, "Invalid circle definition" );
3359 
3360  LIB_SHAPE* circle = new LIB_SHAPE( aSymbol.get(), SHAPE_T::CIRCLE );
3361 
3362  wxPoint center;
3363 
3364  center.x = Mils2Iu( parseInt( aReader, line, &line ) );
3365  center.y = Mils2Iu( parseInt( aReader, line, &line ) );
3366 
3367  int radius = Mils2Iu( parseInt( aReader, line, &line ) );
3368 
3369  circle->SetStart( center );
3370  circle->SetEnd( wxPoint( center.x + radius, center.y ) );
3371  circle->SetUnit( parseInt( aReader, line, &line ) );
3372  circle->SetConvert( parseInt( aReader, line, &line ) );
3373  circle->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
3374 
3375  if( *line != 0 )
3376  circle->SetFillMode( parseFillMode( aReader, line, &line ) );
3377 
3378  return circle;
3379 }
3380 
3381 
3382 LIB_TEXT* SCH_LEGACY_PLUGIN_CACHE::loadText( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3383  LINE_READER& aReader,
3384  int aMajorVersion,
3385  int aMinorVersion )
3386 {
3387  const char* line = aReader.Line();
3388 
3389  wxCHECK_MSG( strCompare( "T", line, &line ), nullptr, "Invalid LIB_TEXT definition" );
3390 
3391  LIB_TEXT* text = new LIB_TEXT( aSymbol.get() );
3392 
3393  text->SetTextAngle( (double) parseInt( aReader, line, &line ) );
3394 
3395  wxPoint center;
3396 
3397  center.x = Mils2Iu( parseInt( aReader, line, &line ) );
3398  center.y = Mils2Iu( parseInt( aReader, line, &line ) );
3399  text->SetPosition( center );
3400 
3401  wxSize size;
3402 
3403  size.x = size.y = Mils2Iu( parseInt( aReader, line, &line ) );
3404  text->SetTextSize( size );
3405  text->SetVisible( !parseInt( aReader, line, &line ) );
3406  text->SetUnit( parseInt( aReader, line, &line ) );
3407  text->SetConvert( parseInt( aReader, line, &line ) );
3408 
3409  wxString str;
3410 
3411  // If quoted string loading fails, load as not quoted string.
3412  if( *line == '"' )
3413  {
3414  parseQuotedString( str, aReader, line, &line );
3415 
3416  str = ConvertToNewOverbarNotation( str );
3417  }
3418  else
3419  {
3420  parseUnquotedString( str, aReader, line, &line );
3421 
3422  // In old libs, "spaces" are replaced by '~' in unquoted strings:
3423  str.Replace( "~", " " );
3424  }
3425 
3426  if( !str.IsEmpty() )
3427  {
3428  // convert two apostrophes back to double quote
3429  str.Replace( "''", "\"" );
3430  }
3431 
3432  text->SetText( str );
3433 
3434  // Here things are murky and not well defined. At some point it appears the format
3435  // was changed to add text properties. However rather than add the token to the end of
3436  // the text definition, it was added after the string and no mention if the file
3437  // verion was bumped or not so this code make break on very old symbol libraries.
3438  //
3439  // Update: apparently even in the latest version this can be different so added a test
3440  // for end of line before checking for the text properties.
3441  if( LIB_VERSION( aMajorVersion, aMinorVersion ) > 0
3442  && LIB_VERSION( aMajorVersion, aMinorVersion ) > LIB_VERSION( 2, 0 ) && !is_eol( *line ) )
3443  {
3444  if( strCompare( "Italic", line, &line ) )
3445  text->SetItalic( true );
3446  else if( !strCompare( "Normal", line, &line ) )
3447  SCH_PARSE_ERROR( "invalid text stype, expected 'Normal' or 'Italic'", aReader, line );
3448 
3449  if( parseInt( aReader, line, &line ) > 0 )
3450  text->SetBold( true );
3451 
3452  // Some old libaries version > 2.0 do not have these options for text justification:
3453  if( !is_eol( *line ) )
3454  {
3455  switch( parseChar( aReader, line, &line ) )
3456  {
3457  case 'L': text->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break;
3458  case 'C': text->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); break;
3459  case 'R': text->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); break;
3460  default: SCH_PARSE_ERROR( "invalid horizontal text justication; expected L, C, or R",
3461  aReader, line );
3462  }
3463 
3464  switch( parseChar( aReader, line, &line ) )
3465  {
3466  case 'T': text->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
3467  case 'C': text->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); break;
3468  case 'B': text->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
3469  default: SCH_PARSE_ERROR( "invalid vertical text justication; expected T, C, or B",
3470  aReader, line );
3471  }
3472  }
3473  }
3474 
3475  return text;
3476 }
3477 
3478 
3479 LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadRect( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3480  LINE_READER& aReader )
3481 {
3482  const char* line = aReader.Line();
3483 
3484  wxCHECK_MSG( strCompare( "S", line, &line ), nullptr, "Invalid rectangle definition" );
3485 
3486  LIB_SHAPE* rectangle = new LIB_SHAPE( aSymbol.get(), SHAPE_T::RECT );
3487 
3488  wxPoint pos;
3489 
3490  pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
3491  pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
3492  rectangle->SetPosition( pos );
3493 
3494  wxPoint end;
3495 
3496  end.x = Mils2Iu( parseInt( aReader, line, &line ) );
3497  end.y = Mils2Iu( parseInt( aReader, line, &line ) );
3498  rectangle->SetEnd( end );
3499 
3500  rectangle->SetUnit( parseInt( aReader, line, &line ) );
3501  rectangle->SetConvert( parseInt( aReader, line, &line ) );
3502  rectangle->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
3503 
3504  if( *line != 0 )
3505  rectangle->SetFillMode( parseFillMode( aReader, line, &line ) );
3506 
3507  return rectangle;
3508 }
3509 
3510 
3511 LIB_PIN* SCH_LEGACY_PLUGIN_CACHE::loadPin( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3512  LINE_READER& aReader )
3513 {
3514  const char* line = aReader.Line();
3515 
3516  wxCHECK_MSG( strCompare( "X", line, &line ), nullptr, "Invalid LIB_PIN definition" );
3517 
3518  wxString name;
3519  wxString number;
3520 
3521  size_t pos = 2; // "X" plus ' ' space character.
3522  wxString tmp;
3523  wxString utf8Line = wxString::FromUTF8( line );
3524  wxStringTokenizer tokens( utf8Line, " \r\n\t" );
3525 
3526  if( tokens.CountTokens() < 11 )
3527  SCH_PARSE_ERROR( "invalid pin definition", aReader, line );
3528 
3529  tmp = tokens.GetNextToken();
3530  name = tmp;
3531  pos += tmp.size() + 1;
3532 
3533  tmp = tokens.GetNextToken();
3534  number = tmp ;
3535  pos += tmp.size() + 1;
3536 
3537  long num;
3538  wxPoint position;
3539 
3540  tmp = tokens.GetNextToken();
3541 
3542  if( !tmp.ToLong( &num ) )
3543  THROW_PARSE_ERROR( "invalid pin X coordinate", aReader.GetSource(), aReader.Line(),
3544  aReader.LineNumber(), pos );
3545 
3546  pos += tmp.size() + 1;
3547  position.x = Mils2Iu( (int) num );
3548 
3549  tmp = tokens.GetNextToken();
3550 
3551  if( !tmp.ToLong( &num ) )
3552  THROW_PARSE_ERROR( "invalid pin Y coordinate", aReader.GetSource(), aReader.Line(),
3553  aReader.LineNumber(), pos );
3554 
3555  pos += tmp.size() + 1;
3556  position.y = Mils2Iu( (int) num );
3557 
3558  tmp = tokens.GetNextToken();
3559 
3560  if( !tmp.ToLong( &num ) )
3561  THROW_PARSE_ERROR( "invalid pin length", aReader.GetSource(), aReader.Line(),
3562  aReader.LineNumber(), pos );
3563 
3564  pos += tmp.size() + 1;
3565  int length = Mils2Iu( (int) num );
3566 
3567 
3568  tmp = tokens.GetNextToken();
3569 
3570  if( tmp.size() > 1 )
3571  THROW_PARSE_ERROR( "invalid pin orientation", aReader.GetSource(), aReader.Line(),
3572  aReader.LineNumber(), pos );
3573 
3574  pos += tmp.size() + 1;
3575  int orientation = tmp[0];
3576 
3577  tmp = tokens.GetNextToken();
3578 
3579  if( !tmp.ToLong( &num ) )
3580  THROW_PARSE_ERROR( "invalid pin number text size", aReader.GetSource(), aReader.Line(),
3581  aReader.LineNumber(), pos );
3582 
3583  pos += tmp.size() + 1;
3584  int numberTextSize = Mils2Iu( (int) num );
3585 
3586  tmp = tokens.GetNextToken();
3587 
3588  if( !tmp.ToLong( &num ) )
3589  THROW_PARSE_ERROR( "invalid pin name text size", aReader.GetSource(), aReader.Line(),
3590  aReader.LineNumber(), pos );
3591 
3592  pos += tmp.size() + 1;
3593  int nameTextSize = Mils2Iu( (int) num );
3594 
3595  tmp = tokens.GetNextToken();
3596 
3597  if( !tmp.ToLong( &num ) )
3598  THROW_PARSE_ERROR( "invalid pin unit", aReader.GetSource(), aReader.Line(),
3599  aReader.LineNumber(), pos );
3600 
3601  pos += tmp.size() + 1;
3602  int unit = (int) num;
3603 
3604  tmp = tokens.GetNextToken();
3605 
3606  if( !tmp.ToLong( &num ) )
3607  THROW_PARSE_ERROR( "invalid pin alternate body type", aReader.GetSource(), aReader.Line(),
3608  aReader.LineNumber(), pos );
3609 
3610  pos += tmp.size() + 1;
3611  int convert = (int) num;
3612 
3613  tmp = tokens.GetNextToken();
3614 
3615  if( tmp.size() != 1 )
3616  THROW_PARSE_ERROR( "invalid pin type", aReader.GetSource(), aReader.Line(),
3617  aReader.LineNumber(), pos );
3618 
3619  pos += tmp.size() + 1;
3620  char type = tmp[0];
3621  ELECTRICAL_PINTYPE pinType;
3622 
3623  switch( type )
3624  {
3625  case 'I': pinType = ELECTRICAL_PINTYPE::PT_INPUT; break;
3626  case 'O': pinType = ELECTRICAL_PINTYPE::PT_OUTPUT; break;
3627  case 'B': pinType = ELECTRICAL_PINTYPE::PT_BIDI; break;
3628  case 'T': pinType = ELECTRICAL_PINTYPE::PT_TRISTATE; break;
3629  case 'P': pinType = ELECTRICAL_PINTYPE::PT_PASSIVE; break;
3630  case 'U': pinType = ELECTRICAL_PINTYPE::PT_UNSPECIFIED; break;
3631  case 'W': pinType = ELECTRICAL_PINTYPE::PT_POWER_IN; break;
3632  case 'w': pinType = ELECTRICAL_PINTYPE::PT_POWER_OUT; break;
3633  case 'C': pinType = ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR; break;
3634  case 'E': pinType = ELECTRICAL_PINTYPE::PT_OPENEMITTER; break;
3635  case 'N': pinType = ELECTRICAL_PINTYPE::PT_NC; break;
3636  default:
3637  THROW_PARSE_ERROR( "unknown pin type", aReader.GetSource(), aReader.Line(),
3638  aReader.LineNumber(), pos );
3639  }
3640 
3641 
3642  LIB_PIN* pin = new LIB_PIN( aSymbol.get(),
3644  ConvertToNewOverbarNotation( number ),
3645  orientation,
3646  pinType,
3647  length,
3648  nameTextSize,
3649  numberTextSize,
3650  convert,
3651  position,
3652  unit );
3653 
3654  // Optional
3655  if( tokens.HasMoreTokens() ) /* Special Symbol defined */
3656  {
3657  tmp = tokens.GetNextToken();
3658 
3659  enum
3660  {
3661  INVERTED = 1 << 0,
3662  CLOCK = 1 << 1,
3663  LOWLEVEL_IN = 1 << 2,
3664  LOWLEVEL_OUT = 1 << 3,
3665  FALLING_EDGE = 1 << 4,
3666  NONLOGIC = 1 << 5
3667  };
3668 
3669  int flags = 0;
3670 
3671  for( int j = tmp.size(); j > 0; )
3672  {
3673  switch( tmp[--j].GetValue() )
3674  {
3675  case '~': break;
3676  case 'N': pin->SetVisible( false ); break;
3677  case 'I': flags |= INVERTED; break;
3678  case 'C': flags |= CLOCK; break;
3679  case 'L': flags |= LOWLEVEL_IN; break;
3680  case 'V': flags |= LOWLEVEL_OUT; break;
3681  case 'F': flags |= FALLING_EDGE; break;
3682  case 'X': flags |= NONLOGIC; break;
3683  default: THROW_PARSE_ERROR( "invalid pin attribut", aReader.GetSource(),
3684  aReader.Line(), aReader.LineNumber(), pos );
3685  }
3686 
3687  pos += 1;
3688  }
3689 
3690  switch( flags )
3691  {
3692  case 0: pin->SetShape( GRAPHIC_PINSHAPE::LINE ); break;
3693  case INVERTED: pin->SetShape( GRAPHIC_PINSHAPE::INVERTED ); break;
3694  case CLOCK: pin->SetShape( GRAPHIC_PINSHAPE::CLOCK ); break;
3695  case INVERTED | CLOCK: pin->SetShape( GRAPHIC_PINSHAPE::INVERTED_CLOCK ); break;
3696  case LOWLEVEL_IN: pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW ); break;
3697  case LOWLEVEL_IN | CLOCK: pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW ); break;
3698  case LOWLEVEL_OUT: pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW ); break;
3699  case FALLING_EDGE: pin->SetShape( GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK ); break;
3700  case NONLOGIC: pin->SetShape( GRAPHIC_PINSHAPE::NONLOGIC ); break;
3701  default:
3702  SCH_PARSE_ERROR( "pin attributes do not define a valid pin shape", aReader, line );
3703  }
3704  }
3705 
3706  return pin;
3707 }
3708 
3709 
3710 LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadPolyLine( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3711  LINE_READER& aReader )
3712 {
3713  const char* line = aReader.Line();
3714 
3715  wxCHECK_MSG( strCompare( "P", line, &line ), nullptr, "Invalid poly definition" );
3716 
3717  LIB_SHAPE* polyLine = new LIB_SHAPE( aSymbol.get(), SHAPE_T::POLY );
3718 
3719  int points = parseInt( aReader, line, &line );
3720  polyLine->SetUnit( parseInt( aReader, line, &line ) );
3721  polyLine->SetConvert( parseInt( aReader, line, &line ) );
3722  polyLine->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
3723 
3724  wxPoint pt;
3725 
3726  for( int i = 0; i < points; i++ )
3727  {
3728  pt.x = Mils2Iu( parseInt( aReader, line, &line ) );
3729  pt.y = Mils2Iu( parseInt( aReader, line, &line ) );
3730  polyLine->AddPoint( pt );
3731  }
3732 
3733  if( *line != 0 )
3734  polyLine->SetFillMode( parseFillMode( aReader, line, &line ) );
3735 
3736  return polyLine;
3737 }
3738 
3739 
3740 LIB_SHAPE* SCH_LEGACY_PLUGIN_CACHE::loadBezier( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3741  LINE_READER& aReader )
3742 {
3743  const char* line = aReader.Line();
3744 
3745  wxCHECK_MSG( strCompare( "B", line, &line ), nullptr, "Invalid Bezier definition" );
3746 
3747  int points = parseInt( aReader, line, &line );
3748 
3749  wxCHECK_MSG( points == 4, NULL, "Invalid Bezier curve definition" );
3750 
3751  LIB_SHAPE* bezier = new LIB_SHAPE( aSymbol.get(), SHAPE_T::BEZIER );
3752 
3753  bezier->SetUnit( parseInt( aReader, line, &line ) );
3754  bezier->SetConvert( parseInt( aReader, line, &line ) );
3755  bezier->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
3756 
3757  bezier->SetStart( wxPoint( Mils2Iu( parseInt( aReader, line, &line ) ),
3758  Mils2Iu( parseInt( aReader, line, &line ) ) ) );
3759 
3760  bezier->SetBezierC1( wxPoint( Mils2Iu( parseInt( aReader, line, &line ) ),
3761  Mils2Iu( parseInt( aReader, line, &line ) ) ) );
3762 
3763  bezier->SetBezierC2( wxPoint( Mils2Iu( parseInt( aReader, line, &line ) ),
3764  Mils2Iu( parseInt( aReader, line, &line ) ) ) );
3765 
3766  bezier->SetEnd( wxPoint( Mils2Iu( parseInt( aReader, line, &line ) ),
3767  Mils2Iu( parseInt( aReader, line, &line ) ) ) );
3768 
3769  bezier->RebuildBezierToSegmentsPointsList( bezier->GetWidth() );
3770 
3771  if( *line != 0 )
3772  bezier->SetFillMode( parseFillMode( aReader, line, &line ) );
3773 
3774  return bezier;
3775 }
3776 
3777 
3778 void SCH_LEGACY_PLUGIN_CACHE::loadFootprintFilters( std::unique_ptr<LIB_SYMBOL>& aSymbol,
3779  LINE_READER& aReader )
3780 {
3781  const char* line = aReader.Line();
3782 
3783  wxCHECK_RET( strCompare( "$FPLIST", line, &line ), "Invalid footprint filter list" );
3784 
3785  line = aReader.ReadLine();
3786 
3787  wxArrayString footprintFilters;
3788 
3789  while( line )
3790  {
3791  if( strCompare( "$ENDFPLIST", line, &line ) )
3792  {
3793  aSymbol->SetFPFilters( footprintFilters );
3794  return;
3795  }
3796 
3797  wxString footprint;
3798 
3799  parseUnquotedString( footprint, aReader, line, &line );
3800  footprintFilters.Add( footprint );
3801  line = aReader.ReadLine();
3802  }
3803 
3804  SCH_PARSE_ERROR( "File ended prematurely while loading footprint filters.", aReader, line );
3805 }
3806 
3807 
3808 void SCH_LEGACY_PLUGIN_CACHE::Save( bool aSaveDocFile )
3809 {
3810  if( !m_isModified )
3811  return;
3812 
3813  // Write through symlinks, don't replace them
3814  wxFileName fn = GetRealFile();
3815 
3816  auto formatter = std::make_unique<FILE_OUTPUTFORMATTER>( fn.GetFullPath() );
3817  formatter->Print( 0, "%s %d.%d\n", LIBFILE_IDENT, LIB_VERSION_MAJOR, LIB_VERSION_MINOR );
3818  formatter->Print( 0, "#encoding utf-8\n");
3819 
3820  for( LIB_SYMBOL_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); it++ )
3821  {
3822  if( !it->second->IsRoot() )
3823  continue;
3824 
3825  SaveSymbol( it->second, *formatter.get(), &m_symbols );
3826  }
3827 
3828  formatter->Print( 0, "#\n#End Library\n" );
3829  formatter.reset();
3830 
3831  m_fileModTime = fn.GetModificationTime();
3832  m_isModified = false;
3833 
3834  if( aSaveDocFile )
3835  saveDocFile();
3836 }
3837 
3838 
3840  LIB_SYMBOL_MAP* aMap )
3841 {
3842  /*
3843  * NB:
3844  * Some of the rescue code still uses the legacy format as an intermediary, so we have
3845  * to keep this code.
3846  */
3847 
3848  wxCHECK_RET( aSymbol && aSymbol->IsRoot(), "Invalid LIB_SYMBOL pointer." );
3849 
3850  // LIB_ALIAS objects are deprecated but we still need to gather up the derived symbols
3851  // and save their names for the old file format.
3852  wxArrayString aliasNames;
3853 
3854  if( aMap )
3855  {
3856  for( auto entry : *aMap )
3857  {
3858  LIB_SYMBOL* symbol = entry.second;
3859 
3860  if( symbol->IsAlias() && symbol->GetParent().lock() == aSymbol->SharedPtr() )
3861  aliasNames.Add( symbol->GetName() );
3862  }
3863  }
3864 
3865  LIB_FIELD& value = aSymbol->GetValueField();
3866 
3867  // First line: it s a comment (symbol name for readers)
3868  aFormatter.Print( 0, "#\n# %s\n#\n", TO_UTF8( value.GetText() ) );
3869 
3870  // Save data
3871  aFormatter.Print( 0, "DEF" );
3872  aFormatter.Print( 0, " %s", TO_UTF8( value.GetText() ) );
3873 
3874  LIB_FIELD& reference = aSymbol->GetReferenceField();
3875 
3876  if( !reference.GetText().IsEmpty() )
3877  aFormatter.Print( 0, " %s", TO_UTF8( reference.GetText() ) );
3878  else
3879  aFormatter.Print( 0, " ~" );
3880 
3881  aFormatter.Print( 0, " %d %d %c %c %d %c %c\n",
3882  0, Iu2Mils( aSymbol->GetPinNameOffset() ),
3883  aSymbol->ShowPinNumbers() ? 'Y' : 'N',
3884  aSymbol->ShowPinNames() ? 'Y' : 'N',
3885  aSymbol->GetUnitCount(), aSymbol->UnitsLocked() ? 'L' : 'F',
3886  aSymbol->IsPower() ? 'P' : 'N' );
3887 
3888  timestamp_t dateModified = aSymbol->GetLastModDate();
3889 
3890  if( dateModified != 0 )
3891  {
3892  int sec = dateModified & 63;
3893  int min = ( dateModified >> 6 ) & 63;
3894  int hour = ( dateModified >> 12 ) & 31;
3895  int day = ( dateModified >> 17 ) & 31;
3896  int mon = ( dateModified >> 22 ) & 15;
3897  int year = ( dateModified >> 26 ) + 1990;
3898 
3899  aFormatter.Print( 0, "Ti %d/%d/%d %d:%d:%d\n", year, mon, day, hour, min, sec );
3900  }
3901 
3902  std::vector<LIB_FIELD*> fields;
3903  aSymbol->GetFields( fields );
3904 
3905  // Mandatory fields:
3906  // may have their own save policy so there is a separate loop for them.
3907  // Empty fields are saved, because the user may have set visibility,
3908  // size and orientation
3909  for( int i = 0; i < MANDATORY_FIELDS; ++i )
3910  saveField( fields[i], aFormatter );
3911 
3912  // User defined fields:
3913  // may have their own save policy so there is a separate loop for them.
3914  int fieldId = MANDATORY_FIELDS; // really wish this would go away.
3915 
3916  for( unsigned i = MANDATORY_FIELDS; i < fields.size(); ++i )
3917  {
3918  // There is no need to save empty fields, i.e. no reason to preserve field
3919  // names now that fields names come in dynamically through the template
3920  // fieldnames.
3921  if( !fields[i]->GetText().IsEmpty() )
3922  {
3923  fields[i]->SetId( fieldId++ );
3924  saveField( fields[i], aFormatter );
3925  }
3926  }
3927 
3928  // Save the alias list: a line starting by "ALIAS".
3929  if( !aliasNames.IsEmpty() )
3930  {
3931  aFormatter.Print( 0, "ALIAS" );
3932 
3933  for( unsigned i = 0; i < aliasNames.GetCount(); i++ )
3934  aFormatter.Print( 0, " %s", TO_UTF8( aliasNames[i] ) );
3935 
3936  aFormatter.Print( 0, "\n" );
3937  }
3938 
3939  wxArrayString footprints = aSymbol->GetFPFilters();
3940 
3941  // Write the footprint filter list
3942  if( footprints.GetCount() != 0 )
3943  {
3944  aFormatter.Print( 0, "$FPLIST\n" );
3945 
3946  for( unsigned i = 0; i < footprints.GetCount(); i++ )
3947  aFormatter.Print( 0, " %s\n", TO_UTF8( footprints[i] ) );
3948 
3949  aFormatter.Print( 0, "$ENDFPLIST\n" );
3950  }
3951 
3952  // Save graphics items (including pins)
3953  if( !aSymbol->GetDrawItems().empty() )
3954  {
3955  // Sort the draw items in order to editing a file editing by hand.
3956  aSymbol->GetDrawItems().sort();
3957 
3958  aFormatter.Print( 0, "DRAW\n" );
3959 
3960  for( LIB_ITEM& item : aSymbol->GetDrawItems() )
3961  {
3962  switch( item.Type() )
3963  {
3964  default:
3965  case LIB_FIELD_T: /* Fields have already been saved above. */ break;
3966  case LIB_PIN_T: savePin( (LIB_PIN* ) &item, aFormatter ); break;
3967  case LIB_TEXT_T: saveText( ( LIB_TEXT* ) &item, aFormatter ); break;
3968  case LIB_SHAPE_T:
3969  {
3970  LIB_SHAPE& shape = static_cast<LIB_SHAPE&>( item );
3971 
3972  switch( shape.GetShape() )
3973  {
3974  case SHAPE_T::ARC: saveArc( &shape, aFormatter ); break;
3975  case SHAPE_T::BEZIER: saveBezier( &shape, aFormatter ); break;
3976  case SHAPE_T::CIRCLE: saveCircle( &shape, aFormatter ); break;
3977  case SHAPE_T::POLY: savePolyLine( &shape, aFormatter ); break;
3978  case SHAPE_T::RECT: saveRectangle( &shape, aFormatter ); break;
3979  default: break;
3980  }
3981  }
3982  }
3983  }
3984 
3985  aFormatter.Print( 0, "ENDDRAW\n" );
3986  }
3987 
3988  aFormatter.Print( 0, "ENDDEF\n" );
3989 }
3990 
3991 
3993 {
3994  wxCHECK_RET( aArc && aArc->GetShape() == SHAPE_T::ARC, "Invalid ARC object." );
3995 
3996  int x1;
3997  int x2;
3998 
3999  aArc->CalcArcAngles( x1, x2 );
4000 
4001  if( x1 > 1800 )
4002  x1 -= 3600;
4003 
4004  if( x2 > 1800 )
4005  x2 -= 3600;
4006 
4007  aFormatter.Print( 0, "A %d %d %d %d %d %d %d %d %c %d %d %d %d\n",
4008  Iu2Mils( aArc->GetPosition().x ),
4009  Iu2Mils( aArc->GetPosition().y ),
4010  Iu2Mils( aArc->GetRadius() ),
4011  x1,
4012  x2,
4013  aArc->GetUnit(),
4014  aArc->GetConvert(),
4015  Iu2Mils( aArc->GetWidth() ),
4016  fill_tab[ static_cast<int>( aArc->GetFillType() ) - 1 ],
4017  Iu2Mils( aArc->GetStart().x ),
4018  Iu2Mils( aArc->GetStart().y ),
4019  Iu2Mils( aArc->GetEnd().x ),
4020  Iu2Mils( aArc->GetEnd().y ) );
4021 }
4022 
4023 
4025 {
4026  wxCHECK_RET( aBezier && aBezier->GetShape() == SHAPE_T::BEZIER, "Invalid BEZIER object." );
4027 
4028  aFormatter.Print( 0, "B %u %d %d %d",
4029  (unsigned)aBezier->GetBezierPoints().size(),
4030  aBezier->GetUnit(),
4031  aBezier->GetConvert(),
4032  Iu2Mils( aBezier->GetWidth() ) );
4033 
4034  for( const wxPoint& pt : aBezier->GetBezierPoints() )
4035  aFormatter.Print( 0, " %d %d", Iu2Mils( pt.x ), Iu2Mils( pt.y ) );
4036 
4037  aFormatter.Print( 0, " %c\n", fill_tab[ static_cast<int>( aBezier->GetFillType() ) - 1 ] );
4038 }
4039 
4040 
4042 {
4043  wxCHECK_RET( aCircle && aCircle->GetShape() == SHAPE_T::CIRCLE, "Invalid CIRCLE object." );
4044 
4045  aFormatter.Print( 0, "C %d %d %d %d %d %d %c\n",
4046  Iu2Mils( aCircle->GetPosition().x ),
4047  Iu2Mils( aCircle->GetPosition().y ),
4048  Iu2Mils( aCircle->GetRadius() ),
4049  aCircle->GetUnit(),
4050  aCircle->GetConvert(),
4051  Iu2Mils( aCircle->GetWidth() ),
4052  fill_tab[ static_cast<int>( aCircle->GetFillType() ) - 1 ] );
4053 }
4054 
4055 
4057 {
4058  wxCHECK_RET( aField && aField->Type() == LIB_FIELD_T, "Invalid LIB_FIELD object." );
4059 
4060  int hjustify, vjustify;
4061  int id = aField->GetId();
4062  wxString text = aField->GetText();
4063 
4064  hjustify = 'C';
4065 
4066  if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
4067  hjustify = 'L';
4068  else if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
4069  hjustify = 'R';
4070 
4071  vjustify = 'C';
4072 
4073  if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
4074  vjustify = 'B';
4075  else if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
4076  vjustify = 'T';
4077 
4078  aFormatter.Print( 0, "F%d %s %d %d %d %c %c %c %c%c%c",
4079  id,
4080  EscapedUTF8( text ).c_str(), // wraps in quotes
4081  Iu2Mils( aField->GetTextPos().x ),
4082  Iu2Mils( aField->GetTextPos().y ),
4083  Iu2Mils( aField->GetTextWidth() ),
4084  aField->GetTextAngle() == 0 ? 'H' : 'V',
4085  aField->IsVisible() ? 'V' : 'I',
4086  hjustify, vjustify,
4087  aField->IsItalic() ? 'I' : 'N',
4088  aField->IsBold() ? 'B' : 'N' );
4089 
4090  /* Save field name, if necessary
4091  * Field name is saved only if it is not the default name.
4092  * Just because default name depends on the language and can change from
4093  * a country to another
4094  */
4095  wxString defName = TEMPLATE_FIELDNAME::GetDefaultFieldName( id );
4096 
4097  if( id >= MANDATORY_FIELDS && !aField->m_name.IsEmpty() && aField->m_name != defName )
4098  aFormatter.Print( 0, " %s", EscapedUTF8( aField->m_name ).c_str() );
4099 
4100  aFormatter.Print( 0, "\n" );
4101 }
4102 
4103 
4105 {
4106  wxCHECK_RET( aPin && aPin->Type() == LIB_PIN_T, "Invalid LIB_PIN object." );
4107 
4108  int Etype;
4109 
4110  switch( aPin->GetType() )
4111  {
4112  default:
4113  case ELECTRICAL_PINTYPE::PT_INPUT: Etype = 'I'; break;
4114  case ELECTRICAL_PINTYPE::PT_OUTPUT: Etype = 'O'; break;
4115  case ELECTRICAL_PINTYPE::PT_BIDI: Etype = 'B'; break;
4116  case ELECTRICAL_PINTYPE::PT_TRISTATE: Etype = 'T'; break;
4117  case ELECTRICAL_PINTYPE::PT_PASSIVE: Etype = 'P'; break;
4118  case ELECTRICAL_PINTYPE::PT_UNSPECIFIED: Etype = 'U'; break;
4119  case ELECTRICAL_PINTYPE::PT_POWER_IN: Etype = 'W'; break;
4120  case ELECTRICAL_PINTYPE::PT_POWER_OUT: Etype = 'w'; break;
4121  case ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR: Etype = 'C'; break;
4122  case ELECTRICAL_PINTYPE::PT_OPENEMITTER: Etype = 'E'; break;
4123  case ELECTRICAL_PINTYPE::PT_NC: Etype = 'N'; break;
4124  }
4125 
4126  if( !aPin->GetName().IsEmpty() )
4127  aFormatter.Print( 0, "X %s", TO_UTF8( aPin->GetName() ) );
4128  else
4129  aFormatter.Print( 0, "X ~" );
4130 
4131  aFormatter.Print( 0, " %s %d %d %d %c %d %d %d %d %c",
4132  aPin->GetNumber().IsEmpty() ? "~" : TO_UTF8( aPin->GetNumber() ),
4133  Iu2Mils( aPin->GetPosition().x ),
4134  Iu2Mils( aPin->GetPosition().y ),
4135  Iu2Mils( (int) aPin->GetLength() ),
4136  (int) aPin->GetOrientation(),
4137  Iu2Mils( aPin->GetNumberTextSize() ),
4138  Iu2Mils( aPin->GetNameTextSize() ),
4139  aPin->GetUnit(),
4140  aPin->GetConvert(),
4141  Etype );
4142 
4143  if( aPin->GetShape() != GRAPHIC_PINSHAPE::LINE || !aPin->IsVisible() )
4144  aFormatter.Print( 0, " " );
4145 
4146  if( !aPin->IsVisible() )
4147  aFormatter.Print( 0, "N" );
4148 
4149  switch( aPin->GetShape() )
4150  {
4151  case GRAPHIC_PINSHAPE::LINE: break;
4152  case GRAPHIC_PINSHAPE::INVERTED: aFormatter.Print( 0, "I" ); break;
4153  case GRAPHIC_PINSHAPE::CLOCK: aFormatter.Print( 0, "C" ); break;
4154  case GRAPHIC_PINSHAPE::INVERTED_CLOCK: aFormatter.Print( 0, "IC" ); break;
4155  case GRAPHIC_PINSHAPE::INPUT_LOW: aFormatter.Print( 0, "L" ); break;
4156  case GRAPHIC_PINSHAPE::CLOCK_LOW: aFormatter.Print( 0, "CL" ); break;
4157  case GRAPHIC_PINSHAPE::OUTPUT_LOW: aFormatter.Print( 0, "V" ); break;
4158  case GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK: aFormatter.Print( 0, "F" ); break;
4159  case GRAPHIC_PINSHAPE::NONLOGIC: aFormatter.Print( 0, "X" ); break;
4160  default: wxFAIL_MSG( "Invalid pin shape" );
4161  }
4162 
4163  aFormatter.Print( 0, "\n" );
4164 
4165  const_cast<LIB_PIN*>( aPin )->ClearFlags( IS_CHANGED );
4166 }
4167 
4168 
4170 {
4171  wxCHECK_RET( aPolyLine && aPolyLine->GetShape() == SHAPE_T::POLY, "Invalid POLY object." );
4172 
4173  aFormatter.Print( 0, "P %d %d %d %d",
4174  (int) aPolyLine->GetPolyShape().Outline( 0 ).GetPointCount(),
4175  aPolyLine->GetUnit(),
4176  aPolyLine->GetConvert(),
4177  Iu2Mils( aPolyLine->GetWidth() ) );
4178 
4179  for( const VECTOR2I& pt : aPolyLine->GetPolyShape().Outline( 0 ).CPoints() )
4180  aFormatter.Print( 0, " %d %d", Iu2Mils( pt.x ), Iu2Mils( pt.y ) );
4181 
4182  aFormatter.Print( 0, " %c\n", fill_tab[ static_cast<int>( aPolyLine->GetFillType() ) - 1 ] );
4183 }
4184 
4185 
4187 {
4188  wxCHECK_RET( aRectangle && aRectangle->GetShape() == SHAPE_T::RECT, "Invalid RECT object." );
4189 
4190  aFormatter.Print( 0, "S %d %d %d %d %d %d %d %c\n",
4191  Iu2Mils( aRectangle->GetPosition().x ),
4192  Iu2Mils( aRectangle->GetPosition().y ),
4193  Iu2Mils( aRectangle->GetEnd().x ),
4194  Iu2Mils( aRectangle->GetEnd().y ),
4195  aRectangle->GetUnit(),
4196  aRectangle->GetConvert(),
4197  Iu2Mils( aRectangle->GetWidth() ),
4198  fill_tab[ static_cast<int>( aRectangle->GetFillType() ) - 1 ] );
4199 }
4200 
4201 
4203 {
4204  wxCHECK_RET( aText && aText->Type() == LIB_TEXT_T, "Invalid LIB_TEXT object." );
4205 
4206  wxString text = aText->GetText();
4207 
4208  if( text.Contains( wxT( " " ) ) || text.Contains( wxT( "~" ) ) || text.Contains( wxT( "\"" ) ) )
4209  {
4210  // convert double quote to similar-looking two apostrophes
4211  text.Replace( wxT( "\"" ), wxT( "''" ) );
4212  text = wxT( "\"" ) + text + wxT( "\"" );
4213  }
4214 
4215  aFormatter.Print( 0, "T %g %d %d %d %d %d %d %s", aText->GetTextAngle(),
4216  Iu2Mils( aText->GetTextPos().x ), Iu2Mils( aText->GetTextPos().y ),
4217  Iu2Mils( aText->GetTextWidth() ), !aText->IsVisible(),
4218  aText->GetUnit(), aText->GetConvert(), TO_UTF8( text ) );
4219 
4220  aFormatter.Print( 0, " %s %d", aText->IsItalic() ? "Italic" : "Normal", aText->IsBold() );
4221 
4222  char hjustify = 'C';
4223 
4224  if( aText->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
4225  hjustify = 'L';
4226  else if( aText->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
4227  hjustify = 'R';
4228 
4229  char vjustify = 'C';
4230 
4231  if( aText->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
4232  vjustify = 'B';
4233  else if( aText->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
4234  vjustify = 'T';
4235 
4236  aFormatter.Print( 0, " %c %c\n", hjustify, vjustify );
4237 }
4238 
4239 
4241 {
4242  /*
4243  * NB:
4244  * Some of the rescue code still uses the legacy format as an intermediary, so we have
4245  * to keep this code.
4246  */
4247 
4248  wxFileName fileName = m_libFileName;
4249 
4250  fileName.SetExt( DOC_EXT );
4251  FILE_OUTPUTFORMATTER formatter( fileName.GetFullPath() );
4252 
4253  formatter.Print( 0, "%s\n", DOCFILE_IDENT );
4254 
4255  for( LIB_SYMBOL_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); ++it )
4256  {
4257  wxString description = it->second->GetDescription();
4258  wxString keyWords = it->second->GetKeyWords();
4259  wxString docFileName = it->second->GetDatasheetField().GetText();
4260 
4261  if( description.IsEmpty() && keyWords.IsEmpty() && docFileName.IsEmpty() )
4262  continue;
4263 
4264  formatter.Print( 0, "#\n$CMP %s\n", TO_UTF8( it->second->GetName() ) );
4265 
4266  if( !description.IsEmpty() )
4267  formatter.Print( 0, "D %s\n", TO_UTF8( description ) );
4268 
4269  if( !keyWords.IsEmpty() )
4270  formatter.Print( 0, "K %s\n", TO_UTF8( keyWords ) );
4271 
4272  if( !docFileName.IsEmpty() )
4273  formatter.Print( 0, "F %s\n", TO_UTF8( docFileName ) );
4274 
4275  formatter.Print( 0, "$ENDCMP\n" );
4276  }
4277 
4278  formatter.Print( 0, "#\n#End Doc Library\n" );
4279 }
4280 
4281 
4282 void SCH_LEGACY_PLUGIN_CACHE::DeleteSymbol( const wxString& aSymbolName )
4283 {
4284  LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbolName );
4285 
4286  if( it == m_symbols.end() )
4287  THROW_IO_ERROR( wxString::Format( _( "library %s does not contain a symbol named %s" ),
4288  m_libFileName.GetFullName(), aSymbolName ) );
4289 
4290  LIB_SYMBOL* symbol = it->second;
4291 
4292  if( symbol->IsRoot() )
4293  {
4294  LIB_SYMBOL* rootSymbol = symbol;
4295 
4296  // Remove the root symbol and all its children.
4297  m_symbols.erase( it );
4298 
4299  LIB_SYMBOL_MAP::iterator it1 = m_symbols.begin();
4300 
4301  while( it1 != m_symbols.end() )
4302  {
4303  if( it1->second->IsAlias()
4304  && it1->second->GetParent().lock() == rootSymbol->SharedPtr() )
4305  {
4306  delete it1->second;
4307  it1 = m_symbols.erase( it1 );
4308  }
4309  else
4310  {
4311  it1++;
4312  }
4313  }
4314 
4315  delete rootSymbol;
4316  }
4317  else
4318  {
4319  // Just remove the alias.
4320  m_symbols.erase( it );
4321  delete symbol;
4322  }
4323 
4325  m_isModified = true;
4326 }
4327 
4328 
4329 void SCH_LEGACY_PLUGIN::cacheLib( const wxString& aLibraryFileName, const PROPERTIES* aProperties )
4330 {
4331  if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() )
4332  {
4333  // a spectacular episode in memory management:
4334  delete m_cache;
4335  m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryFileName );
4336 
4337  // Because m_cache is rebuilt, increment SYMBOL_LIBS::s_modify_generation
4338  // to modify the hash value that indicate symbol to symbol links
4339  // must be updated.
4341 
4342  if( !isBuffering( aProperties ) )
4343  m_cache->Load();
4344  }
4345 }
4346 
4347 
4349 {
4350  std::string propName( SCH_LEGACY_PLUGIN::PropNoDocFile );
4351 
4352  if( aProperties && aProperties->find( propName ) != aProperties->end() )
4353  return false;
4354 
4355  return true;
4356 }
4357 
4358 
4359 bool SCH_LEGACY_PLUGIN::isBuffering( const PROPERTIES* aProperties )
4360 {
4361  return ( aProperties && aProperties->Exists( SCH_LEGACY_PLUGIN::PropBuffering ) );
4362 }
4363 
4364 
4366 {
4367  if( m_cache )
4369 
4370  // If the cache hasn't been loaded, it hasn't been modified.
4371  return 0;
4372 }
4373 
4374 
4375 void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
4376  const wxString& aLibraryPath,
4377  const PROPERTIES* aProperties )
4378 {
4379  LOCALE_IO toggle; // toggles on, then off, the C locale.
4380 
4381  bool powerSymbolsOnly = ( aProperties &&
4382  aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
4383 
4384  cacheLib( aLibraryPath, aProperties );
4385 
4386  const LIB_SYMBOL_MAP& symbols = m_cache->m_symbols;
4387 
4388  for( LIB_SYMBOL_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it )
4389  {
4390  if( !powerSymbolsOnly || it->second->IsPower() )
4391  aSymbolNameList.Add( it->first );
4392  }
4393 }
4394 
4395 
4396 void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
4397  const wxString& aLibraryPath,
4398  const PROPERTIES* aProperties )
4399 {
4400  LOCALE_IO toggle; // toggles on, then off, the C locale.
4401 
4402  bool powerSymbolsOnly = ( aProperties &&
4403  aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
4404 
4405  cacheLib( aLibraryPath, aProperties );
4406 
4407  const LIB_SYMBOL_MAP& symbols = m_cache->m_symbols;
4408 
4409  for( LIB_SYMBOL_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it )
4410  {
4411  if( !powerSymbolsOnly || it->second->IsPower() )
4412  aSymbolList.push_back( it->second );
4413  }
4414 }
4415 
4416 
4417 LIB_SYMBOL* SCH_LEGACY_PLUGIN::LoadSymbol( const wxString& aLibraryPath,
4418  const wxString& aSymbolName,
4419  const PROPERTIES* aProperties )
4420 {
4421  LOCALE_IO toggle; // toggles on, then off, the C locale.
4422 
4423  cacheLib( aLibraryPath, aProperties );
4424 
4425  LIB_SYMBOL_MAP::const_iterator it = m_cache->m_symbols.find( aSymbolName );
4426 
4427  if( it == m_cache->m_symbols.end() )
4428  return nullptr;
4429 
4430  return it->second;
4431 }
4432 
4433 
4434 void SCH_LEGACY_PLUGIN::SaveSymbol( const wxString& aLibraryPath, const LIB_SYMBOL* aSymbol,
4435  const PROPERTIES* aProperties )
4436 {
4437  LOCALE_IO toggle; // toggles on, then off, the C locale.
4438 
4439  cacheLib( aLibraryPath, aProperties );
4440 
4441  m_cache->AddSymbol( aSymbol );
4442 
4443  if( !isBuffering( aProperties ) )
4444  m_cache->Save( writeDocFile( aProperties ) );
4445 }
4446 
4447 
4448 void SCH_LEGACY_PLUGIN::DeleteSymbol( const wxString& aLibraryPath, const wxString& aSymbolName,
4449  const PROPERTIES* aProperties )
4450 {
4451  LOCALE_IO toggle; // toggles on, then off, the C locale.
4452 
4453  cacheLib( aLibraryPath, aProperties );
4454 
4455  m_cache->DeleteSymbol( aSymbolName );
4456 
4457  if( !isBuffering( aProperties ) )
4458  m_cache->Save( writeDocFile( aProperties ) );
4459 }
4460 
4461 
4462 void SCH_LEGACY_PLUGIN::CreateSymbolLib( const wxString& aLibraryPath,
4463  const PROPERTIES* aProperties )
4464 {
4465  if( wxFileExists( aLibraryPath ) )
4466  {
4467  THROW_IO_ERROR( wxString::Format( _( "Symbol library '%s' already exists." ),
4468  aLibraryPath.GetData() ) );
4469  }
4470 
4471  LOCALE_IO toggle;
4472 
4473  delete m_cache;
4474  m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
4475  m_cache->SetModified();
4476  m_cache->Save( writeDocFile( aProperties ) );
4477  m_cache->Load(); // update m_writable and m_mod_time
4478 }
4479 
4480 
4481 bool SCH_LEGACY_PLUGIN::DeleteSymbolLib( const wxString& aLibraryPath,
4482  const PROPERTIES* aProperties )
4483 {
4484  wxFileName fn = aLibraryPath;
4485 
4486  if( !fn.FileExists() )
4487  return false;
4488 
4489  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
4490  // we don't want that. we want bare metal portability with no UI here.
4491  if( wxRemove( aLibraryPath ) )
4492  {
4493  THROW_IO_ERROR( wxString::Format( _( "Symbol library '%s' cannot be deleted." ),
4494  aLibraryPath.GetData() ) );
4495  }
4496 
4497  if( m_cache && m_cache->IsFile( aLibraryPath ) )
4498  {
4499  delete m_cache;
4500  m_cache = nullptr;
4501  }
4502 
4503  return true;
4504 }
4505 
4506 
4507 void SCH_LEGACY_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const PROPERTIES* aProperties )
4508 {
4509  if( !m_cache )
4510  m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
4511 
4512  wxString oldFileName = m_cache->GetFileName();
4513 
4514  if( !m_cache->IsFile( aLibraryPath ) )
4515  {
4516  m_cache->SetFileName( aLibraryPath );
4517  }
4518 
4519  // This is a forced save.
4520  m_cache->SetModified();
4521  m_cache->Save( writeDocFile( aProperties ) );
4522  m_cache->SetFileName( oldFileName );
4523 }
4524 
4525 
4526 bool SCH_LEGACY_PLUGIN::CheckHeader( const wxString& aFileName )
4527 {
4528  // Open file and check first line
4529  wxTextFile tempFile;
4530 
4531  tempFile.Open( aFileName );
4532  wxString firstline;
4533  // read the first line
4534  firstline = tempFile.GetFirstLine();
4535  tempFile.Close();
4536 
4537  return firstline.StartsWith( "EESchema" );
4538 }
4539 
4540 
4541 bool SCH_LEGACY_PLUGIN::IsSymbolLibWritable( const wxString& aLibraryPath )
4542 {
4543  // Writing legacy symbol libraries is deprecated.
4544  return false;
4545 }
4546 
4547 
4549  int aMinorVersion )
4550 {
4551  return SCH_LEGACY_PLUGIN_CACHE::LoadPart( reader, aMajorVersion, aMinorVersion );
4552 }
4553 
4554 
4556 {
4557  SCH_LEGACY_PLUGIN_CACHE::SaveSymbol( symbol, formatter );
4558 }
4559 
4560 
4561 
4562 const char* SCH_LEGACY_PLUGIN::PropBuffering = "buffering";
4563 const char* SCH_LEGACY_PLUGIN::PropNoDocFile = "no_doc_file";
Field Reference of part, i.e. "IC21".
static LIB_TEXT * loadText(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader, int aMajorVersion, int aMinorVersion)
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
SCH_SHEET * loadSheet(LINE_READER &aReader)
CITER next(CITER it)
Definition: ptree.cpp:126
void CalcArcAngles(int &aStartAngle, int &aEndAngle) const
Definition: lib_shape.cpp:430
power input (GND, VCC for ICs). Must be connected to a power output.
#define TEXT_ANGLE_HORIZ
Frequent text rotations, used with {Set,Get}TextAngle(), in 0.1 degrees for now, hoping to migrate to...
Definition: eda_text.h:71
LIB_FIELD * FindField(const wxString &aFieldName)
Find a field within this symbol matching aFieldName and returns it or NULL if not found.
void LoadContent(LINE_READER &aReader, SCH_SCREEN *aScreen, int version=EESCHEMA_VERSION)
SCH_LIB_TYPE
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:230
static const char * GetLineStyleName(PLOT_DASH_TYPE aStyle)
Definition: sch_line.cpp:105
bool IsBold() const
Definition: eda_text.h:204
SCH_JUNCTION * loadJunction(LINE_READER &aReader)
#define IS_CHANGED
Item was edited, and modified.
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const PROPERTIES *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
void DeleteSymbol(const wxString &aName)
static LIB_SYMBOL * LoadPart(LINE_READER &aReader, int aMajorVersion, int aMinorVersion, LIB_SYMBOL_MAP *aMap=nullptr)
LIB_SYMBOL_REF & GetParent()
Definition: lib_symbol.h:124
int GetPinNameOffset() const
Definition: lib_symbol.h:572
COLOR4D GetLineColor() const
Returns COLOR4D::UNSPECIFIED if a custom color hasn't been set for this line.
Definition: sch_line.cpp:236
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
Symbol map used by symbol library object.
const wxString & GetFileName() const
Definition: sch_screen.h:145
void AddPoint(const wxPoint &aPosition)
Definition: lib_shape.cpp:414
LIB_SYMBOL_SPTR SharedPtr() const
Definition: lib_symbol.h:107
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:80
const std::vector< wxPoint > & GetBezierPoints() const
Definition: eda_shape.h:210
timestamp_t GetLastModDate() const
Definition: lib_symbol.h:179
static void saveCircle(LIB_SHAPE *aCircle, OUTPUTFORMATTER &aFormatter)
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:214
int GetWidth() const
Definition: eda_shape.h:98
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,...
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:693
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:117
EDA_TEXT_VJUSTIFY_T GetVertJustify() const
Definition: eda_text.h:220
bool IsGraphicLine() const
Return if the line is a graphic (non electrical line)
Definition: sch_line.cpp:966
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
virtual size_t GetPointCount() const override
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
void SetVirtualPageNumber(int aPageNumber)
Definition: base_screen.h:76
static void saveBezier(LIB_SHAPE *aBezier, OUTPUTFORMATTER &aFormatter)
int y2
Definition: transform.h:51
virtual const wxString & GetSource() const
Returns the name of the source of the lines in an abstract sense.
Definition: richio.h:109
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:135
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
wxPoint GetStartPoint() const
Definition: sch_line.h:90
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: sch_symbol.cpp:705
int GetPenSizeForBold(int aTextSize)
Definition: gr_text.cpp:46
wxString m_error
For throwing exceptions or errors on partial schematic loads.
static bool strCompare(const char *aString, const char *aLine, const char **aOutput=nullptr)
Compare aString to the string starting at aLine and advances the character point to the end of String...
static void saveRectangle(LIB_SHAPE *aRectangle, OUTPUTFORMATTER &aFormatter)
Holds all the data relating to one schematic.
Definition: schematic.h:59
wxPoint GetPosition() const override
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:106
void SetRevision(const wxString &aRevision)
Definition: title_block.h:81
wxString GetName() const override
Definition: lib_symbol.h:133
Define a symbol library graphical text item.
Definition: lib_text.h:39
void saveLine(SCH_LINE *aLine)
int GetOrientation() const
Definition: lib_pin.h:75
const wxString & GetComment(int aIdx) const
Definition: title_block.h:107
int GetHeightMils() const
Definition: page_info.h:133
static void saveArc(LIB_SHAPE *aArc, OUTPUTFORMATTER &aFormatter)
GRAPHIC_PINSHAPE GetShape() const
Definition: lib_pin.h:78
static void parseUnquotedString(wxString &aString, LINE_READER &aReader, const char *aCurrentToken, const char **aNextToken=nullptr, bool aCanBeEmpty=false)
Parse an unquoted utf8 string and updates the pointer at aOutput if it is not NULL.
SCH_LEGACY_PLUGIN_CACHE * m_cache
static void savePolyLine(LIB_SHAPE *aPolyLine, OUTPUTFORMATTER &aFormatter)
static void loadAliases(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader, LIB_SYMBOL_MAP *aMap=nullptr)
void SetItalic(bool isItalic)
Definition: eda_text.h:200
int color
Definition: DXF_plotter.cpp:57
Field object used in symbol libraries.
Definition: lib_field.h:59
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:175
const wxChar *const traceSchLegacyPlugin
Flag to enable legacy schematic plugin debug output.
bool IsAlias() const
Definition: lib_symbol.h:172
double GetScale() const
Definition: bitmap_base.h:79
int GetVirtualPageNumber() const
Definition: base_screen.h:75
pin for passive symbols: must be connected, and can be connected to any pin
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition: schematic.h:108
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:741
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:267
wxImage * GetImageData()
Definition: bitmap_base.h:70
void AddSymbol(const LIB_SYMBOL *aSymbol)
bool ShowPinNumbers() const
Definition: lib_symbol.h:588
void sort()
Definition: multivector.h:247
void init(SCHEMATIC *aSchematic, const PROPERTIES *aProperties=nullptr)
initialize PLUGIN like a constructor would.
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:119
int x2
Definition: transform.h:50
SCH_SHEET * m_rootSheet
The root sheet of the schematic being loaded.
wxString GetLogicalName() const
double GetTextAngle() const
Definition: eda_text.h:195
int GetId() const
Definition: sch_field.h:113
int GetPageCount() const
Definition: base_screen.h:72
static uint32_t parseHex(LINE_READER &aReader, const char *aLine, const char **aOutput=nullptr)
Parse an ASCII hex integer string with possible leading whitespace into a long integer and updates th...
unknown electrical properties: creates always a warning when connected
void saveText(SCH_TEXT *aText)
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition: title_block.h:71
const TITLE_BLOCK & GetTitleBlock() const
Definition: sch_screen.h:156
SCH_LEGACY_PLUGIN_CACHE(const wxString &aLibraryPath)
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:309
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: sch_screen.h:133
int x1
Definition: transform.h:48
#define MAX_UNIT_COUNT_PER_PACKAGE
The maximum number of units per package.
Definition: eeschema_id.h:44
bool IsPower() const
Definition: lib_symbol.cpp:459
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:77
const wxString & GetType() const
Definition: page_info.h:94
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:258
int GetId() const
Definition: lib_field.h:115
void saveNoConnect(SCH_NO_CONNECT *aNoConnect)
void SetFileExists(bool aFileExists)
Definition: sch_screen.h:150
static double parseDouble(LINE_READER &aReader, const char *aLine, const char **aOutput=nullptr)
Parses an ASCII point string with possible leading whitespace into a double precision floating point ...
LIB_FIELD & GetValueField()
Return reference to the value field.
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
bool IsCustom() const
Definition: page_info.cpp:180
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:290
wxPoint GetPosition() const override
Definition: sch_bitmap.h:136
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
#define LIB_VERSION_MINOR
bool empty(int aType=UNDEFINED_TYPE) const
Definition: multivector.h:242
int GetTextThickness() const
Definition: eda_text.h:181
A name/value tuple with unique names and optional values.
Definition: properties.h:33
void loadHeader(FILE_LINE_READER &aReader)
std::string toUTFTildaText(const wxString &txt)
Convert a wxString to UTF8 and replace any control characters with a ~, where a control character is ...
Definition: sch_symbol.cpp:46
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:102
Define a library symbol object.
Definition: lib_symbol.h:96
std::chrono::steady_clock CLOCK
#define Mils2Iu(x)
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:40
const PAGE_INFO & GetPageSettings() const
Definition: sch_screen.h:132
TRANSFORM & GetTransform()
Definition: sch_symbol.h:232
#define T_WIDTH
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:122
#define SCH_LAYER_ID_COUNT
Definition: layer_ids.h:379
LIB_FIELD & GetReferenceField()
Return reference to the reference designator field.
void saveSheet(SCH_SHEET *aSheet)
void SetBezierC1(const wxPoint &aPt)
Definition: eda_shape.h:153
SCH_BUS_ENTRY_BASE * loadBusEntry(LINE_READER &aReader)
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:115
name of datasheet
FILL_T
Definition: eda_shape.h:53
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition: eda_item.cpp:83
The base class for drawable items used by schematic library symbols.
Definition: lib_item.h:61
void GetFields(std::vector< LIB_FIELD * > &aList)
Return a list of fields within this symbol.
Definition: lib_symbol.cpp:982
A cache assistant for the symbol library portion of the SCH_PLUGIN API, and only for the SCH_LEGACY_P...
void saveField(SCH_FIELD *aField)
static void FormatPart(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter)
#define T_STYLE
Base class for a bus or wire entry.
Definition: sch_bus_entry.h:42
const std::vector< SYMBOL_INSTANCE_REFERENCE > & GetInstanceReferences()
Definition: sch_symbol.h:123
void saveJunction(SCH_JUNCTION *aJunction)
int m_version
Version of file being loaded.
void loadFile(const wxString &aFileName, SCH_SCREEN *aScreen)
bool IsItalic() const
Definition: eda_text.h:201
#define SCHEMATIC_HEAD_STRING
Definition: general.h:36
void saveSymbol(SCH_SYMBOL *aSymbol)
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:110
void Rewind()
Rewind the file and resets the line number back to zero.
Definition: richio.h:222
std::vector< SCH_FIELD > & GetFields()
Definition: sch_sheet.h:90
void DeleteSymbol(const wxString &aLibraryPath, const wxString &aSymbolName, const PROPERTIES *aProperties=nullptr) override
Delete the entire LIB_SYMBOL associated with aAliasName from the library aLibraryPath.
void SetPosition(const wxPoint &aPosition) override
Definition: lib_shape.h:88
void SetFileReadOnly(bool aIsReadOnly)
Definition: sch_screen.h:147
wxString m_path
Root project path for loading child sheets.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
int y1
Definition: transform.h:49
static LIB_PIN * loadPin(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:206
Definition: kiid.h:44
#define EESCHEMA_VERSION
Definition: general.h:35
int GetUnit() const
Definition: lib_item.h:266
PLOT_DASH_TYPE GetDefaultStyle() const
Definition: sch_line.cpp:254
#define DOC_EXT
A LINE_READER that reads from an open file.
Definition: richio.h:172
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:131
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:45
#define DOCFILE_IDENT
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
bool isBuffering(const PROPERTIES *aProperties)
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:219
Field Value of part, i.e. "3.3K".
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:124
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
static void loadDrawEntries(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader, int aMajorVersion, int aMinorVersion)
void loadHierarchy(SCH_SHEET *aSheet)
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
const std::vector< VECTOR2I > & CPoints() const
BITMAP_BASE * GetImage() const
Definition: sch_bitmap.h:54
SCH_LAYER_ID
Eeschema drawing layers.
Definition: layer_ids.h:335
static LIB_SHAPE * loadPolyLine(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition: richio.h:135
const wxString & GetRevision() const
Definition: title_block.h:86
wxPoint GetPosition() const override
Definition: sch_junction.h:103
timestamp_t AsLegacyTimestamp() const
Definition: kiid.cpp:209
const wxString & GetName() const
Definition: lib_pin.h:106
PLOT_DASH_TYPE GetLineStyle() const
Definition: sch_line.cpp:278
SHAPE_LINE_CHAIN & Outline(int aIndex)
virtual bool IsVisible() const
Definition: eda_text.h:207
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:53
const wxSize & GetTextSize() const
Definition: eda_text.h:259
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:223
const wxString & GetCompany() const
Definition: title_block.h:96
int GetUnitCount() const override
For items with units, return the number of units.
int GetFieldCount() const
Return the number of fields in this symbol.
Definition: sch_symbol.h:425
static bool is_eol(char c)
static void ResolvePossibleSymlinks(wxFileName &aFilename)
Definition: wx_filename.cpp:85
const int fill_tab[3]
Definition: lib_item.cpp:32
#define LIB_VERSION_MAJOR
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:402
void SetDescription(const wxString &aDescription)
Definition: lib_symbol.h:140
void cacheLib(const wxString &aLibraryFileName, const PROPERTIES *aProperties)
static void IncrementModifyGeneration()
void Empty()
Definition: eda_text.h:275
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
A simple container for schematic symbol instance information.
Definition for symbol library class.
void loadPageSettings(LINE_READER &aReader, SCH_SCREEN *aScreen)
void Save(const wxString &aFileName, SCH_SHEET *aScreen, SCHEMATIC *aSchematic, const PROPERTIES *aProperties=nullptr) override
Write aSchematic to a storage file in a format that this SCH_PLUGIN implementation knows about,...
bool ShowPinNames() const
Definition: lib_symbol.h:580
#define _(s)
wxLogTrace helper definitions.
static int parseInt(LINE_READER &aReader, const char *aLine, const char **aOutput=nullptr)
Parse an ASCII integer string with possible leading whitespace into an integer and updates the pointe...
#define USE_OLD_DOC_FILE_FORMAT(major, minor)
void SetWidth(int aWidth)
Definition: eda_shape.h:97
#define T_COLOR
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:716
void saveBitmap(SCH_BITMAP *aBitmap)
wxPoint GetLibPosition() const
Definition: sch_field.h:199
void SetParent(LIB_SYMBOL *aParent=nullptr)
Definition: lib_symbol.cpp:371
LABEL_SPIN_STYLE GetLabelSpinStyle() const
Definition: sch_text.h:159
static const char * PropNoDocFile
The property used internally by the plugin to disable writing the library documentation (....
static LIB_SYMBOL * ParsePart(LINE_READER &aReader, int majorVersion=0, int minorVersion=0)
bool IsVisible() const
Definition: lib_pin.h:97
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:65
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslate=true)
Return a default symbol field name for field aFieldNdx for all components.
int GetNameTextSize() const
Definition: lib_pin.h:126
wxPoint GetCenter() const
Definition: lib_shape.h:90
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:314
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition: lib_id.cpp:108
void SetModified(bool aModified=true)
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: sch_screen.h:158
wxPoint GetPosition() const override
Definition: sch_text.h:241
wxPoint GetPosition() const override
Definition: sch_sheet.h:379
void SetTitle(const wxString &aTitle)
Definition: title_block.h:58
#define LIBFILE_IDENT
int GetConvert() const
Definition: lib_item.h:269
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition: lib_symbol.h:484
static PLOT_DASH_TYPE GetLineStyleByName(const wxString &aStyleName)
Definition: sch_line.cpp:115
Object to handle a bitmap image that can be inserted in a schematic.
Definition: sch_bitmap.h:40
void SetHeightMils(int aHeightInMils)
Definition: page_info.cpp:257
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:227
UTF8 Format() const
Definition: lib_id.cpp:116
void SaveLibrary(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
wxString UnescapeString(const wxString &aSource)
const char * delims
bool UnitsLocked() const
Check whether symbol units are interchangeable.
Definition: lib_symbol.h:241
bool CheckHeader(const wxString &aFileName) override
Return true if the first line in aFileName begins with the expected header.
static void SaveSymbol(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter, LIB_SYMBOL_MAP *aMap=nullptr)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
PLOT_DASH_TYPE
Dashed line types.
Definition: plotter.h:104
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:75
const KIID m_Uuid
Definition: eda_item.h:474
void SetConvert(int aConvert)
Definition: lib_item.h:268
wxPoint GetPosition() const override
Definition: lib_pin.h:212
void Format(SCH_SHEET *aSheet)
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:88
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:259
void SetFileName(const wxString &aFileName)
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:183
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
void SetContentModified(bool aModified=true)
Definition: base_screen.h:59
The first 4 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
SCH_BITMAP * loadBitmap(LINE_READER &aReader)
static void saveText(const LIB_TEXT *aText, OUTPUTFORMATTER &aFormatter)
uint32_t timestamp_t
timestamp_t is our type to represent unique IDs for all kinds of elements; historically simply the ti...
Definition: kiid.h:32
static void loadField(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
static void savePin(const LIB_PIN *aPin, OUTPUTFORMATTER &aFormatter)
bool IsFile(const wxString &aFullPathAndFileName) const
static LIB_SHAPE * loadCircle(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
const wxString & GetDate() const
Definition: title_block.h:76
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:366
const wxString & GetNumber() const
Definition: lib_pin.h:116
ELECTRICAL_PINTYPE GetType() const
Definition: lib_pin.h:84
static void saveField(const LIB_FIELD *aField, OUTPUTFORMATTER &aFormatter)
void SetUnit(int aUnit)
Definition: lib_item.h:265
std::unordered_set< std::shared_ptr< BUS_ALIAS > > GetBusAliases() const
Return a list of bus aliases defined in this screen.
Definition: sch_screen.h:481
int GetLineSize() const
Definition: sch_line.h:164
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
LIB_FIELD * GetFieldById(int aId) const
Return pointer to the requested field.
SCH_SHEET & Root() const
Definition: schematic.h:92
int GetNumberTextSize() const
Definition: lib_pin.h:129
bool Exists(const std::string &aProperty) const
Definition: properties.h:41
int GetTextWidth() const
Definition: eda_text.h:262
virtual char * ReadLine()=0
Read a line of text into the buffer and increments the line number counter.
#define SCH_PARSE_ERROR(text, reader, pos)
Schematic symbol object.
Definition: sch_symbol.h:78
wxString AsLegacyTimestampString() const
Definition: kiid.cpp:242
void saveBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition (and transfers ownership of the pointer).
const char * name
Definition: DXF_plotter.cpp:56
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
static LIB_SHAPE * loadRect(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
OUTPUTFORMATTER * m_out
The formatter for saving SCH_SCREEN objects.
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:35
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:146
const std::map< PINSHEETLABEL_SHAPE, const char * > sheetLabelNames
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.cpp:53
int GetLength() const
Definition: lib_pin.h:81
bool IsPortrait() const
Definition: page_info.h:117
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:222
usual pin input: must be connected
wxSize GetSize() const
Definition: sch_sheet.h:104
LINE_READER * m_lineReader
for progress reporting
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
void CreateSymbolLib(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Create a new empty symbol library at aLibraryPath.
wxString GetName(bool aUseDefaultName=true) const
Return the field name.
Definition: sch_field.cpp:678
unsigned m_lineCount
for progress reporting
static FILL_T parseFillMode(LINE_READER &aReader, const char *aLine, const char **aOutput)
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:110
void SetWidthMils(int aWidthInMils)
Definition: page_info.cpp:243
static char parseChar(LINE_READER &aReader, const char *aCurrentToken, const char **aNextToken=nullptr)
Parse a single ASCII character and updates the pointer at aOutput if it is not NULL.
void SetName(const wxString &aName)
Definition: sch_field.h:111
wxString GetPrefix() const
Definition: sch_symbol.h:228
void SaveSymbol(const wxString &aLibraryPath, const LIB_SYMBOL *aSymbol, const PROPERTIES *aProperties=nullptr) override
Write aSymbol to an existing library located at aLibraryPath.
wxPoint GetPosition() const override
Definition: sch_symbol.h:645
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
void SetBezierC2(const wxPoint &aPt)
Definition: eda_shape.h:156
wxArrayString GetFPFilters() const
Definition: lib_symbol.h:183
Used for text file output.
Definition: richio.h:456
int GetModifyHash() const override
Return the modification hash from the library cache.
SCH_NO_CONNECT * loadNoConnect(LINE_READER &aReader)
const wxPoint & GetTextPos() const
Definition: eda_text.h:268
void SetPosition(const wxPoint &aPosition) override
Definition: lib_item.h:230
static void parseQuotedString(wxString &aString, LINE_READER &aReader, const char *aCurrentToken, const char **aNextToken=nullptr, bool aCanBeEmpty=false)
Parse an quoted ASCII utf8 and updates the pointer at aOutput if it is not NULL.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
static LIB_SHAPE * loadArc(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
const wxString & GetTitle() const
Definition: title_block.h:63
input or output (like port for a microprocessor)
void SetFillMode(FILL_T aFill)
Definition: eda_shape.h:87
bool IsRoot() const override
For symbols derived from other symbols, IsRoot() indicates no derivation.
Definition: lib_symbol.h:171
void loadHeader(LINE_READER &aReader, SCH_SCREEN *aScreen)
std::string EscapedUTF8(const wxString &aString)
Return an 8 bit UTF8 string given aString in Unicode form.
static std::mutex s_modHashMutex
static LIB_SHAPE * loadBezier(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
#define TEXT_ANGLE_VERT
Definition: eda_text.h:72
virtual void SetTextAngle(double aAngle)
Definition: eda_text.h:188
SHAPE_T GetShape() const
Definition: eda_shape.h:101
SCH_LINE * loadWire(LINE_READER &aReader)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:110
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:426
wxString m_name
Name (not the field text value itself, that is #EDA_TEXT::m_Text)
Definition: lib_field.h:209
int GetUnit() const
Definition: sch_symbol.h:196
int GetRadius() const
Definition: eda_shape.cpp:472
not connected (must be left open)
SCH_TEXT * loadText(LINE_READER &aReader)
wxPoint GetPosition() const override
Definition: lib_shape.h:87
#define T_COLORA
LIB_SYMBOL * removeSymbol(LIB_SYMBOL *aAlias)
void SetBold(bool aBold)
Definition: eda_text.h:203
bool IsSymbolLibWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
void SetPageCount(int aPageCount)
Definition: base_screen.cpp:63
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:186
void Save(bool aSaveDocFile=true)
Save the entire library to file m_libFileName;.
int GetConvert() const
Definition: sch_symbol.h:224
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
SCH_SYMBOL * loadSymbol(LINE_READER &aReader)
output of a regulator: intended to be connected to power input pins
void saveBusEntry(SCH_BUS_ENTRY_BASE *aBusEntry)
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:154
static void loadFootprintFilters(std::unique_ptr< LIB_SYMBOL > &aSymbol, LINE_READER &aReader)
static const char * PropPowerSymsOnly
void SetKeyWords(const wxString &aKeyWords)
Definition: lib_symbol.h:153
FILL_T GetFillType() const
Definition: eda_shape.h:88
bool writeDocFile(const PROPERTIES *aProperties)
wxFileName GetRealFile() const
int GetWidthMils() const
Definition: page_info.h:130
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
PINSHEETLABEL_SHAPE GetShape() const
Definition: sch_text.h:161
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
wxPoint GetEnd() const
std::shared_ptr< BUS_ALIAS > loadBusAlias(LINE_READER &aReader, SCH_SCREEN *aScreen)
const LIB_ID & GetLibId() const
Definition: sch_symbol.h:148
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...
#define LIB_VERSION(major, minor)
wxPoint GetPosition() const override
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
std::stack< wxString > m_currentPath
Stack to maintain nested sheet paths.
wxPoint GetEndPoint() const
Definition: sch_line.h:93