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-2017 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Wayne Stambaugh <stambaughw@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <ctype.h>
24 #include <algorithm>
25 
26 #include <wx/mstream.h>
27 #include <wx/filename.h>
28 #include <wx/tokenzr.h>
29 
30 #include <drawtxt.h>
31 #include <kiway.h>
32 #include <kicad_string.h>
33 #include <richio.h>
34 #include <core/typeinfo.h>
35 #include <properties.h>
36 
37 #include <general.h>
38 #include <lib_field.h>
39 #include <sch_bus_entry.h>
40 #include <sch_marker.h>
41 #include <sch_junction.h>
42 #include <sch_line.h>
43 #include <sch_no_connect.h>
44 #include <sch_component.h>
45 #include <sch_text.h>
46 #include <sch_sheet.h>
47 #include <sch_bitmap.h>
48 #include <sch_legacy_plugin.h>
49 #include <template_fieldnames.h>
50 #include <class_sch_screen.h>
51 #include <class_libentry.h>
52 #include <class_library.h>
53 #include <lib_arc.h>
54 #include <lib_bezier.h>
55 #include <lib_circle.h>
56 #include <lib_pin.h>
57 #include <lib_polyline.h>
58 #include <lib_rectangle.h>
59 #include <lib_text.h>
60 #include <eeschema_id.h> // for MAX_UNIT_COUNT_PER_PACKAGE definition
61 
62 
63 // Must be the first line of part library document (.dcm) files.
64 #define DOCFILE_IDENT "EESchema-DOCLIB Version 2.0"
65 
66 #define SCH_PARSE_ERROR( text, reader, pos ) \
67  THROW_PARSE_ERROR( text, reader.GetSource(), reader.Line(), \
68  reader.LineNumber(), pos - reader.Line() )
69 
70 
71 // Token delimiters.
72 const char* delims = " \t\r\n";
73 
74 
80 const wxChar traceSchLegacyPlugin[] = wxT( "KICAD_TRACE_SCH_LEGACY_PLUGIN" );
81 
82 
83 static bool is_eol( char c )
84 {
85  // The default file eol character used internally by KiCad.
86  // |
87  // | Possible eol if someone edited the file by hand on certain platforms.
88  // | |
89  // | | May have gone past eol with strtok().
90  // | | |
91  if( c == '\n' || c == '\r' || c == 0 )
92  return true;
93 
94  return false;
95 }
96 
97 
109 static bool strCompare( const char* aString, const char* aLine, const char** aOutput = NULL )
110 {
111  size_t len = strlen( aString );
112  bool retv = ( strncasecmp( aLine, aString, len ) == 0 ) &&
113  ( isspace( aLine[ len ] ) || aLine[ len ] == 0 );
114 
115  if( retv && aOutput )
116  {
117  const char* tmp = aLine;
118 
119  // Move past the end of the token.
120  tmp += len;
121 
122  // Move to the beginning of the next token.
123  while( *tmp && isspace( *tmp ) )
124  tmp++;
125 
126  *aOutput = tmp;
127  }
128 
129  return retv;
130 }
131 
132 
148 static int parseInt( FILE_LINE_READER& aReader, const char* aLine, const char** aOutput = NULL )
149 {
150  if( !*aLine )
151  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
152 
153  // Clear errno before calling strtol() in case some other crt call set it.
154  errno = 0;
155 
156  long retv = strtol( aLine, (char**) aOutput, 10 );
157 
158  // Make sure no error occurred when calling strtol().
159  if( errno == ERANGE )
160  SCH_PARSE_ERROR( "invalid integer value", aReader, aLine );
161 
162  // strtol does not strip off whitespace before the next token.
163  if( aOutput )
164  {
165  const char* next = *aOutput;
166 
167  while( *next && isspace( *next ) )
168  next++;
169 
170  *aOutput = next;
171  }
172 
173  return (int) retv;
174 }
175 
176 
192 static unsigned long parseHex( FILE_LINE_READER& aReader, const char* aLine,
193  const char** aOutput = NULL )
194 {
195  if( !*aLine )
196  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
197 
198  unsigned long retv;
199 
200  // Clear errno before calling strtoul() in case some other crt call set it.
201  errno = 0;
202  retv = strtoul( aLine, (char**) aOutput, 16 );
203 
204  // Make sure no error occurred when calling strtoul().
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 = aLine + strlen( token );
212 
213  const char* next = *aOutput;
214 
215  while( *next && isspace( *next ) )
216  next++;
217 
218  *aOutput = next;
219  }
220 
221  return retv;
222 }
223 
224 
240 static double parseDouble( FILE_LINE_READER& aReader, const char* aLine,
241  const char** aOutput = NULL )
242 {
243  if( !*aLine )
244  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
245 
246  // Clear errno before calling strtod() in case some other crt call set it.
247  errno = 0;
248 
249  double retv = strtod( aLine, (char**) aOutput );
250 
251  // Make sure no error occurred when calling strtod().
252  if( errno == ERANGE )
253  SCH_PARSE_ERROR( "invalid floating point number", aReader, aLine );
254 
255  // strtod does not strip off whitespace before the next token.
256  if( aOutput )
257  {
258  const char* next = *aOutput;
259 
260  while( *next && isspace( *next ) )
261  next++;
262 
263  *aOutput = next;
264  }
265 
266  return retv;
267 }
268 
269 
283 static char parseChar( FILE_LINE_READER& aReader, const char* aCurrentToken,
284  const char** aNextToken = NULL )
285 {
286  while( *aCurrentToken && isspace( *aCurrentToken ) )
287  aCurrentToken++;
288 
289  if( !*aCurrentToken )
290  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
291 
292  if( !isspace( *( aCurrentToken + 1 ) ) )
293  SCH_PARSE_ERROR( "expected single character token", aReader, aCurrentToken );
294 
295  if( aNextToken )
296  {
297  const char* next = aCurrentToken + 2;
298 
299  while( *next && isspace( *next ) )
300  next++;
301 
302  *aNextToken = next;
303  }
304 
305  return *aCurrentToken;
306 }
307 
308 
325 static void parseUnquotedString( wxString& aString, FILE_LINE_READER& aReader,
326  const char* aCurrentToken, const char** aNextToken = NULL,
327  bool aCanBeEmpty = false )
328 {
329  if( !*aCurrentToken )
330  {
331  if( aCanBeEmpty )
332  return;
333  else
334  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
335  }
336 
337  const char* tmp = aCurrentToken;
338 
339  while( *tmp && isspace( *tmp ) )
340  tmp++;
341 
342  if( !*tmp )
343  {
344  if( aCanBeEmpty )
345  return;
346  else
347  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
348  }
349 
350  std::string utf8;
351 
352  while( *tmp && !isspace( *tmp ) )
353  utf8 += *tmp++;
354 
355  aString = FROM_UTF8( utf8.c_str() );
356 
357  if( aString.IsEmpty() && !aCanBeEmpty )
358  SCH_PARSE_ERROR( _( "expected unquoted string" ), aReader, aCurrentToken );
359 
360  if( aNextToken )
361  {
362  const char* next = tmp;
363 
364  while( *next && isspace( *next ) )
365  next++;
366 
367  *aNextToken = next;
368  }
369 }
370 
371 
389 static void parseQuotedString( wxString& aString, FILE_LINE_READER& aReader,
390  const char* aCurrentToken, const char** aNextToken = NULL,
391  bool aCanBeEmpty = false )
392 {
393  if( !*aCurrentToken )
394  {
395  if( aCanBeEmpty )
396  return;
397  else
398  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
399  }
400 
401  const char* tmp = aCurrentToken;
402 
403  while( *tmp && isspace( *tmp ) )
404  tmp++;
405 
406  if( !*tmp )
407  {
408  if( aCanBeEmpty )
409  return;
410  else
411  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
412  }
413 
414  // Verify opening quote.
415  if( *tmp != '"' )
416  SCH_PARSE_ERROR( "expecting opening quote", aReader, aCurrentToken );
417 
418  tmp++;
419 
420  std::string utf8; // utf8 without escapes and quotes.
421 
422  // Fetch everything up to closing quote.
423  while( *tmp )
424  {
425  if( *tmp == '\\' )
426  {
427  tmp++;
428 
429  if( !*tmp )
430  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
431 
432  // Do not copy the escape byte if it is followed by \ or "
433  if( *tmp != '"' && *tmp != '\\' )
434  utf8 += '\\';
435 
436  utf8 += *tmp;
437  }
438  else if( *tmp == '"' ) // Closing double quote.
439  {
440  break;
441  }
442  else
443  {
444  utf8 += *tmp;
445  }
446 
447  tmp++;
448  }
449 
450  aString = FROM_UTF8( utf8.c_str() );
451 
452  if( aString.IsEmpty() && !aCanBeEmpty )
453  SCH_PARSE_ERROR( "expected quoted string", aReader, aCurrentToken );
454 
455  if( *tmp && *tmp != '"' )
456  SCH_PARSE_ERROR( "no closing quote for string found", aReader, tmp );
457 
458  // Move past the closing quote.
459  tmp++;
460 
461  if( aNextToken )
462  {
463  const char* next = tmp;
464 
465  while( *next && *next == ' ' )
466  next++;
467 
468  *aNextToken = next;
469  }
470 }
471 
472 
480 {
481  wxFileName m_libFileName; // Absolute path and file name is required here.
482  wxDateTime m_fileModTime;
483  LIB_ALIAS_MAP m_aliases; // Map of names of LIB_ALIAS pointers.
486  int m_modHash; // Keep track of the modification status of the library.
489  int m_libType; // Is this cache a component or symbol library.
490 
491  LIB_PART* loadPart( FILE_LINE_READER& aReader );
492  void loadHeader( FILE_LINE_READER& aReader );
493  void loadAliases( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
494  void loadField( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
495  void loadDrawEntries( std::unique_ptr< LIB_PART >& aPart,
496  FILE_LINE_READER& aReader );
497  void loadFootprintFilters( std::unique_ptr< LIB_PART >& aPart,
498  FILE_LINE_READER& aReader );
499  void loadDocs();
500  LIB_ARC* loadArc( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
501  LIB_CIRCLE* loadCircle( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
502  LIB_TEXT* loadText( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
503  LIB_RECTANGLE* loadRectangle( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
504  LIB_PIN* loadPin( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
505  LIB_POLYLINE* loadPolyLine( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
506  LIB_BEZIER* loadBezier( std::unique_ptr< LIB_PART >& aPart, FILE_LINE_READER& aReader );
507 
508  FILL_T parseFillMode( FILE_LINE_READER& aReader, const char* aLine,
509  const char** aOutput );
510  bool checkForDuplicates( wxString& aAliasName );
511  LIB_ALIAS* removeAlias( LIB_ALIAS* aAlias );
512 
513  void saveDocFile();
514 
516 
517 public:
518  SCH_LEGACY_PLUGIN_CACHE( const wxString& aLibraryPath );
520 
521  int GetModifyHash() const { return m_modHash; }
522 
523  // Most all functions in this class throw IO_ERROR exceptions. There are no
524  // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects.
525  // Catch these exceptions higher up please.
526 
528  void Save( bool aSaveDocFile = true );
529 
530  void Load();
531 
532  void AddSymbol( const LIB_PART* aPart );
533 
534  void DeleteAlias( const wxString& aAliasName );
535 
536  void DeleteSymbol( const wxString& aAliasName );
537 
538  wxDateTime GetLibModificationTime();
539 
540  bool IsFile( const wxString& aFullPathAndFileName ) const;
541 
542  bool IsFileChanged() const;
543 
544  void SetModified( bool aModified = true ) { m_isModified = aModified; }
545 
546  wxString GetLogicalName() const { return m_libFileName.GetName(); }
547 
548  void SetFileName( const wxString& aFileName ) { m_libFileName = aFileName; }
549 
550  wxString GetFileName() const { return m_libFileName.GetFullPath(); }
551 };
552 
553 
555 {
556  init( NULL );
557 }
558 
559 
561 {
562  delete m_cache;
563 }
564 
565 
566 void SCH_LEGACY_PLUGIN::init( KIWAY* aKiway, const PROPERTIES* aProperties )
567 {
568  m_version = 0;
569  m_rootSheet = NULL;
570  m_props = aProperties;
571  m_kiway = aKiway;
572  m_cache = NULL;
573  m_out = NULL;
574 }
575 
576 
577 SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, KIWAY* aKiway,
578  SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties )
579 {
580  wxASSERT( !aFileName || aKiway != NULL );
581 
582  LOCALE_IO toggle; // toggles on, then off, the C locale.
583  SCH_SHEET* sheet;
584 
585  wxFileName fn = aFileName;
586 
587  // Unfortunately child sheet file names the legacy schematic file format are not fully
588  // qualified and are always appended to the project path. The aFileName attribute must
589  // always be an absolute path so the project path can be used for load child sheet files.
590  wxASSERT( fn.IsAbsolute() );
591 
592  m_path = fn.GetPath();
593 
594  init( aKiway, aProperties );
595 
596  if( aAppendToMe == NULL )
597  {
598  // Clean up any allocated memory if an exception occurs loading the schematic.
599  std::unique_ptr< SCH_SHEET > newSheet( new SCH_SHEET );
600  newSheet->SetFileName( aFileName );
601  m_rootSheet = newSheet.get();
602  loadHierarchy( newSheet.get() );
603 
604  // If we got here, the schematic loaded successfully.
605  sheet = newSheet.release();
606  }
607  else
608  {
609  m_rootSheet = aAppendToMe->GetRootSheet();
610  wxASSERT( m_rootSheet != NULL );
611  sheet = aAppendToMe;
612  loadHierarchy( sheet );
613  }
614 
615  return sheet;
616 }
617 
618 
619 // Everything below this comment is recursive. Modify with care.
620 
622 {
623  SCH_SCREEN* screen = NULL;
624 
625  if( !aSheet->GetScreen() )
626  {
627  // SCH_SCREEN objects store the full path and file name where the SCH_SHEET object only
628  // stores the file name and extension. Add the project path to the file name and
629  // extension to compare when calling SCH_SHEET::SearchHierarchy().
630  wxFileName fileName = aSheet->GetFileName();
631 
632  if( !fileName.IsAbsolute() )
633  fileName.MakeAbsolute( m_path );
634 
635  m_rootSheet->SearchHierarchy( fileName.GetFullPath(), &screen );
636 
637  if( screen )
638  {
639  aSheet->SetScreen( screen );
640 
641  // Do not need to load the sub-sheets - this has already been done.
642  }
643  else
644  {
645  aSheet->SetScreen( new SCH_SCREEN( m_kiway ) );
646  aSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
647  loadFile( fileName.GetFullPath(), aSheet->GetScreen() );
648 
649  EDA_ITEM* item = aSheet->GetScreen()->GetDrawItems();
650 
651  while( item )
652  {
653  if( item->Type() == SCH_SHEET_T )
654  {
655  SCH_SHEET* sheet = (SCH_SHEET*) item;
656 
657  // Set the parent to aSheet. This effectively creates a method to find
658  // the root sheet from any sheet so a pointer to the root sheet does not
659  // need to be stored globally. Note: this is not the same as a hierarchy.
660  // Complex hierarchies can have multiple copies of a sheet. This only
661  // provides a simple tree to find the root sheet.
662  sheet->SetParent( aSheet );
663 
664  // Recursion starts here.
665  loadHierarchy( sheet );
666  }
667 
668  item = item->Next();
669  }
670  }
671  }
672 }
673 
674 
675 void SCH_LEGACY_PLUGIN::loadFile( const wxString& aFileName, SCH_SCREEN* aScreen )
676 {
677  FILE_LINE_READER reader( aFileName );
678 
679  loadHeader( reader, aScreen );
680 
681  while( reader.ReadLine() )
682  {
683  char* line = reader.Line();
684 
685  while( *line && *line == ' ' )
686  line++;
687 
688  // Either an object will be loaded properly or the file load will fail and raise
689  // an exception.
690  if( strCompare( "$Descr", line ) )
691  loadPageSettings( reader, aScreen );
692  else if( strCompare( "$Comp", line ) )
693  aScreen->Append( loadComponent( reader ) );
694  else if( strCompare( "$Sheet", line ) )
695  aScreen->Append( loadSheet( reader ) );
696  else if( strCompare( "$Bitmap", line ) )
697  aScreen->Append( loadBitmap( reader ) );
698  else if( strCompare( "Connection", line ) )
699  aScreen->Append( loadJunction( reader ) );
700  else if( strCompare( "NoConn", line ) )
701  aScreen->Append( loadNoConnect( reader ) );
702  else if( strCompare( "Wire", line ) )
703  aScreen->Append( loadWire( reader ) );
704  else if( strCompare( "Entry", line ) )
705  aScreen->Append( loadBusEntry( reader ) );
706  else if( strCompare( "Text", line ) )
707  aScreen->Append( loadText( reader ) );
708  else if( strCompare( "$EndSCHEMATC", line ) )
709  return;
710  }
711 
712  // Unfortunately schematic files prior to version 2 are not terminated with $EndSCHEMATC
713  // so checking for it's existance will fail so just exit here and take our chances. :(
714  if( m_version > 1 )
715  THROW_IO_ERROR( "'$EndSCHEMATC' not found" );
716 }
717 
718 
720 {
721  const char* line = aReader.ReadLine();
722 
723  if( !strCompare( "Eeschema Schematic File Version", line, &line ) )
724  {
725  m_error.Printf( _( "'%s' does not appear to be an Eeschema file" ),
726  GetChars( aScreen->GetFileName() ) );
728  }
729 
730  // get the file version here.
731  m_version = parseInt( aReader, line, &line );
732 
733  // The next lines are the lib list section, and are mainly comments, like:
734  // LIBS:power
735  // the lib list is not used, but is in schematic file just in case.
736  // It is usually not empty, but we accept empty list.
737  // If empty, there is a legacy section, not used
738  // EELAYER i j
739  // and the last line is
740  // EELAYER END
741  // Skip all lines until the end of header "EELAYER END" is found
742  while( aReader.ReadLine() )
743  {
744  line = aReader.Line();
745 
746  while( *line == ' ' )
747  line++;
748 
749  if( strCompare( "EELAYER END", line ) )
750  return;
751  }
752 
753  THROW_IO_ERROR( _( "Missing 'EELAYER END'" ) );
754 }
755 
756 
758 {
759  wxASSERT( aScreen != NULL );
760 
761  wxString buf;
762  const char* line = aReader.Line();
763 
764  PAGE_INFO pageInfo;
765  TITLE_BLOCK tb;
766 
767  wxCHECK_RET( strCompare( "$Descr", line, &line ), "Invalid sheet description" );
768 
769  parseUnquotedString( buf, aReader, line, &line );
770 
771  if( !pageInfo.SetType( buf ) )
772  SCH_PARSE_ERROR( "invalid page size", aReader, line );
773 
774  int pagew = parseInt( aReader, line, &line );
775  int pageh = parseInt( aReader, line, &line );
776 
777  if( buf == PAGE_INFO::Custom )
778  {
779  pageInfo.SetWidthMils( pagew );
780  pageInfo.SetHeightMils( pageh );
781  }
782  else
783  {
784  wxString orientation;
785 
786  // Non custom size, set portrait if its present. Can be empty string which defaults
787  // to landscape.
788  parseUnquotedString( orientation, aReader, line, &line, true );
789 
790  if( orientation == "portrait" )
791  pageInfo.SetPortrait( true );
792  }
793 
794  aScreen->SetPageSettings( pageInfo );
795 
796  while( line != NULL )
797  {
798  buf.clear();
799 
800  if( !aReader.ReadLine() )
801  SCH_PARSE_ERROR( _( "unexpected end of file" ), aReader, line );
802 
803  line = aReader.Line();
804 
805  if( strCompare( "Sheet", line, &line ) )
806  {
807  aScreen->m_ScreenNumber = parseInt( aReader, line, &line );
808  aScreen->m_NumberOfScreens = parseInt( aReader, line, &line );
809  }
810  else if( strCompare( "Title", line, &line ) )
811  {
812  parseQuotedString( buf, aReader, line, &line, true );
813  tb.SetTitle( buf );
814  }
815  else if( strCompare( "Date", line, &line ) )
816  {
817  parseQuotedString( buf, aReader, line, &line, true );
818  tb.SetDate( buf );
819  }
820  else if( strCompare( "Rev", line, &line ) )
821  {
822  parseQuotedString( buf, aReader, line, &line, true );
823  tb.SetRevision( buf );
824  }
825  else if( strCompare( "Comp", line, &line ) )
826  {
827  parseQuotedString( buf, aReader, line, &line, true );
828  tb.SetCompany( buf );
829  }
830  else if( strCompare( "Comment1", line, &line ) )
831  {
832  parseQuotedString( buf, aReader, line, &line, true );
833  tb.SetComment1( buf );
834  }
835  else if( strCompare( "Comment2", line, &line ) )
836  {
837  parseQuotedString( buf, aReader, line, &line, true );
838  tb.SetComment2( buf );
839  }
840  else if( strCompare( "Comment3", line, &line ) )
841  {
842  parseQuotedString( buf, aReader, line, &line, true );
843  tb.SetComment3( buf );
844  }
845  else if( strCompare( "Comment4", line, &line ) )
846  {
847  parseQuotedString( buf, aReader, line, &line, true );
848  tb.SetComment4( buf );
849  }
850  else if( strCompare( "$EndDescr", line ) )
851  {
852  aScreen->SetTitleBlock( tb );
853  return;
854  }
855  }
856 
857  SCH_PARSE_ERROR( "missing 'EndDescr'", aReader, line );
858 }
859 
860 
862 {
863  std::unique_ptr< SCH_SHEET > sheet( new SCH_SHEET() );
864 
865  sheet->SetTimeStamp( GetNewTimeStamp() );
866 
867  const char* line = aReader.ReadLine();
868 
869  while( line != NULL )
870  {
871  if( strCompare( "S", line, &line ) ) // Sheet dimensions.
872  {
873  wxPoint position;
874 
875  position.x = parseInt( aReader, line, &line );
876  position.y = parseInt( aReader, line, &line );
877  sheet->SetPosition( position );
878 
879  wxSize size;
880 
881  size.SetWidth( parseInt( aReader, line, &line ) );
882  size.SetHeight( parseInt( aReader, line, &line ) );
883  sheet->SetSize( size );
884  }
885  else if( strCompare( "U", line, &line ) ) // Sheet time stamp.
886  {
887  sheet->SetTimeStamp( parseHex( aReader, line ) );
888  }
889  else if( *line == 'F' ) // Sheet field.
890  {
891  line++;
892 
893  wxString text;
894  int size;
895  int fieldId = parseInt( aReader, line, &line );
896 
897  if( fieldId == 0 || fieldId == 1 ) // Sheet name and file name.
898  {
899  parseQuotedString( text, aReader, line, &line );
900  size = parseInt( aReader, line, &line );
901 
902  if( fieldId == 0 )
903  {
904  sheet->SetName( text );
905  sheet->SetSheetNameSize( size );
906  }
907  else
908  {
909  sheet->SetFileName( text );
910  sheet->SetFileNameSize( size );
911  }
912  }
913  else // Sheet pin.
914  {
915  std::unique_ptr< SCH_SHEET_PIN > sheetPin( new SCH_SHEET_PIN( sheet.get() ) );
916 
917  sheetPin->SetNumber( fieldId );
918 
919  // Can be empty fields.
920  parseQuotedString( text, aReader, line, &line, true );
921 
922  sheetPin->SetText( text );
923 
924  if( line == NULL )
925  THROW_IO_ERROR( _( "unexpected end of line" ) );
926 
927  switch( parseChar( aReader, line, &line ) )
928  {
929  case 'I':
930  sheetPin->SetShape( NET_INPUT );
931  break;
932 
933  case 'O':
934  sheetPin->SetShape( NET_OUTPUT );
935  break;
936 
937  case 'B':
938  sheetPin->SetShape( NET_BIDI );
939  break;
940 
941  case 'T':
942  sheetPin->SetShape( NET_TRISTATE );
943  break;
944 
945  case 'U':
946  sheetPin->SetShape( NET_UNSPECIFIED );
947  break;
948  default:
949  SCH_PARSE_ERROR( "invalid sheet pin type", aReader, line );
950  }
951 
952  switch( parseChar( aReader, line, &line ) )
953  {
954  case 'R': /* pin on right side */
955  sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_RIGHT_SIDE );
956  break;
957 
958  case 'T': /* pin on top side */
959  sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_TOP_SIDE );
960  break;
961 
962  case 'B': /* pin on bottom side */
963  sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_BOTTOM_SIDE );
964  break;
965 
966  case 'L': /* pin on left side */
967  sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_LEFT_SIDE );
968  break;
969  default:
970  SCH_PARSE_ERROR( "invalid sheet pin side", aReader, line );
971  }
972 
973  wxPoint position;
974 
975  position.x = parseInt( aReader, line, &line );
976  position.y = parseInt( aReader, line, &line );
977  sheetPin->SetPosition( position );
978 
979  size = parseInt( aReader, line, &line );
980 
981  sheetPin->SetTextSize( wxSize( size, size ) );
982 
983  sheet->AddPin( sheetPin.release() );
984  }
985  }
986  else if( strCompare( "$EndSheet", line ) )
987  return sheet.release();
988 
989  line = aReader.ReadLine();
990  }
991 
992  SCH_PARSE_ERROR( "missing '$EndSheet`", aReader, line );
993 
994  return NULL; // Prevents compiler warning. Should never get here.
995 }
996 
997 
999 {
1000  std::unique_ptr< SCH_BITMAP > bitmap( new SCH_BITMAP );
1001 
1002  const char* line = aReader.Line();
1003 
1004  wxCHECK( strCompare( "$Bitmap", line, &line ), NULL );
1005 
1006  line = aReader.ReadLine();
1007 
1008  while( line != NULL )
1009  {
1010  if( strCompare( "Pos", line, &line ) )
1011  {
1012  wxPoint position;
1013 
1014  position.x = parseInt( aReader, line, &line );
1015  position.y = parseInt( aReader, line, &line );
1016  bitmap->SetPosition( position );
1017  }
1018  else if( strCompare( "Scale", line, &line ) )
1019  {
1021  bitmap->GetImage()->SetScale( parseDouble( aReader, line, &line ) );
1022  }
1023  else if( strCompare( "Data", line, &line ) )
1024  {
1025  wxMemoryOutputStream stream;
1026 
1027  while( line )
1028  {
1029  if( !aReader.ReadLine() )
1030  SCH_PARSE_ERROR( _( "Unexpected end of file" ), aReader, line );
1031 
1032  line = aReader.Line();
1033 
1034  if( strCompare( "EndData", line ) )
1035  {
1036  // all the PNG date is read.
1037  // We expect here m_image and m_bitmap are void
1038  wxImage* image = new wxImage();
1039  wxMemoryInputStream istream( stream );
1040  image->LoadFile( istream, wxBITMAP_TYPE_PNG );
1041  bitmap->GetImage()->SetImage( image );
1042  bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
1043  break;
1044  }
1045 
1046  // Read PNG data, stored in hexadecimal,
1047  // each byte = 2 hexadecimal digits and a space between 2 bytes
1048  // and put it in memory stream buffer
1049  int len = strlen( line );
1050 
1051  for( ; len > 0 && !isspace( *line ); len -= 3, line += 3 )
1052  {
1053  int value = 0;
1054 
1055  if( sscanf( line, "%X", &value ) == 1 )
1056  stream.PutC( (char) value );
1057  else
1058  THROW_IO_ERROR( "invalid PNG data" );
1059  }
1060  }
1061 
1062  if( line == NULL )
1063  THROW_IO_ERROR( _( "unexpected end of file" ) );
1064  }
1065  else if( strCompare( "$EndBitmap", line ) )
1066  return bitmap.release();
1067 
1068  line = aReader.ReadLine();
1069  }
1070 
1071  THROW_IO_ERROR( _( "unexpected end of file" ) );
1072 }
1073 
1074 
1076 {
1077  std::unique_ptr< SCH_JUNCTION > junction( new SCH_JUNCTION );
1078 
1079  const char* line = aReader.Line();
1080 
1081  wxCHECK( strCompare( "Connection", line, &line ), NULL );
1082 
1083  wxString name;
1084 
1085  parseUnquotedString( name, aReader, line, &line );
1086 
1087  wxPoint position;
1088 
1089  position.x = parseInt( aReader, line, &line );
1090  position.y = parseInt( aReader, line, &line );
1091  junction->SetPosition( position );
1092 
1093  return junction.release();
1094 }
1095 
1096 
1098 {
1099  std::unique_ptr< SCH_NO_CONNECT > no_connect( new SCH_NO_CONNECT );
1100 
1101  const char* line = aReader.Line();
1102 
1103  wxCHECK( strCompare( "NoConn", line, &line ), NULL );
1104 
1105  wxString name;
1106 
1107  parseUnquotedString( name, aReader, line, &line );
1108 
1109  wxPoint position;
1110 
1111  position.x = parseInt( aReader, line, &line );
1112  position.y = parseInt( aReader, line, &line );
1113  no_connect->SetPosition( position );
1114 
1115  return no_connect.release();
1116 }
1117 
1118 
1120 {
1121  std::unique_ptr< SCH_LINE > wire( new SCH_LINE );
1122 
1123  const char* line = aReader.Line();
1124 
1125  wxCHECK( strCompare( "Wire", line, &line ), NULL );
1126 
1127  if( strCompare( "Wire", line, &line ) )
1128  wire->SetLayer( LAYER_WIRE );
1129  else if( strCompare( "Bus", line, &line ) )
1130  wire->SetLayer( LAYER_BUS );
1131  else if( strCompare( "Notes", line, &line ) )
1132  wire->SetLayer( LAYER_NOTES );
1133  else
1134  SCH_PARSE_ERROR( "invalid line type", aReader, line );
1135 
1136  if( !strCompare( "Line", line, &line ) )
1137  SCH_PARSE_ERROR( "invalid wire definition", aReader, line );
1138 
1139  line = aReader.ReadLine();
1140 
1141  wxPoint begin, end;
1142 
1143  begin.x = parseInt( aReader, line, &line );
1144  begin.y = parseInt( aReader, line, &line );
1145  end.x = parseInt( aReader, line, &line );
1146  end.y = parseInt( aReader, line, &line );
1147 
1148  wire->SetStartPoint( begin );
1149  wire->SetEndPoint( end );
1150 
1151  return wire.release();
1152 }
1153 
1154 
1156 {
1157  const char* line = aReader.Line();
1158 
1159  wxCHECK( strCompare( "Entry", line, &line ), NULL );
1160 
1161  std::unique_ptr< SCH_BUS_ENTRY_BASE > busEntry;
1162 
1163  if( strCompare( "Wire", line, &line ) )
1164  {
1165  busEntry.reset( new SCH_BUS_WIRE_ENTRY );
1166 
1167  if( !strCompare( "Line", line, &line ) )
1168  SCH_PARSE_ERROR( "invalid bus entry definition expected 'Line'", aReader, line );
1169  }
1170  else if( strCompare( "Bus", line, &line ) )
1171  {
1172  busEntry.reset( new SCH_BUS_BUS_ENTRY );
1173 
1174  if( !strCompare( "Bus", line, &line ) )
1175  SCH_PARSE_ERROR( "invalid bus entry definition expected 'Bus'", aReader, line );
1176  }
1177  else
1178  SCH_PARSE_ERROR( "invalid bus entry type", aReader, line );
1179 
1180  line = aReader.ReadLine();
1181 
1182  wxPoint pos;
1183  wxSize size;
1184 
1185  pos.x = parseInt( aReader, line, &line );
1186  pos.y = parseInt( aReader, line, &line );
1187  size.x = parseInt( aReader, line, &line );
1188  size.y = parseInt( aReader, line, &line );
1189 
1190  size.x -= pos.x;
1191  size.y -= pos.y;
1192 
1193  busEntry->SetPosition( pos );
1194  busEntry->SetSize( size );
1195 
1196  return busEntry.release();
1197 }
1198 
1199 
1201 {
1202  const char* line = aReader.Line();
1203 
1204  wxCHECK( strCompare( "Text", line, &line ), NULL );
1205 
1206  std::unique_ptr< SCH_TEXT> text;
1207 
1208  if( strCompare( "Notes", line, &line ) )
1209  text.reset( new SCH_TEXT );
1210  else if( strCompare( "Label", line, &line ) )
1211  text.reset( new SCH_LABEL );
1212  else if( strCompare( "HLabel", line, &line ) )
1213  text.reset( new SCH_HIERLABEL );
1214  else if( strCompare( "GLabel", line, &line ) )
1215  {
1216  // Prior to version 2, the SCH_GLOBALLABEL object did not exist.
1217  if( m_version == 1 )
1218  text.reset( new SCH_HIERLABEL );
1219  else
1220  text.reset( new SCH_GLOBALLABEL );
1221  }
1222  else
1223  SCH_PARSE_ERROR( "unknown Text type", aReader, line );
1224 
1225  // Parse the parameters common to all text objects.
1226  wxPoint position;
1227 
1228  position.x = parseInt( aReader, line, &line );
1229  position.y = parseInt( aReader, line, &line );
1230  text->SetPosition( position );
1231  text->SetLabelSpinStyle( parseInt( aReader, line, &line ) );
1232 
1233  int size = parseInt( aReader, line, &line );
1234 
1235  text->SetTextSize( wxSize( size, size ) );
1236 
1237  // Parse the global and hierarchical label type.
1238  if( text->Type() == SCH_HIERARCHICAL_LABEL_T || text->Type() == SCH_GLOBAL_LABEL_T )
1239  {
1240  if( strCompare( SheetLabelType[NET_INPUT], line, &line ) )
1241  text->SetShape( NET_INPUT );
1242  else if( strCompare( SheetLabelType[NET_OUTPUT], line, &line ) )
1243  text->SetShape( NET_OUTPUT );
1244  else if( strCompare( SheetLabelType[NET_BIDI], line, &line ) )
1245  text->SetShape( NET_BIDI );
1246  else if( strCompare( SheetLabelType[NET_TRISTATE], line, &line ) )
1247  text->SetShape( NET_TRISTATE );
1248  else if( strCompare( SheetLabelType[NET_UNSPECIFIED], line, &line ) )
1249  text->SetShape( NET_UNSPECIFIED );
1250  else
1251  SCH_PARSE_ERROR( "invalid label type", aReader, line );
1252  }
1253 
1254  int thickness = 0;
1255 
1256  // The following tokens do not exist in version 1 schematic files.
1257  if( m_version > 1 )
1258  {
1259  if( strCompare( "Italic", line, &line ) )
1260  text->SetItalic( true );
1261  else if( !strCompare( "~", line, &line ) )
1262  SCH_PARSE_ERROR( _( "expected 'Italics' or '~'" ), aReader, line );
1263 
1264  // The thickness token does not exist in older versions of the schematic file format
1265  // so calling parseInt will be made only if the EOL is not reached.
1266  if( *line >= ' ' )
1267  thickness = parseInt( aReader, line, &line );
1268  }
1269 
1270  text->SetBold( thickness != 0 );
1271  text->SetThickness( thickness != 0 ? GetPenSizeForBold( size ) : 0 );
1272 
1273  // Read the text string for the text.
1274  char* tmp = aReader.ReadLine();
1275 
1276  tmp = strtok( tmp, "\r\n" );
1277  wxString val = FROM_UTF8( tmp );
1278 
1279  for( ; ; )
1280  {
1281  int i = val.find( wxT( "\\n" ) );
1282 
1283  if( i == wxNOT_FOUND )
1284  break;
1285 
1286  val.erase( i, 2 );
1287  val.insert( i, wxT( "\n" ) );
1288  }
1289 
1290  text->SetText( val );
1291 
1292  return text.release();
1293 }
1294 
1295 
1297 {
1298  const char* line = aReader.Line();
1299 
1300  wxCHECK( strCompare( "$Comp", line, &line ), NULL );
1301 
1302  std::unique_ptr< SCH_COMPONENT > component( new SCH_COMPONENT() );
1303 
1304  line = aReader.ReadLine();
1305 
1306  while( line != NULL )
1307  {
1308  if( strCompare( "L", line, &line ) )
1309  {
1310  wxString libName;
1311 
1312  parseUnquotedString( libName, aReader, line, &line );
1313  libName.Replace( "~", " " );
1314 
1315  LIB_ID libId( wxEmptyString, libName );
1316 
1317  component->SetLibId( libId );
1318 
1319  wxString refDesignator;
1320 
1321  parseUnquotedString( refDesignator, aReader, line, &line );
1322  refDesignator.Replace( "~", " " );
1323 
1324  wxString prefix = refDesignator;
1325 
1326  while( prefix.Length() )
1327  {
1328  if( ( prefix.Last() < '0' || prefix.Last() > '9') && prefix.Last() != '?' )
1329  break;
1330 
1331  prefix.RemoveLast();
1332  }
1333 
1334  // Avoid a prefix containing trailing/leading spaces
1335  prefix.Trim( true );
1336  prefix.Trim( false );
1337 
1338  if( prefix.IsEmpty() )
1339  component->SetPrefix( wxString( "U" ) );
1340  else
1341  component->SetPrefix( prefix );
1342  }
1343  else if( strCompare( "U", line, &line ) )
1344  {
1345  // This fixes a potentially buggy files caused by unit being set to zero which
1346  // causes netlist issues. See https://bugs.launchpad.net/kicad/+bug/1677282.
1347  int unit = parseInt( aReader, line, &line );
1348 
1349  if( unit == 0 )
1350  {
1351  unit = 1;
1352 
1353  // Set the file as modified so the user can be warned.
1354  if( m_rootSheet && m_rootSheet->GetScreen() )
1356  }
1357 
1358  component->SetUnit( unit );
1359  component->SetConvert( parseInt( aReader, line, &line ) );
1360  component->SetTimeStamp( parseHex( aReader, line, &line ) );
1361  }
1362  else if( strCompare( "P", line, &line ) )
1363  {
1364  wxPoint pos;
1365 
1366  pos.x = parseInt( aReader, line, &line );
1367  pos.y = parseInt( aReader, line, &line );
1368  component->SetPosition( pos );
1369  }
1370  else if( strCompare( "AR", line, &line ) )
1371  {
1372  const char* strCompare = "Path=";
1373  int len = strlen( strCompare );
1374 
1375  if( strncasecmp( strCompare, line, len ) != 0 )
1376  SCH_PARSE_ERROR( "missing 'Path=' token", aReader, line );
1377 
1378  line += len;
1379  wxString path, reference, unit;
1380 
1381  parseQuotedString( path, aReader, line, &line );
1382 
1383  strCompare = "Ref=";
1384  len = strlen( strCompare );
1385 
1386  if( strncasecmp( strCompare, line, len ) != 0 )
1387  SCH_PARSE_ERROR( "missing 'Ref=' token", aReader, line );
1388 
1389  line+= len;
1390  parseQuotedString( reference, aReader, line, &line );
1391 
1392  strCompare = "Part=";
1393  len = strlen( strCompare );
1394 
1395  if( strncasecmp( strCompare, line, len ) != 0 )
1396  SCH_PARSE_ERROR( "missing 'Part=' token", aReader, line );
1397 
1398  line+= len;
1399  parseQuotedString( unit, aReader, line, &line );
1400 
1401  long tmp;
1402 
1403  if( !unit.ToLong( &tmp, 10 ) )
1404  SCH_PARSE_ERROR( "expected integer value", aReader, line );
1405 
1406  if( tmp < 0 || tmp > MAX_UNIT_COUNT_PER_PACKAGE )
1407  SCH_PARSE_ERROR( "unit value out of range", aReader, line );
1408 
1409  component->AddHierarchicalReference( path, reference, (int)tmp );
1410  component->GetField( REFERENCE )->SetText( reference );
1411 
1412  }
1413  else if( strCompare( "F", line, &line ) )
1414  {
1415  int index = parseInt( aReader, line, &line );
1416 
1417  wxString text, name;
1418 
1419  parseQuotedString( text, aReader, line, &line, true );
1420 
1421  char orientation = parseChar( aReader, line, &line );
1422  wxPoint pos;
1423  pos.x = parseInt( aReader, line, &line );
1424  pos.y = parseInt( aReader, line, &line );
1425  int size = parseInt( aReader, line, &line );
1426  int attributes = parseHex( aReader, line, &line );
1427 
1428  if( index >= component->GetFieldCount() )
1429  {
1430  // The first MANDATOR_FIELDS _must_ be constructed within
1431  // the SCH_COMPONENT constructor. This assert is simply here
1432  // to guard against a change in that constructor.
1433  wxASSERT( component->GetFieldCount() >= MANDATORY_FIELDS );
1434 
1435  // Ignore the _supplied_ fieldNdx. It is not important anymore
1436  // if within the user defined fields region (i.e. >= MANDATORY_FIELDS).
1437  // We freely renumber the index to fit the next available field slot.
1438  index = component->GetFieldCount(); // new has this index after insertion
1439 
1440  SCH_FIELD field( wxPoint( 0, 0 ), -1, component.get(), name );
1441  component->AddField( field );
1442  }
1443 
1444  // Prior to version 2 of the schematic file format, none of the following existed.
1445  if( m_version > 1 )
1446  {
1447  wxString textAttrs;
1448  char hjustify = parseChar( aReader, line, &line );
1449 
1450  parseUnquotedString( textAttrs, aReader, line, &line );
1451 
1452  // The name of the field is optional.
1453  parseQuotedString( name, aReader, line, &line, true );
1454 
1455  if( hjustify == 'L' )
1456  component->GetField( index )->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1457  else if( hjustify == 'R' )
1458  component->GetField( index )->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1459  else if( hjustify != 'C' )
1460  SCH_PARSE_ERROR( "component field text horizontal justification must be "
1461  "L, R, or C", aReader, line );
1462 
1463  // We are guaranteed to have a least one character here for older file formats
1464  // otherwise an exception would have been raised..
1465  if( textAttrs[0] == 'T' )
1466  component->GetField( index )->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
1467  else if( textAttrs[0] == 'B' )
1468  component->GetField( index )->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
1469  else if( textAttrs[0] != 'C' )
1470  SCH_PARSE_ERROR( "component field text vertical justification must be "
1471  "B, T, or C", aReader, line );
1472 
1473  // Newer file formats include the bold and italics text attribute.
1474  if( textAttrs.Length() > 1 )
1475  {
1476  if( textAttrs.Length() != 3 )
1477  SCH_PARSE_ERROR( _( "component field text attributes must be 3 characters wide" ),
1478  aReader, line );
1479 
1480  if( textAttrs[1] == 'I' )
1481  component->GetField( index )->SetItalic( true );
1482  else if( textAttrs[1] != 'N' )
1483  SCH_PARSE_ERROR( "component field text italics indicator must be I or N",
1484  aReader, line );
1485 
1486  if( textAttrs[2] == 'B' )
1487  component->GetField( index )->SetBold( true );
1488  else if( textAttrs[2] != 'N' )
1489  SCH_PARSE_ERROR( "component field text bold indicator must be B or N",
1490  aReader, line );
1491  }
1492  }
1493 
1494  component->GetField( index )->SetText( text );
1495  component->GetField( index )->SetTextPos( pos );
1496  component->GetField( index )->SetVisible( !attributes );
1497  component->GetField( index )->SetTextSize( wxSize( size, size ) );
1498 
1499  if( orientation == 'H' )
1500  component->GetField( index )->SetTextAngle( TEXT_ANGLE_HORIZ );
1501  else if( orientation == 'V' )
1502  component->GetField( index )->SetTextAngle( TEXT_ANGLE_VERT );
1503  else
1504  SCH_PARSE_ERROR( "component field orientation must be H or V",
1505  aReader, line );
1506 
1507  if( name.IsEmpty() )
1509 
1510  component->GetField( index )->SetName( name );
1511  }
1512  else if( strCompare( "$EndComp", line ) )
1513  {
1514  // Ensure all flags (some are set by previous initializations) are reset:
1515  component->ClearFlags();
1516  return component.release();
1517  }
1518  else
1519  {
1520  // There are two lines that begin with a tab or spaces that includes a line with the
1521  // redundant position information and the transform matrix settings.
1522 
1523  // Parse the redundant position information just the same to check for formatting
1524  // errors.
1525  parseInt( aReader, line, &line ); // Always 1.
1526  parseInt( aReader, line, &line ); // The X coordinate.
1527  parseInt( aReader, line, &line ); // The Y coordinate.
1528 
1529  line = aReader.ReadLine();
1530 
1531  TRANSFORM transform;
1532 
1533  transform.x1 = parseInt( aReader, line, &line );
1534 
1535  if( transform.x1 < -1 || transform.x1 > 1 )
1536  SCH_PARSE_ERROR( "invalid component X1 transform value", aReader, line );
1537 
1538  transform.y1 = parseInt( aReader, line, &line );
1539 
1540  if( transform.y1 < -1 || transform.y1 > 1 )
1541  SCH_PARSE_ERROR( "invalid component Y1 transform value", aReader, line );
1542 
1543  transform.x2 = parseInt( aReader, line, &line );
1544 
1545  if( transform.x2 < -1 || transform.x2 > 1 )
1546  SCH_PARSE_ERROR( "invalid component X2 transform value", aReader, line );
1547 
1548  transform.y2 = parseInt( aReader, line, &line );
1549 
1550  if( transform.y2 < -1 || transform.y2 > 1 )
1551  SCH_PARSE_ERROR( "invalid component Y2 transform value", aReader, line );
1552 
1553  component->SetTransform( transform );
1554  }
1555 
1556  line = aReader.ReadLine();
1557  }
1558 
1559  SCH_PARSE_ERROR( "invalid component line", aReader, line );
1560 
1561  return NULL; // Prevents compiler warning. Should never get here.
1562 }
1563 
1564 
1565 void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway,
1566  const PROPERTIES* aProperties )
1567 {
1568  wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN object." );
1569  wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
1570 
1571  init( aKiway, aProperties );
1572 
1573  wxFileName fn = aFileName;
1574 
1575  // File names should be absolute. Don't assume everything relative to the project path
1576  // works properly.
1577  wxASSERT( fn.IsAbsolute() );
1578 
1579  FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
1580 
1581  m_out = &formatter; // no ownership
1582 
1583  Format( aScreen );
1584 }
1585 
1586 
1588 {
1589  wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN* object." );
1590  wxCHECK_RET( m_kiway != NULL, "NULL KIWAY* object." );
1591 
1592  // Write the header
1593  m_out->Print( 0, "%s %s %d\n", "EESchema", SCHEMATIC_HEAD_STRING, EESCHEMA_VERSION );
1594 
1595  // Write the project libraries.
1596  for( const PART_LIB& lib : *m_kiway->Prj().SchLibs() )
1597  m_out->Print( 0, "LIBS:%s\n", TO_UTF8( lib.GetName() ) );
1598 
1599  // This section is not used, but written for file compatibility
1600  m_out->Print( 0, "EELAYER %d %d\n", SCH_LAYER_ID_COUNT, 0 );
1601  m_out->Print( 0, "EELAYER END\n" );
1602 
1603  /* Write page info, ScreenNumber and NumberOfScreen; not very meaningful for
1604  * SheetNumber and Sheet Count in a complex hierarchy, but useful in
1605  * simple hierarchy and flat hierarchy. Used also to search the root
1606  * sheet ( ScreenNumber = 1 ) within the files
1607  */
1608  const TITLE_BLOCK& tb = aScreen->GetTitleBlock();
1609  const PAGE_INFO& page = aScreen->GetPageSettings();
1610 
1611  m_out->Print( 0, "$Descr %s %d %d%s\n", TO_UTF8( page.GetType() ),
1612  page.GetWidthMils(),
1613  page.GetHeightMils(),
1614  !page.IsCustom() && page.IsPortrait() ? " portrait" : "" );
1615  m_out->Print( 0, "encoding utf-8\n" );
1616  m_out->Print( 0, "Sheet %d %d\n", aScreen->m_ScreenNumber, aScreen->m_NumberOfScreens );
1617  m_out->Print( 0, "Title %s\n", EscapedUTF8( tb.GetTitle() ).c_str() );
1618  m_out->Print( 0, "Date %s\n", EscapedUTF8( tb.GetDate() ).c_str() );
1619  m_out->Print( 0, "Rev %s\n", EscapedUTF8( tb.GetRevision() ).c_str() );
1620  m_out->Print( 0, "Comp %s\n", EscapedUTF8( tb.GetCompany() ).c_str() );
1621  m_out->Print( 0, "Comment1 %s\n", EscapedUTF8( tb.GetComment1() ).c_str() );
1622  m_out->Print( 0, "Comment2 %s\n", EscapedUTF8( tb.GetComment2() ).c_str() );
1623  m_out->Print( 0, "Comment3 %s\n", EscapedUTF8( tb.GetComment3() ).c_str() );
1624  m_out->Print( 0, "Comment4 %s\n", EscapedUTF8( tb.GetComment4() ).c_str() );
1625  m_out->Print( 0, "$EndDescr\n" );
1626 
1627  for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
1628  {
1629  switch( item->Type() )
1630  {
1631  case SCH_COMPONENT_T:
1632  saveComponent( dynamic_cast< SCH_COMPONENT* >( item ) );
1633  break;
1634  case SCH_BITMAP_T:
1635  saveBitmap( dynamic_cast< SCH_BITMAP* >( item ) );
1636  break;
1637  case SCH_SHEET_T:
1638  saveSheet( dynamic_cast< SCH_SHEET* >( item ) );
1639  break;
1640  case SCH_JUNCTION_T:
1641  saveJunction( dynamic_cast< SCH_JUNCTION* >( item ) );
1642  break;
1643  case SCH_NO_CONNECT_T:
1644  saveNoConnect( dynamic_cast< SCH_NO_CONNECT* >( item ) );
1645  break;
1646  case SCH_BUS_WIRE_ENTRY_T:
1647  case SCH_BUS_BUS_ENTRY_T:
1648  saveBusEntry( dynamic_cast< SCH_BUS_ENTRY_BASE* >( item ) );
1649  break;
1650  case SCH_LINE_T:
1651  saveLine( dynamic_cast< SCH_LINE* >( item ) );
1652  break;
1653  case SCH_TEXT_T:
1654  case SCH_LABEL_T:
1655  case SCH_GLOBAL_LABEL_T:
1657  saveText( dynamic_cast< SCH_TEXT* >( item ) );
1658  break;
1659  default:
1660  wxASSERT( "Unexpected schematic object type in SCH_LEGACY_PLUGIN::Format()" );
1661  }
1662  }
1663 
1664  m_out->Print( 0, "$EndSCHEMATC\n" );
1665 }
1666 
1667 
1669 {
1670  std::string name1;
1671  std::string name2;
1672  wxArrayString reference_fields;
1673 
1674  static wxString delimiters( wxT( " " ) );
1675 
1676  // This is redundant with the AR entries below, but it makes the files backwards-compatible.
1677  if( aComponent->GetPathsAndReferences().GetCount() > 0 )
1678  {
1679  reference_fields = wxStringTokenize( aComponent->GetPathsAndReferences()[0], delimiters );
1680  name1 = toUTFTildaText( reference_fields[1] );
1681  }
1682  else
1683  {
1684  if( aComponent->GetField( REFERENCE )->GetText().IsEmpty() )
1685  name1 = toUTFTildaText( aComponent->GetPrefix() );
1686  else
1687  name1 = toUTFTildaText( aComponent->GetField( REFERENCE )->GetText() );
1688  }
1689 
1690  wxString part_name = aComponent->GetLibId().GetLibItemName();
1691 
1692  if( part_name.size() )
1693  {
1694  name2 = toUTFTildaText( part_name );
1695  }
1696  else
1697  {
1698  name2 = "_NONAME_";
1699  }
1700 
1701  m_out->Print( 0, "$Comp\n" );
1702  m_out->Print( 0, "L %s %s\n", name2.c_str(), name1.c_str() );
1703 
1704  // Generate unit number, convert and time stamp
1705  m_out->Print( 0, "U %d %d %8.8lX\n", aComponent->GetUnit(), aComponent->GetConvert(),
1706  (unsigned long)aComponent->GetTimeStamp() );
1707 
1708  // Save the position
1709  m_out->Print( 0, "P %d %d\n", aComponent->GetPosition().x, aComponent->GetPosition().y );
1710 
1711  /* If this is a complex hierarchy; save hierarchical references.
1712  * but for simple hierarchies it is not necessary.
1713  * the reference inf is already saved
1714  * this is useful for old Eeschema version compatibility
1715  */
1716  if( aComponent->GetPathsAndReferences().GetCount() > 1 )
1717  {
1718  for( unsigned int ii = 0; ii < aComponent->GetPathsAndReferences().GetCount(); ii++ )
1719  {
1720  /*format:
1721  * AR Path="/140/2" Ref="C99" Part="1"
1722  * where 140 is the uid of the containing sheet
1723  * and 2 is the timestamp of this component.
1724  * (timestamps are actually 8 hex chars)
1725  * Ref is the conventional component reference for this 'path'
1726  * Part is the conventional component part selection for this 'path'
1727  */
1728  reference_fields = wxStringTokenize( aComponent->GetPathsAndReferences()[ii],
1729  delimiters );
1730 
1731  m_out->Print( 0, "AR Path=\"%s\" Ref=\"%s\" Part=\"%s\" \n",
1732  TO_UTF8( reference_fields[0] ),
1733  TO_UTF8( reference_fields[1] ),
1734  TO_UTF8( reference_fields[2] ) );
1735  }
1736  }
1737 
1738  // update the ugly field index, which I would like to see go away someday soon.
1739  for( int i = 0; i < aComponent->GetFieldCount(); ++i )
1740  aComponent->GetField( i )->SetId( i );
1741 
1742  // Fixed fields:
1743  // Save mandatory fields even if they are blank,
1744  // because the visibility, size and orientation are set from libary editor.
1745  for( unsigned i = 0; i < MANDATORY_FIELDS; ++i )
1746  saveField( aComponent->GetField( i ) );
1747 
1748  // User defined fields:
1749  // The *policy* about which user defined fields are part of a symbol is now
1750  // only in the dialog editors. No policy should be enforced here, simply
1751  // save all the user defined fields, they are present because a dialog editor
1752  // thought they should be. If you disagree, go fix the dialog editors.
1753  for( int i = MANDATORY_FIELDS; i < aComponent->GetFieldCount(); ++i )
1754  saveField( aComponent->GetField( i ) );
1755 
1756  // Unit number, position, box ( old standard )
1757  m_out->Print( 0, "\t%-4d %-4d %-4d\n", aComponent->GetUnit(), aComponent->GetPosition().x,
1758  aComponent->GetPosition().y );
1759 
1760  TRANSFORM transform = aComponent->GetTransform();
1761 
1762  m_out->Print( 0, "\t%-4d %-4d %-4d %-4d\n",
1763  transform.x1, transform.y1, transform.x2, transform.y2 );
1764  m_out->Print( 0, "$EndComp\n" );
1765 }
1766 
1767 
1769 {
1770  char hjustify = 'C';
1771 
1772  if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
1773  hjustify = 'L';
1774  else if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
1775  hjustify = 'R';
1776 
1777  char vjustify = 'C';
1778 
1779  if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
1780  vjustify = 'B';
1781  else if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
1782  vjustify = 'T';
1783 
1784  m_out->Print( 0, "F %d %s %c %-3d %-3d %-3d %4.4X %c %c%c%c",
1785  aField->GetId(),
1786  EscapedUTF8( aField->GetText() ).c_str(), // wraps in quotes too
1787  aField->GetTextAngle() == TEXT_ANGLE_HORIZ ? 'H' : 'V',
1788  aField->GetLibPosition().x, aField->GetLibPosition().y,
1789  aField->GetTextWidth(),
1790  !aField->IsVisible(),
1791  hjustify, vjustify,
1792  aField->IsItalic() ? 'I' : 'N',
1793  aField->IsBold() ? 'B' : 'N' );
1794 
1795  // Save field name, if the name is user definable
1796  if( aField->GetId() >= FIELD1 )
1797  {
1798  m_out->Print( 0, " %s", EscapedUTF8( aField->GetName() ).c_str() );
1799  }
1800 
1801  m_out->Print( 0, "\n" );
1802 }
1803 
1804 
1806 {
1807  wxCHECK_RET( aBitmap != NULL, "SCH_BITMAP* is NULL" );
1808 
1809  wxImage* image = aBitmap->GetImage()->GetImageData();
1810 
1811  wxCHECK_RET( image != NULL, "wxImage* is NULL" );
1812 
1813  m_out->Print( 0, "$Bitmap\n" );
1814  m_out->Print( 0, "Pos %-4d %-4d\n", aBitmap->GetPosition().x, aBitmap->GetPosition().y );
1815  m_out->Print( 0, "Scale %f\n", aBitmap->GetImage()->GetScale() );
1816  m_out->Print( 0, "Data\n" );
1817 
1818  wxMemoryOutputStream stream;
1819 
1820  image->SaveFile( stream, wxBITMAP_TYPE_PNG );
1821 
1822  // Write binary data in hexadecimal form (ASCII)
1823  wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
1824  char* begin = (char*) buffer->GetBufferStart();
1825 
1826  for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
1827  {
1828  if( ii >= 32 )
1829  {
1830  ii = 0;
1831 
1832  m_out->Print( 0, "\n" );
1833  }
1834 
1835  m_out->Print( 0, "%2.2X ", *begin & 0xFF );
1836  }
1837 
1838  m_out->Print( 0, "\nEndData\n" );
1839  m_out->Print( 0, "$EndBitmap\n" );
1840 }
1841 
1842 
1844 {
1845  wxCHECK_RET( aSheet != NULL, "SCH_SHEET* is NULL" );
1846 
1847  m_out->Print( 0, "$Sheet\n" );
1848  m_out->Print( 0, "S %-4d %-4d %-4d %-4d\n",
1849  aSheet->GetPosition().x, aSheet->GetPosition().y,
1850  aSheet->GetSize().x, aSheet->GetSize().y );
1851 
1852  m_out->Print( 0, "U %8.8lX\n", (unsigned long) aSheet->GetTimeStamp() );
1853 
1854  if( !aSheet->GetName().IsEmpty() )
1855  m_out->Print( 0, "F0 %s %d\n", EscapedUTF8( aSheet->GetName() ).c_str(),
1856  aSheet->GetSheetNameSize() );
1857 
1858  if( !aSheet->GetFileName().IsEmpty() )
1859  m_out->Print( 0, "F1 %s %d\n", EscapedUTF8( aSheet->GetFileName() ).c_str(),
1860  aSheet->GetFileNameSize() );
1861 
1862  for( const SCH_SHEET_PIN& pin : aSheet->GetPins() )
1863  {
1864  int type, side;
1865 
1866  if( pin.GetText().IsEmpty() )
1867  break;
1868 
1869  switch( pin.GetEdge() )
1870  {
1871  default:
1873  side = 'L';
1874  break;
1875 
1877  side = 'R';
1878  break;
1879 
1881  side = 'T';
1882  break;
1883 
1885  side = 'B';
1886  break;
1887  }
1888 
1889  switch( pin.GetShape() )
1890  {
1891  case NET_INPUT:
1892  type = 'I'; break;
1893 
1894  case NET_OUTPUT:
1895  type = 'O'; break;
1896 
1897  case NET_BIDI:
1898  type = 'B'; break;
1899 
1900  case NET_TRISTATE:
1901  type = 'T'; break;
1902 
1903  default:
1904  case NET_UNSPECIFIED:
1905  type = 'U'; break;
1906  }
1907 
1908  m_out->Print( 0, "F%d %s %c %c %-3d %-3d %-3d\n", pin.GetNumber(),
1909  EscapedUTF8( pin.GetText() ).c_str(), // supplies wrapping quotes
1910  type, side, pin.GetPosition().x, pin.GetPosition().y,
1911  pin.GetTextWidth() );
1912  }
1913 
1914  m_out->Print( 0, "$EndSheet\n" );
1915 }
1916 
1917 
1919 {
1920  wxCHECK_RET( aJunction != NULL, "SCH_JUNCTION* is NULL" );
1921 
1922  m_out->Print( 0, "Connection ~ %-4d %-4d\n",
1923  aJunction->GetPosition().x, aJunction->GetPosition().y );
1924 }
1925 
1926 
1928 {
1929  wxCHECK_RET( aNoConnect != NULL, "SCH_NOCONNECT* is NULL" );
1930 
1931  m_out->Print( 0, "NoConn ~ %-4d %-4d\n", aNoConnect->GetPosition().x,
1932  aNoConnect->GetPosition().y );
1933 }
1934 
1935 
1937 {
1938  wxCHECK_RET( aBusEntry != NULL, "SCH_BUS_ENTRY_BASE* is NULL" );
1939 
1940  if( aBusEntry->GetLayer() == LAYER_WIRE )
1941  m_out->Print( 0, "Entry Wire Line\n\t%-4d %-4d %-4d %-4d\n",
1942  aBusEntry->GetPosition().x, aBusEntry->GetPosition().y,
1943  aBusEntry->m_End().x, aBusEntry->m_End().y );
1944  else
1945  m_out->Print( 0, "Entry Bus Bus\n\t%-4d %-4d %-4d %-4d\n",
1946  aBusEntry->GetPosition().x, aBusEntry->GetPosition().y,
1947  aBusEntry->m_End().x, aBusEntry->m_End().y );
1948 }
1949 
1950 
1952 {
1953  wxCHECK_RET( aLine != NULL, "SCH_LINE* is NULL" );
1954 
1955  const char* layer = "Notes";
1956  const char* width = "Line";
1957 
1958  if( aLine->GetLayer() == LAYER_WIRE )
1959  layer = "Wire";
1960  else if( aLine->GetLayer() == LAYER_BUS )
1961  layer = "Bus";
1962 
1963  m_out->Print( 0, "Wire %s %s\n", layer, width );
1964  m_out->Print( 0, "\t%-4d %-4d %-4d %-4d\n", aLine->GetStartPoint().x, aLine->GetStartPoint().y,
1965  aLine->GetEndPoint().x, aLine->GetEndPoint().y );
1966 }
1967 
1968 
1970 {
1971  wxCHECK_RET( aText != NULL, "SCH_TEXT* is NULL" );
1972 
1973  const char* italics = "~";
1974  const char* textType = "Notes";
1975 
1976  if( aText->IsItalic() )
1977  italics = "Italic";
1978 
1979  wxString text = aText->GetText();
1980 
1981  SCH_LAYER_ID layer = aText->GetLayer();
1982 
1983  if( layer == LAYER_NOTES || layer == LAYER_LOCLABEL )
1984  {
1985  if( layer == LAYER_NOTES )
1986  {
1987  // For compatibility reasons, the text must be saved in only one text line
1988  // so replace all EOLs with \\n
1989  text.Replace( wxT( "\n" ), wxT( "\\n" ) );
1990 
1991  // Here we should have no CR or LF character in line
1992  // This is not always the case if a multiline text was copied (using a copy/paste
1993  // function) from a text that uses E.O.L characters that differs from the current
1994  // EOL format. This is mainly the case under Linux using LF symbol when copying
1995  // a text from Windows (using CRLF symbol) so we must just remove the extra CR left
1996  // (or LF left under MacOSX)
1997  for( unsigned ii = 0; ii < text.Len(); )
1998  {
1999  if( text[ii] == 0x0A || text[ii] == 0x0D )
2000  text.erase( ii, 1 );
2001  else
2002  ii++;
2003  }
2004  }
2005  else
2006  {
2007  textType = "Label";
2008  }
2009 
2010  m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %d\n%s\n", textType,
2011  aText->GetPosition().x, aText->GetPosition().y,
2012  aText->GetLabelSpinStyle(),
2013  aText->GetTextWidth(),
2014  italics, aText->GetThickness(), TO_UTF8( text ) );
2015  }
2016  else if( layer == LAYER_GLOBLABEL || layer == LAYER_HIERLABEL )
2017  {
2018  textType = ( layer == LAYER_GLOBLABEL ) ? "GLabel" : "HLabel";
2019 
2020  m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %s %d\n%s\n", textType,
2021  aText->GetPosition().x, aText->GetPosition().y,
2022  aText->GetLabelSpinStyle(),
2023  aText->GetTextWidth(),
2024  SheetLabelType[aText->GetShape()],
2025  italics,
2026  aText->GetThickness(), TO_UTF8( text ) );
2027  }
2028 }
2029 
2030 
2031 SCH_LEGACY_PLUGIN_CACHE::SCH_LEGACY_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) :
2032  m_libFileName( aFullPathAndFileName ),
2033  m_isWritable( true ),
2034  m_isModified( false ),
2035  m_modHash( 1 )
2036 {
2037  m_versionMajor = -1;
2038  m_versionMinor = -1;
2040 }
2041 
2042 
2044 {
2045  // When the cache is destroyed, all of the alias objects on the heap should be deleted.
2046  for( LIB_ALIAS_MAP::iterator it = m_aliases.begin(); it != m_aliases.end(); ++it )
2047  {
2048  wxLogTrace( traceSchLegacyPlugin, wxT( "Removing alias %s from library %s." ),
2049  GetChars( it->second->GetName() ), GetChars( GetLogicalName() ) );
2050  LIB_PART* part = it->second->GetPart();
2051  LIB_ALIAS* alias = it->second;
2052  delete alias;
2053 
2054  // When the last alias of a part is destroyed, the part is no longer required and it
2055  // too is destroyed.
2056  if( part && part->GetAliasCount() == 0 )
2057  delete part;
2058  }
2059 
2060  m_aliases.clear();
2061 }
2062 
2063 
2065 {
2066  // update the writable flag while we have a wxFileName, in a network this
2067  // is possibly quite dynamic anyway.
2068  m_isWritable = m_libFileName.IsFileWritable();
2069 
2070  return m_libFileName.GetModificationTime();
2071 }
2072 
2073 
2074 bool SCH_LEGACY_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const
2075 {
2076  return m_libFileName == aFullPathAndFileName;
2077 }
2078 
2079 
2081 {
2082  if( m_fileModTime.IsValid() && m_libFileName.IsOk() && m_libFileName.FileExists() )
2083  return m_libFileName.GetModificationTime() != m_fileModTime;
2084 
2085  return false;
2086 }
2087 
2088 
2090 {
2091  wxCHECK_MSG( aAlias != NULL, NULL, "NULL pointer cannot be removed from library." );
2092 
2093  LIB_ALIAS_MAP::iterator it = m_aliases.find( aAlias->GetName() );
2094 
2095  if( it == m_aliases.end() )
2096  return NULL;
2097 
2098  // If the entry pointer doesn't match the name it is mapped to in the library, we
2099  // have done something terribly wrong.
2100  wxCHECK_MSG( *it->second == aAlias, NULL,
2101  "Pointer mismatch while attempting to remove alias entry <" + aAlias->GetName() +
2102  "> from library cache <" + m_libFileName.GetName() + ">." );
2103 
2104  LIB_ALIAS* alias = aAlias;
2105  LIB_PART* part = alias->GetPart();
2106 
2107  alias = part->RemoveAlias( alias );
2108 
2109  if( !alias )
2110  {
2111  delete part;
2112 
2113  if( m_aliases.size() > 1 )
2114  {
2115  LIB_ALIAS_MAP::iterator next = it;
2116  next++;
2117 
2118  if( next == m_aliases.end() )
2119  next = m_aliases.begin();
2120 
2121  alias = next->second;
2122  }
2123  }
2124 
2125  m_aliases.erase( it );
2126  m_isModified = true;
2127  ++m_modHash;
2128  return alias;
2129 }
2130 
2131 
2133 {
2134  // aPart is cloned in PART_LIB::AddPart(). The cache takes ownership of aPart.
2135  wxArrayString aliasNames = aPart->GetAliasNames();
2136 
2137  for( size_t i = 0; i < aliasNames.size(); i++ )
2138  {
2139  LIB_ALIAS_MAP::iterator it = m_aliases.find( aliasNames[i] );
2140 
2141  if( it != m_aliases.end() )
2142  removeAlias( it->second );
2143 
2144  LIB_ALIAS* alias = const_cast< LIB_PART* >( aPart )->GetAlias( aliasNames[i] );
2145 
2146  wxASSERT_MSG( alias != NULL, "No alias <" + aliasNames[i] + "> found in symbol <" +
2147  aPart->GetName() +">." );
2148 
2149  m_aliases[ aliasNames[i] ] = alias;
2150  }
2151 
2152  m_isModified = true;
2153  ++m_modHash;
2154 }
2155 
2156 
2158 {
2159  wxCHECK_RET( m_libFileName.IsAbsolute(),
2160  wxString::Format( "Cannot use relative file paths in legacy plugin to "
2161  "open library '%s'.", m_libFileName.GetFullPath() ) );
2162 
2163  wxLogTrace( traceSchLegacyPlugin, "Loading legacy symbol file '%s'",
2164  m_libFileName.GetFullPath() );
2165 
2166  FILE_LINE_READER reader( m_libFileName.GetFullPath() );
2167 
2168  if( !reader.ReadLine() )
2169  THROW_IO_ERROR( _( "unexpected end of file" ) );
2170 
2171  const char* line = reader.Line();
2172 
2173  if( !strCompare( "EESchema-LIBRARY Version", line, &line ) )
2174  {
2175  // Old .sym files (which are libraries with only one symbol, used to store and reuse shapes)
2176  // EESchema-LIB Version x.x SYMBOL. They are valid files.
2177  if( !strCompare( "EESchema-LIB Version", line, &line ) )
2178  SCH_PARSE_ERROR( "file is not a valid component or symbol library file", reader, line );
2179  }
2180 
2181  m_versionMajor = parseInt( reader, line, &line );
2182 
2183  if( *line != '.' )
2184  SCH_PARSE_ERROR( "invalid file version formatting in header", reader, line );
2185 
2186  line++;
2187 
2188  m_versionMinor = parseInt( reader, line, &line );
2189 
2190  if( m_versionMajor < 1 || m_versionMinor < 0 || m_versionMinor > 99 )
2191  SCH_PARSE_ERROR( "invalid file version in header", reader, line );
2192 
2193  // Check if this is a symbol library which is the same as a component library but without
2194  // any alias, documentation, footprint filters, etc.
2195  if( strCompare( "SYMBOL", line, &line ) )
2196  {
2197  // Symbol files add date and time stamp info to the header.
2199 
2201  }
2202  else
2203  {
2205  }
2206 
2207  while( reader.ReadLine() )
2208  {
2209  line = reader.Line();
2210 
2211  if( *line == '#' || isspace( *line ) ) // Skip comments and blank lines.
2212  continue;
2213 
2214  // Headers where only supported in older library file formats.
2215  if( m_libType == LIBRARY_TYPE_EESCHEMA && strCompare( "$HEADER", line ) )
2216  loadHeader( reader );
2217 
2218  if( strCompare( "DEF", line ) )
2219  {
2220  // Read one DEF/ENDDEF part entry from library:
2221  loadPart( reader );
2222 
2223  }
2224  }
2225 
2226  ++m_modHash;
2227 
2228  // Remember the file modification time of library file when the
2229  // cache snapshot was made, so that in a networked environment we will
2230  // reload the cache as needed.
2232 
2234  loadDocs();
2235 }
2236 
2237 
2239 {
2240  const char* line;
2241  wxString text;
2242  wxString aliasName;
2243  wxFileName fn = m_libFileName;
2244  LIB_ALIAS* alias = NULL;;
2245 
2246  fn.SetExt( DOC_EXT );
2247 
2248  // Not all libraries will have a document file.
2249  if( !fn.FileExists() )
2250  return;
2251 
2252  if( !fn.IsFileReadable() )
2253  THROW_IO_ERROR( wxString::Format( _( "user does not have permission to read library "
2254  "document file '%s'" ), fn.GetFullPath() ) );
2255 
2256  FILE_LINE_READER reader( fn.GetFullPath() );
2257 
2258  line = reader.ReadLine();
2259 
2260  if( !line )
2261  THROW_IO_ERROR( _( "symbol document library file is empty" ) );
2262 
2263  if( !strCompare( DOCFILE_IDENT, line, &line ) )
2264  SCH_PARSE_ERROR( "invalid document library file version formatting in header",
2265  reader, line );
2266 
2267  while( reader.ReadLine() )
2268  {
2269  line = reader.Line();
2270 
2271  if( *line == '#' ) // Comment line.
2272  continue;
2273 
2274  if( !strCompare( "$CMP", line, &line ) != 0 )
2275  SCH_PARSE_ERROR( "$CMP command expected", reader, line );
2276 
2277  parseUnquotedString( aliasName, reader, line, &line ); // Alias name.
2278 
2279  LIB_ALIAS_MAP::iterator it = m_aliases.find( aliasName );
2280 
2281  if( it == m_aliases.end() )
2282  wxLogWarning( "Alias '%s' not found in library:\n\n"
2283  "'%s'\n\nat line %d offset %d", aliasName, fn.GetFullPath(),
2284  reader.LineNumber(), (int) (line - reader.Line() ) );
2285  else
2286  alias = it->second;
2287 
2288  // Read the curent alias associated doc.
2289  // if the alias does not exist, just skip the description
2290  // (Can happen if a .dcm is not synchronized with the corresponding .lib file)
2291  while( reader.ReadLine() )
2292  {
2293  line = reader.Line();
2294 
2295  if( !line )
2296  SCH_PARSE_ERROR( "unexpected end of file", reader, line );
2297 
2298  if( strCompare( "$ENDCMP", line, &line ) )
2299  break;
2300 
2301  text = FROM_UTF8( line + 2 );
2302  text = text.Trim();
2303 
2304  switch( line[0] )
2305  {
2306  case 'D':
2307  if( alias )
2308  alias->SetDescription( text );
2309  break;
2310 
2311  case 'K':
2312  if( alias )
2313  alias->SetKeyWords( text );
2314  break;
2315 
2316  case 'F':
2317  if( alias )
2318  alias->SetDocFileName( text );
2319  break;
2320 
2321  case '#':
2322  break;
2323 
2324  default:
2325  SCH_PARSE_ERROR( "expected token in symbol definition", reader, line );
2326  }
2327  }
2328  }
2329 }
2330 
2331 
2333 {
2334  const char* line = aReader.Line();
2335 
2336  wxASSERT( strCompare( "$HEADER", line, &line ) );
2337 
2338  while( aReader.ReadLine() )
2339  {
2340  line = (char*) aReader;
2341 
2342  // The time stamp saved in old library files is not used or saved in the latest
2343  // library file version.
2344  if( strCompare( "TimeStamp", line, &line ) )
2345  continue;
2346  else if( strCompare( "$ENDHEADER", line, &line ) )
2347  return;
2348  }
2349 
2350  SCH_PARSE_ERROR( "$ENDHEADER not found", aReader, line );
2351 }
2352 
2353 
2355 {
2356  const char* line = aReader.Line();
2357 
2358  wxCHECK( strCompare( "DEF", line, &line ), NULL );
2359 
2360  // Read DEF line:
2361  char yes_no = 0;
2362 
2363  std::unique_ptr< LIB_PART > part( new LIB_PART( wxEmptyString ) );
2364 
2365  wxString name, prefix;
2366 
2367  parseUnquotedString( name, aReader, line, &line ); // Part name.
2368  parseUnquotedString( prefix, aReader, line, &line ); // Prefix name
2369  parseInt( aReader, line, &line ); // NumOfPins, unused.
2370  part->SetPinNameOffset( parseInt( aReader, line, &line ) ); // Pin name offset.
2371  yes_no = parseChar( aReader, line, &line ); // Show pin numbers.
2372 
2373  if( !( yes_no == 'Y' || yes_no == 'N') )
2374  SCH_PARSE_ERROR( "expected Y or N", aReader, line );
2375 
2376  part->SetShowPinNumbers( ( yes_no == 'N' ) ? false : true );
2377 
2378  yes_no = parseChar( aReader, line, &line ); // Show pin numbers.
2379 
2380  if( !( yes_no == 'Y' || yes_no == 'N') )
2381  SCH_PARSE_ERROR( "expected Y or N", aReader, line );
2382 
2383  part->SetShowPinNames( ( yes_no == 'N' ) ? false : true ); // Show pin names.
2384 
2385  part->SetUnitCount( parseInt( aReader, line, &line ) ); // Number of units.
2386 
2387  // Ensure m_unitCount is >= 1. Could be read as 0 in old libraries.
2388  if( part->GetUnitCount() < 1 )
2389  part->SetUnitCount( 1 );
2390 
2391  // Copy part name and prefix.
2392  LIB_FIELD& value = part->GetValueField();
2393 
2394  // The root alias is added to the alias list by SetName() which is called by SetText().
2395  if( name.IsEmpty() )
2396  {
2397  part->m_name = "~";
2398  value.SetText( "~" );
2399  }
2400  else if( name[0] != '~' )
2401  {
2402  part->m_name = name;
2403  value.SetText( name );
2404  }
2405  else
2406  {
2407  name = name.Right( name.Length() - 1 );
2408  part->m_name = name;
2409  value.SetText( name );
2410  value.SetVisible( false );
2411  }
2412 
2413  // There are some code paths in SetText() that do not set the root alias to the
2414  // alias list so add it here if it didn't get added by SetText().
2415  if( !part->HasAlias( part->GetName() ) )
2416  part->AddAlias( part->GetName() );
2417 
2418  LIB_FIELD& reference = part->GetReferenceField();
2419 
2420  if( prefix == "~" )
2421  {
2422  reference.Empty();
2423  reference.SetVisible( false );
2424  }
2425  else
2426  {
2427  reference.SetText( prefix );
2428  }
2429 
2430  // In version 2.2 and earlier, this parameter was a '0' which was just a place holder.
2431  // The was no concept of interchangeable multiple unit symbols.
2433  {
2434  // Nothing needs to be set since the default setting for symbols with multiple
2435  // units were never interchangeable. Just parse the 0 an move on.
2436  parseInt( aReader, line, &line );
2437  }
2438  else
2439  {
2440  char locked = parseChar( aReader, line, &line );
2441 
2442  if( locked == 'L' )
2443  part->LockUnits( true );
2444  else if( locked == 'F' || locked == '0' )
2445  part->LockUnits( false );
2446  else
2447  SCH_PARSE_ERROR( "expected L, F, or 0", aReader, line );
2448  }
2449 
2450 
2451  // There is the optional power component flag.
2452  if( *line )
2453  {
2454  char power = parseChar( aReader, line, &line );
2455 
2456  if( power == 'P' )
2457  part->SetPower();
2458  else if( power == 'N' )
2459  part->SetNormal();
2460  else
2461  SCH_PARSE_ERROR( "expected P or N", aReader, line );
2462  }
2463 
2464  line = aReader.ReadLine();
2465 
2466  // Read lines until "ENDDEF" is found.
2467  while( line )
2468  {
2469  if( *line == '#' ) // Comment
2470  ;
2471  else if( strCompare( "Ti", line, &line ) ) // Modification date is ignored.
2472  continue;
2473  else if( strCompare( "ALIAS", line, &line ) ) // Aliases
2474  loadAliases( part, aReader );
2475  else if( *line == 'F' ) // Fields
2476  loadField( part, aReader );
2477  else if( strCompare( "DRAW", line, &line ) ) // Drawing objects.
2478  loadDrawEntries( part, aReader );
2479  else if( strCompare( "$FPLIST", line, &line ) ) // Footprint filter list
2480  loadFootprintFilters( part, aReader );
2481  else if( strCompare( "ENDDEF", line, &line ) ) // End of part description
2482  {
2483  // Now all is good, Add the root alias to the cache alias list.
2484  m_aliases[ part->GetName() ] = part->GetAlias( part->GetName() );
2485 
2486  // Add aliases when exist
2487  for( size_t ii = 0; ii < part->GetAliasCount(); ++ii )
2488  m_aliases[ part->GetAlias( ii )->GetName() ] = part->GetAlias( ii );
2489 
2490  return part.release();
2491  }
2492 
2493  line = aReader.ReadLine();
2494  }
2495 
2496  SCH_PARSE_ERROR( "missing ENDDEF", aReader, line );
2497 }
2498 
2499 
2501 {
2502  wxCHECK_MSG( !aAliasName.IsEmpty(), false, "alias name cannot be empty" );
2503 
2504  // The alias name is not a duplicate so don't change it.
2505  if( m_aliases.find( aAliasName ) == m_aliases.end() )
2506  return false;
2507 
2508  int dupCounter = 1;
2509  wxString newAlias = aAliasName;
2510 
2511  // If the alias is already loaded, the library is broken. It may have been possible in
2512  // the past that this could happen so we assign a new alias name to prevent any conflicts
2513  // rather than throw an exception.
2514  while( m_aliases.find( newAlias ) != m_aliases.end() )
2515  {
2516  newAlias = aAliasName << dupCounter;
2517  dupCounter++;
2518  }
2519 
2520  aAliasName = newAlias;
2521 
2522  return true;
2523 }
2524 
2525 
2526 void SCH_LEGACY_PLUGIN_CACHE::loadAliases( std::unique_ptr< LIB_PART >& aPart,
2527  FILE_LINE_READER& aReader )
2528 {
2529  wxString newAlias;
2530  const char* line = aReader.Line();
2531 
2532  wxCHECK_RET( strCompare( "ALIAS", line, &line ), "Invalid ALIAS section" );
2533 
2534  // Parse the ALIAS list.
2535  wxString alias;
2536  parseUnquotedString( alias, aReader, line, &line );
2537 
2538  while( !alias.IsEmpty() )
2539  {
2540  newAlias = alias;
2541  checkForDuplicates( newAlias );
2542  aPart->AddAlias( newAlias );
2543  alias.clear();
2544  parseUnquotedString( alias, aReader, line, &line, true );
2545  }
2546 }
2547 
2548 
2549 void SCH_LEGACY_PLUGIN_CACHE::loadField( std::unique_ptr< LIB_PART >& aPart,
2550  FILE_LINE_READER& aReader )
2551 {
2552  const char* line = aReader.Line();
2553 
2554  wxCHECK_RET( *line == 'F', "Invalid field line" );
2555 
2556  int id;
2557 
2558  if( sscanf( line + 1, "%d", &id ) != 1 || id < 0 )
2559  SCH_PARSE_ERROR( "invalid field ID", aReader, line + 1 );
2560 
2561  std::unique_ptr< LIB_FIELD > field( new LIB_FIELD( aPart.get(), id ) );
2562 
2563  // Skip to the first double quote.
2564  while( *line != '"' && *line != 0 )
2565  line++;
2566 
2567  if( *line == 0 )
2568  SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, line );
2569 
2570  wxString text;
2571  parseQuotedString( text, aReader, line, &line, true );
2572 
2573  // Doctor the *.lib file field which has a "~" in blank fields. New saves will
2574  // not save like this.
2575  if( text.size() == 1 && text[0] == '~' )
2576  text.clear();
2577 
2578  field->m_Text = text;
2579 
2580  wxPoint pos;
2581 
2582  pos.x = parseInt( aReader, line, &line );
2583  pos.y = parseInt( aReader, line, &line );
2584  field->SetPosition( pos );
2585 
2586  wxSize textSize;
2587 
2588  textSize.x = textSize.y = parseInt( aReader, line, &line );
2589  field->SetTextSize( textSize );
2590 
2591  char textOrient = parseChar( aReader, line, &line );
2592 
2593  if( textOrient == 'H' )
2594  field->SetTextAngle( TEXT_ANGLE_HORIZ );
2595  else if( textOrient == 'V' )
2596  field->SetTextAngle( TEXT_ANGLE_VERT );
2597  else
2598  SCH_PARSE_ERROR( "invalid field text orientation parameter", aReader, line );
2599 
2600  char textVisible = parseChar( aReader, line, &line );
2601 
2602  if( textVisible == 'V' )
2603  field->SetVisible( true );
2604  else if ( textVisible == 'I' )
2605  field->SetVisible( false );
2606  else
2607  SCH_PARSE_ERROR( "invalid field text visibility parameter", aReader, line );
2608 
2609  // It may be technically correct to use the library version to determine if the field text
2610  // attributes are present. If anyone knows if that is valid and what version that would be,
2611  // please change this to test the library version rather than an EOL or the quoted string
2612  // of the field name.
2613  if( *line != 0 && *line != '"' )
2614  {
2615  char textHJustify = parseChar( aReader, line, &line );
2616 
2617  if( textHJustify == 'C' )
2618  field->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
2619  else if( textHJustify == 'L' )
2620  field->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
2621  else if( textHJustify == 'R' )
2622  field->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
2623  else
2624  SCH_PARSE_ERROR( "invalid field text horizontal justification parameter",
2625  aReader, line );
2626 
2627  wxString attributes;
2628 
2629  parseUnquotedString( attributes, aReader, line, &line );
2630 
2631  if( !(attributes.size() == 3 || attributes.size() == 1 ) )
2632  SCH_PARSE_ERROR( "invalid field text attributes size",
2633  aReader, line );
2634 
2635  if( attributes[0] == 'C' )
2636  field->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
2637  else if( attributes[0] == 'B' )
2638  field->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2639  else if( attributes[0] == 'T' )
2640  field->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2641  else
2642  SCH_PARSE_ERROR( "invalid field text vertical justification parameter",
2643  aReader, line );
2644 
2645  if( attributes.size() == 3 )
2646  {
2647  if( attributes[1] == 'I' ) // Italic
2648  field->SetItalic( true );
2649  else if( attributes[1] != 'N' ) // No italics is default, check for error.
2650  SCH_PARSE_ERROR( "invalid field text italic parameter", aReader, line );
2651 
2652  if ( attributes[2] == 'B' ) // Bold
2653  field->SetBold( true );
2654  else if( attributes[2] != 'N' ) // No bold is default, check for error.
2655  SCH_PARSE_ERROR( "invalid field text bold parameter", aReader, line );
2656  }
2657  }
2658 
2659  // Fields in RAM must always have names.
2660  if( id < MANDATORY_FIELDS )
2661  {
2662  // Fields in RAM must always have names, because we are trying to get
2663  // less dependent on field ids and more dependent on names.
2664  // Plus assumptions are made in the field editors.
2665  field->m_name = TEMPLATE_FIELDNAME::GetDefaultFieldName( id );
2666 
2667  LIB_FIELD* fixedField = aPart->GetField( field->GetId() );
2668 
2669  // this will fire only if somebody broke a constructor or editor.
2670  // MANDATORY_FIELDS are always present in ram resident components, no
2671  // exceptions, and they always have their names set, even fixed fields.
2672  wxASSERT( fixedField );
2673 
2674  *fixedField = *field;
2675 
2676  // Ensure the VALUE field = the part name (can be not the case
2677  // with malformed libraries: edited by hand, or converted from other tools)
2678  if( fixedField->GetId() == VALUE )
2679  fixedField->m_Text = aPart->m_name;
2680  }
2681  else
2682  {
2683  wxString name;
2684 
2685  parseQuotedString( name, aReader, line, &line, true ); // Optional.
2686 
2687  if( !name.IsEmpty() )
2688  field->m_name = name;
2689 
2690  aPart->AddDrawItem( field.release() ); // LIB_FIELD* is now owned by the LIB_PART.
2691  }
2692 }
2693 
2694 
2695 void SCH_LEGACY_PLUGIN_CACHE::loadDrawEntries( std::unique_ptr< LIB_PART >& aPart,
2696  FILE_LINE_READER& aReader )
2697 {
2698  const char* line = aReader.Line();
2699 
2700  wxCHECK_RET( strCompare( "DRAW", line, &line ), "Invalid DRAW section" );
2701 
2702  line = aReader.ReadLine();
2703 
2704  while( line )
2705  {
2706  if( strCompare( "ENDDRAW", line, &line ) )
2707  return;
2708 
2709  switch( line[0] )
2710  {
2711  case 'A': // Arc
2712  aPart->AddDrawItem( loadArc( aPart, aReader ) );
2713  break;
2714 
2715  case 'C': // Circle
2716  aPart->AddDrawItem( loadCircle( aPart, aReader ) );
2717  break;
2718 
2719  case 'T': // Text
2720  aPart->AddDrawItem( loadText( aPart, aReader ) );
2721  break;
2722 
2723  case 'S': // Square
2724  aPart->AddDrawItem( loadRectangle( aPart, aReader ) );
2725  break;
2726 
2727  case 'X': // Pin Description
2728  aPart->AddDrawItem( loadPin( aPart, aReader ) );
2729  break;
2730 
2731  case 'P': // Polyline
2732  aPart->AddDrawItem( loadPolyLine( aPart, aReader ) );
2733  break;
2734 
2735  case 'B': // Bezier Curves
2736  aPart->AddDrawItem( loadBezier( aPart, aReader ) );
2737  break;
2738 
2739  case '#': // Comment
2740  case '\n': // Empty line
2741  case '\r':
2742  case 0:
2743  break;
2744 
2745  default:
2746  SCH_PARSE_ERROR( "undefined DRAW entry", aReader, line );
2747  }
2748 
2749  line = aReader.ReadLine();
2750  }
2751 
2752  SCH_PARSE_ERROR( "file ended prematurely loading component draw element", aReader, line );
2753 }
2754 
2755 
2757  const char** aOutput )
2758 {
2759  FILL_T mode;
2760 
2761  switch( parseChar( aReader, aLine, aOutput ) )
2762  {
2763  case 'F':
2764  mode = FILLED_SHAPE;
2765  break;
2766 
2767  case 'f':
2768  mode = FILLED_WITH_BG_BODYCOLOR;
2769  break;
2770 
2771  case 'N':
2772  mode = NO_FILL;
2773  break;
2774 
2775  default:
2776  SCH_PARSE_ERROR( "invalid fill type, expected f, F, or N", aReader, aLine );
2777  }
2778 
2779  return mode;
2780 }
2781 
2782 
2783 LIB_ARC* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr< LIB_PART >& aPart,
2784  FILE_LINE_READER& aReader )
2785 {
2786  const char* line = aReader.Line();
2787 
2788  wxCHECK_MSG( strCompare( "A", line, &line ), NULL, "Invalid LIB_ARC definition" );
2789 
2790  std::unique_ptr< LIB_ARC > arc( new LIB_ARC( aPart.get() ) );
2791 
2792  wxPoint center;
2793 
2794  center.x = parseInt( aReader, line, &line );
2795  center.y = parseInt( aReader, line, &line );
2796 
2797  arc->SetPosition( center );
2798  arc->SetRadius( parseInt( aReader, line, &line ) );
2799 
2800  int angle1 = parseInt( aReader, line, &line );
2801  int angle2 = parseInt( aReader, line, &line );
2802 
2803  NORMALIZE_ANGLE_POS( angle1 );
2804  NORMALIZE_ANGLE_POS( angle2 );
2805  arc->SetFirstRadiusAngle( angle1 );
2806  arc->SetSecondRadiusAngle( angle2 );
2807 
2808  arc->SetUnit( parseInt( aReader, line, &line ) );
2809  arc->SetConvert( parseInt( aReader, line, &line ) );
2810  arc->SetWidth( parseInt( aReader, line, &line ) );
2811 
2812  // Old libraries (version <= 2.2) do not have always this FILL MODE param
2813  // when fill mode is no fill (default mode).
2814  if( *line != 0 )
2815  arc->SetFillMode( parseFillMode( aReader, line, &line ) );
2816 
2817  // Actual Coordinates of arc ends are read from file
2818  if( *line != 0 )
2819  {
2820  wxPoint arcStart, arcEnd;
2821 
2822  arcStart.x = parseInt( aReader, line, &line );
2823  arcStart.y = parseInt( aReader, line, &line );
2824  arcEnd.x = parseInt( aReader, line, &line );
2825  arcEnd.y = parseInt( aReader, line, &line );
2826 
2827  arc->SetStart( arcStart );
2828  arc->SetEnd( arcEnd );
2829  }
2830  else
2831  {
2832  // Actual Coordinates of arc ends are not read from file
2833  // (old library), calculate them
2834  wxPoint arcStart( arc->GetRadius(), 0 );
2835  wxPoint arcEnd( arc->GetRadius(), 0 );
2836 
2837  RotatePoint( &arcStart.x, &arcStart.y, -angle1 );
2838  arcStart += arc->GetPosition();
2839  arc->SetStart( arcStart );
2840  RotatePoint( &arcEnd.x, &arcEnd.y, -angle2 );
2841  arcEnd += arc->GetPosition();
2842  arc->SetEnd( arcEnd );
2843  }
2844 
2845  return arc.release();
2846 }
2847 
2848 
2849 LIB_CIRCLE* SCH_LEGACY_PLUGIN_CACHE::loadCircle( std::unique_ptr< LIB_PART >& aPart,
2850  FILE_LINE_READER& aReader )
2851 {
2852  const char* line = aReader.Line();
2853 
2854  wxCHECK_MSG( strCompare( "C", line, &line ), NULL, "Invalid LIB_CIRCLE definition" );
2855 
2856  std::unique_ptr< LIB_CIRCLE > circle( new LIB_CIRCLE( aPart.get() ) );
2857 
2858  wxPoint center;
2859 
2860  center.x = parseInt( aReader, line, &line );
2861  center.y = parseInt( aReader, line, &line );
2862 
2863  circle->SetPosition( center );
2864  circle->SetRadius( parseInt( aReader, line, &line ) );
2865  circle->SetUnit( parseInt( aReader, line, &line ) );
2866  circle->SetConvert( parseInt( aReader, line, &line ) );
2867  circle->SetWidth( parseInt( aReader, line, &line ) );
2868 
2869  if( *line != 0 )
2870  circle->SetFillMode( parseFillMode( aReader, line, &line ) );
2871 
2872  return circle.release();
2873 }
2874 
2875 
2876 LIB_TEXT* SCH_LEGACY_PLUGIN_CACHE::loadText( std::unique_ptr< LIB_PART >& aPart,
2877  FILE_LINE_READER& aReader )
2878 {
2879  const char* line = aReader.Line();
2880 
2881  wxCHECK_MSG( strCompare( "T", line, &line ), NULL, "Invalid LIB_TEXT definition" );
2882 
2883  std::unique_ptr< LIB_TEXT > text( new LIB_TEXT( aPart.get() ) );
2884 
2885  text->SetTextAngle( (double) parseInt( aReader, line, &line ) );
2886 
2887  wxPoint center;
2888 
2889  center.x = parseInt( aReader, line, &line );
2890  center.y = parseInt( aReader, line, &line );
2891  text->SetPosition( center );
2892 
2893  wxSize size;
2894 
2895  size.x = size.y = parseInt( aReader, line, &line );
2896  text->SetTextSize( size );
2897  text->SetVisible( !parseInt( aReader, line, &line ) );
2898  text->SetUnit( parseInt( aReader, line, &line ) );
2899  text->SetConvert( parseInt( aReader, line, &line ) );
2900 
2901  wxString str;
2902 
2903  // If quoted string loading fails, load as not quoted string.
2904  if( *line == '"' )
2905  parseQuotedString( str, aReader, line, &line );
2906  else
2907  parseUnquotedString( str, aReader, line, &line );
2908 
2909  if( !str.IsEmpty() )
2910  {
2911  // convert two apostrophes back to double quote
2912  str.Replace( "''", "\"" );
2913  str.Replace( wxT( "~" ), wxT( " " ) );
2914  }
2915 
2916  text->SetText( str );
2917 
2918  // Here things are murky and not well defined. At some point it appears the format
2919  // was changed to add text properties. However rather than add the token to the end of
2920  // the text definition, it was added after the string and no mention if the file
2921  // verion was bumped or not so this code make break on very old component libraries.
2922  //
2923  // Update: apparently even in the latest version this can be different so added a test
2924  // for end of line before checking for the text properties.
2925  if( LIB_VERSION( m_versionMajor, m_versionMinor ) > LIB_VERSION( 2, 0 ) && !is_eol( *line ) )
2926  {
2927  if( strCompare( "Italic", line, &line ) )
2928  text->SetItalic( true );
2929  else if( !strCompare( "Normal", line, &line ) )
2930  SCH_PARSE_ERROR( "invalid text stype, expected 'Normal' or 'Italic'",
2931  aReader, line );
2932 
2933  if( parseInt( aReader, line, &line ) > 0 )
2934  text->SetBold( true );
2935 
2936  // Some old libaries version > 2.0 do not have these options for text justification:
2937  if( !is_eol( *line ) )
2938  {
2939  switch( parseChar( aReader, line, &line ) )
2940  {
2941  case 'L':
2942  text->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
2943  break;
2944 
2945  case 'C':
2946  text->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
2947  break;
2948 
2949  case 'R':
2950  text->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
2951  break;
2952 
2953  default:
2954  SCH_PARSE_ERROR( "invalid horizontal text justication parameter, expected L, C, or R",
2955  aReader, line );
2956  }
2957 
2958  switch( parseChar( aReader, line, &line ) )
2959  {
2960  case 'T':
2961  text->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2962  break;
2963 
2964  case 'C':
2965  text->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
2966  break;
2967 
2968  case 'B':
2969  text->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2970  break;
2971 
2972  default:
2973  SCH_PARSE_ERROR( "invalid vertical text justication parameter, expected T, C, or B",
2974  aReader, line );
2975  }
2976  }
2977  }
2978 
2979  return text.release();
2980 }
2981 
2982 
2983 LIB_RECTANGLE* SCH_LEGACY_PLUGIN_CACHE::loadRectangle( std::unique_ptr< LIB_PART >& aPart,
2984  FILE_LINE_READER& aReader )
2985 {
2986  const char* line = aReader.Line();
2987 
2988  wxCHECK_MSG( strCompare( "S", line, &line ), NULL, "Invalid LIB_RECTANGLE definition" );
2989 
2990  std::unique_ptr< LIB_RECTANGLE > rectangle( new LIB_RECTANGLE( aPart.get() ) );
2991 
2992  wxPoint pos;
2993 
2994  pos.x = parseInt( aReader, line, &line );
2995  pos.y = parseInt( aReader, line, &line );
2996  rectangle->SetPosition( pos );
2997 
2998  wxPoint end;
2999 
3000  end.x = parseInt( aReader, line, &line );
3001  end.y = parseInt( aReader, line, &line );
3002  rectangle->SetEnd( end );
3003 
3004  rectangle->SetUnit( parseInt( aReader, line, &line ) );
3005  rectangle->SetConvert( parseInt( aReader, line, &line ) );
3006  rectangle->SetWidth( parseInt( aReader, line, &line ) );
3007 
3008  if( *line != 0 )
3009  rectangle->SetFillMode( parseFillMode( aReader, line, &line ) );
3010 
3011  return rectangle.release();
3012 }
3013 
3014 
3015 LIB_PIN* SCH_LEGACY_PLUGIN_CACHE::loadPin( std::unique_ptr< LIB_PART >& aPart,
3016  FILE_LINE_READER& aReader )
3017 {
3018  const char* line = aReader.Line();
3019 
3020  wxCHECK_MSG( strCompare( "X", line, &line ), NULL, "Invalid LIB_PIN definition" );
3021 
3022  std::unique_ptr< LIB_PIN > pin( new LIB_PIN( aPart.get() ) );
3023 
3024  wxString name, number;
3025 
3026  parseUnquotedString( name, aReader, line, &line );
3027  parseUnquotedString( number, aReader, line, &line );
3028 
3029  pin->SetName( name );
3030  pin->SetNumber( number );
3031 
3032  wxPoint pos;
3033 
3034  pos.x = parseInt( aReader, line, &line );
3035  pos.y = parseInt( aReader, line, &line );
3036  pin->SetPosition( pos );
3037  pin->SetLength( parseInt( aReader, line, &line ) );
3038  pin->SetOrientation( parseChar( aReader, line, &line ) );
3039  pin->SetNumberTextSize( parseInt( aReader, line, &line ) );
3040  pin->SetNameTextSize( parseInt( aReader, line, &line ) );
3041  pin->SetUnit( parseInt( aReader, line, &line ) );
3042  pin->SetConvert( parseInt( aReader, line, &line ) );
3043 
3044  char type = parseChar( aReader, line, &line );
3045 
3046  wxString attributes;
3047 
3048  // Optional
3049  parseUnquotedString( attributes, aReader, line, &line, true );
3050 
3051  switch( type )
3052  {
3053  case 'I':
3054  pin->SetType( PIN_INPUT );
3055  break;
3056 
3057  case 'O':
3058  pin->SetType( PIN_OUTPUT );
3059  break;
3060 
3061  case 'B':
3062  pin->SetType( PIN_BIDI );
3063  break;
3064 
3065  case 'T':
3066  pin->SetType( PIN_TRISTATE );
3067  break;
3068 
3069  case 'P':
3070  pin->SetType( PIN_PASSIVE );
3071  break;
3072 
3073  case 'U':
3074  pin->SetType( PIN_UNSPECIFIED );
3075  break;
3076 
3077  case 'W':
3078  pin->SetType( PIN_POWER_IN );
3079  break;
3080 
3081  case 'w':
3082  pin->SetType( PIN_POWER_OUT );
3083  break;
3084 
3085  case 'C':
3086  pin->SetType( PIN_OPENCOLLECTOR );
3087  break;
3088 
3089  case 'E':
3090  pin->SetType( PIN_OPENEMITTER );
3091  break;
3092 
3093  case 'N':
3094  pin->SetType( PIN_NC );
3095  break;
3096 
3097  default:
3098  SCH_PARSE_ERROR( "unknown pin type", aReader, line );
3099  }
3100 
3101  if( !attributes.IsEmpty() ) /* Special Symbol defined */
3102  {
3103  enum
3104  {
3105  INVERTED = 1 << 0,
3106  CLOCK = 1 << 1,
3107  LOWLEVEL_IN = 1 << 2,
3108  LOWLEVEL_OUT = 1 << 3,
3109  FALLING_EDGE = 1 << 4,
3110  NONLOGIC = 1 << 5
3111  };
3112 
3113  int flags = 0;
3114 
3115  for( int j = attributes.size(); j > 0; )
3116  {
3117  switch( attributes[--j].GetValue() )
3118  {
3119  case '~':
3120  break;
3121 
3122  case 'N':
3123  pin->SetVisible( false );
3124  break;
3125 
3126  case 'I':
3127  flags |= INVERTED;
3128  break;
3129 
3130  case 'C':
3131  flags |= CLOCK;
3132  break;
3133 
3134  case 'L':
3135  flags |= LOWLEVEL_IN;
3136  break;
3137 
3138  case 'V':
3139  flags |= LOWLEVEL_OUT;
3140  break;
3141 
3142  case 'F':
3143  flags |= FALLING_EDGE;
3144  break;
3145 
3146  case 'X':
3147  flags |= NONLOGIC;
3148  break;
3149 
3150  default:
3151  SCH_PARSE_ERROR( "unknown pin attribute", aReader, line );
3152  }
3153  }
3154 
3155  switch( flags )
3156  {
3157  case 0:
3158  pin->SetShape( PINSHAPE_LINE );
3159  break;
3160 
3161  case INVERTED:
3162  pin->SetShape( PINSHAPE_INVERTED );
3163  break;
3164 
3165  case CLOCK:
3166  pin->SetShape( PINSHAPE_CLOCK );
3167  break;
3168 
3169  case INVERTED | CLOCK:
3170  pin->SetShape( PINSHAPE_INVERTED_CLOCK );
3171  break;
3172 
3173  case LOWLEVEL_IN:
3174  pin->SetShape( PINSHAPE_INPUT_LOW );
3175  break;
3176 
3177  case LOWLEVEL_IN | CLOCK:
3178  pin->SetShape( PINSHAPE_CLOCK_LOW );
3179  break;
3180 
3181  case LOWLEVEL_OUT:
3182  pin->SetShape( PINSHAPE_OUTPUT_LOW );
3183  break;
3184 
3185  case FALLING_EDGE:
3186  pin->SetShape( PINSHAPE_FALLING_EDGE_CLOCK );
3187  break;
3188 
3189  case NONLOGIC:
3190  pin->SetShape( PINSHAPE_NONLOGIC );
3191  break;
3192 
3193  default:
3194  SCH_PARSE_ERROR( "pin attributes do not define a valid pin shape", aReader, line );
3195  }
3196  }
3197 
3198  return pin.release();
3199 }
3200 
3201 
3202 LIB_POLYLINE* SCH_LEGACY_PLUGIN_CACHE::loadPolyLine( std::unique_ptr< LIB_PART >& aPart,
3203  FILE_LINE_READER& aReader )
3204 {
3205  const char* line = aReader.Line();
3206 
3207  wxCHECK_MSG( strCompare( "P", line, &line ), NULL, "Invalid LIB_POLYLINE definition" );
3208 
3209  std::unique_ptr< LIB_POLYLINE > polyLine( new LIB_POLYLINE( aPart.get() ) );
3210 
3211  int points = parseInt( aReader, line, &line );
3212  polyLine->SetUnit( parseInt( aReader, line, &line ) );
3213  polyLine->SetConvert( parseInt( aReader, line, &line ) );
3214  polyLine->SetWidth( parseInt( aReader, line, &line ) );
3215 
3216  wxPoint pt;
3217 
3218  for( int i = 0; i < points; i++ )
3219  {
3220  pt.x = parseInt( aReader, line, &line );
3221  pt.y = parseInt( aReader, line, &line );
3222  polyLine->AddPoint( pt );
3223  }
3224 
3225  if( *line != 0 )
3226  polyLine->SetFillMode( parseFillMode( aReader, line, &line ) );
3227 
3228  return polyLine.release();
3229 }
3230 
3231 
3232 LIB_BEZIER* SCH_LEGACY_PLUGIN_CACHE::loadBezier( std::unique_ptr< LIB_PART >& aPart,
3233  FILE_LINE_READER& aReader )
3234 {
3235  const char* line = aReader.Line();
3236 
3237  wxCHECK_MSG( strCompare( "B", line, &line ), NULL, "Invalid LIB_BEZIER definition" );
3238 
3239  std::unique_ptr< LIB_BEZIER > bezier( new LIB_BEZIER( aPart.get() ) );
3240 
3241  int points = parseInt( aReader, line, &line );
3242  bezier->SetUnit( parseInt( aReader, line, &line ) );
3243  bezier->SetConvert( parseInt( aReader, line, &line ) );
3244  bezier->SetWidth( parseInt( aReader, line, &line ) );
3245 
3246  wxPoint pt;
3247 
3248  for( int i = 0; i < points; i++ )
3249  {
3250  pt.x = parseInt( aReader, line, &line );
3251  pt.y = parseInt( aReader, line, &line );
3252  bezier->AddPoint( pt );
3253  }
3254 
3255  if( *line != 0 )
3256  bezier->SetFillMode( parseFillMode( aReader, line, &line ) );
3257 
3258  return bezier.release();
3259 }
3260 
3261 
3262 void SCH_LEGACY_PLUGIN_CACHE::loadFootprintFilters( std::unique_ptr< LIB_PART >& aPart,
3263  FILE_LINE_READER& aReader )
3264 {
3265  const char* line = aReader.Line();
3266 
3267  wxCHECK_RET( strCompare( "$FPLIST", line, &line ), "Invalid footprint filter list" );
3268 
3269  line = aReader.ReadLine();
3270 
3271  while( line )
3272  {
3273  if( strCompare( "$ENDFPLIST", line, &line ) )
3274  return;
3275 
3276  wxString footprint;
3277 
3278  parseUnquotedString( footprint, aReader, line, &line );
3279  aPart->GetFootPrints().Add( footprint );
3280  line = aReader.ReadLine();
3281  }
3282 
3283  SCH_PARSE_ERROR( "file ended prematurely while loading footprint filters", aReader, line );
3284 }
3285 
3286 
3287 void SCH_LEGACY_PLUGIN_CACHE::Save( bool aSaveDocFile )
3288 {
3289  if( !m_isModified )
3290  return;
3291 
3292  std::unique_ptr< FILE_OUTPUTFORMATTER > formatter( new FILE_OUTPUTFORMATTER( m_libFileName.GetFullPath() ) );
3293  formatter->Print( 0, "%s %d.%d\n", LIBFILE_IDENT, LIB_VERSION_MAJOR, LIB_VERSION_MINOR );
3294  formatter->Print( 0, "#encoding utf-8\n");
3295 
3296  for( LIB_ALIAS_MAP::iterator it = m_aliases.begin(); it != m_aliases.end(); it++ )
3297  {
3298  if( !it->second->IsRoot() )
3299  continue;
3300 
3301  it->second->GetPart()->Save( *formatter.get() );
3302  }
3303 
3304  formatter->Print( 0, "#\n#End Library\n" );
3305  formatter.reset();
3306 
3307  m_fileModTime = m_libFileName.GetModificationTime();
3308  m_isModified = false;
3309 
3310  if( aSaveDocFile )
3311  saveDocFile();
3312 }
3313 
3314 
3316 {
3317  wxFileName docFileName = m_libFileName;
3318 
3319  docFileName.SetExt( DOC_EXT );
3320  FILE_OUTPUTFORMATTER formatter( docFileName.GetFullPath() );
3321 
3322  formatter.Print( 0, "%s\n", DOCFILE_IDENT );
3323 
3324  for( LIB_ALIAS_MAP::iterator it = m_aliases.begin(); it != m_aliases.end(); it++ )
3325  {
3326  it->second->SaveDoc( formatter );
3327  }
3328 
3329  formatter.Print( 0, "#\n#End Doc Library\n" );
3330 }
3331 
3332 
3333 void SCH_LEGACY_PLUGIN_CACHE::DeleteAlias( const wxString& aAliasName )
3334 {
3335  LIB_ALIAS_MAP::iterator it = m_aliases.find( aAliasName );
3336 
3337  if( it == m_aliases.end() )
3338  THROW_IO_ERROR( wxString::Format( _( "library %s does not contain an alias %s" ),
3339  m_libFileName.GetFullName(), aAliasName ) );
3340 
3341  LIB_ALIAS* alias = it->second;
3342  LIB_PART* part = alias->GetPart();
3343 
3344  alias = part->RemoveAlias( alias );
3345 
3346  if( !alias )
3347  {
3348  delete part;
3349 
3350  if( m_aliases.size() > 1 )
3351  {
3352  LIB_ALIAS_MAP::iterator next = it;
3353  next++;
3354 
3355  if( next == m_aliases.end() )
3356  next = m_aliases.begin();
3357 
3358  alias = next->second;
3359  }
3360  }
3361 
3362  m_aliases.erase( it );
3363  ++m_modHash;
3364  m_isModified = true;
3365 }
3366 
3367 
3368 void SCH_LEGACY_PLUGIN_CACHE::DeleteSymbol( const wxString& aAliasName )
3369 {
3370  LIB_ALIAS_MAP::iterator it = m_aliases.find( aAliasName );
3371 
3372  if( it == m_aliases.end() )
3373  THROW_IO_ERROR( wxString::Format( _( "library %s does not contain an alias %s" ),
3374  m_libFileName.GetFullName(), aAliasName ) );
3375 
3376  LIB_ALIAS* alias = it->second;
3377  LIB_PART* part = alias->GetPart();
3378 
3379  wxArrayString aliasNames = part->GetAliasNames();
3380 
3381  // Deleting all of the aliases deletes the symbol from the library.
3382  for( size_t i = 0; i < aliasNames.Count(); i++ )
3383  DeleteAlias( aliasNames[i] );
3384 }
3385 
3386 
3387 void SCH_LEGACY_PLUGIN::cacheLib( const wxString& aLibraryFileName )
3388 {
3389  if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() )
3390  {
3391  // a spectacular episode in memory management:
3392  delete m_cache;
3393  m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryFileName );
3394 
3395  // Because m_cache is rebuilt, increment PART_LIBS::s_modify_generation
3396  // to modify the hash value that indicate component to symbol links
3397  // must be updated.
3399 
3400  if( !isBuffering( m_props ) )
3401  m_cache->Load();
3402  }
3403 }
3404 
3405 
3407 {
3408  std::string propName( SCH_LEGACY_PLUGIN::PropNoDocFile );
3409 
3410  if( aProperties && aProperties->find( propName ) != aProperties->end() )
3411  return false;
3412 
3413  return true;
3414 }
3415 
3416 
3417 bool SCH_LEGACY_PLUGIN::isBuffering( const PROPERTIES* aProperties )
3418 {
3419  return ( aProperties && aProperties->Exists( SCH_LEGACY_PLUGIN::PropBuffering ) );
3420 }
3421 
3422 
3424 {
3425  if( m_cache )
3426  return m_cache->GetModifyHash();
3427 
3428  // If the cache hasn't been loaded, it hasn't been modified.
3429  return 0;
3430 }
3431 
3432 
3433 size_t SCH_LEGACY_PLUGIN::GetSymbolLibCount( const wxString& aLibraryPath,
3434  const PROPERTIES* aProperties )
3435 {
3436  LOCALE_IO toggle;
3437 
3438  m_props = aProperties;
3439 
3440  cacheLib( aLibraryPath );
3441 
3442  return m_cache->m_aliases.size();
3443 }
3444 
3445 
3446 void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( wxArrayString& aAliasNameList,
3447  const wxString& aLibraryPath,
3448  const PROPERTIES* aProperties )
3449 {
3450  LOCALE_IO toggle; // toggles on, then off, the C locale.
3451 
3452  m_props = aProperties;
3453 
3454  cacheLib( aLibraryPath );
3455 
3456  const LIB_ALIAS_MAP& aliases = m_cache->m_aliases;
3457 
3458  for( LIB_ALIAS_MAP::const_iterator it = aliases.begin(); it != aliases.end(); ++it )
3459  aAliasNameList.Add( it->first );
3460 }
3461 
3462 
3463 void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( std::vector<LIB_ALIAS*>& aAliasList,
3464  const wxString& aLibraryPath,
3465  const PROPERTIES* aProperties )
3466 {
3467  LOCALE_IO toggle; // toggles on, then off, the C locale.
3468 
3469  m_props = aProperties;
3470 
3471  cacheLib( aLibraryPath );
3472 
3473  const LIB_ALIAS_MAP& aliases = m_cache->m_aliases;
3474 
3475  for( LIB_ALIAS_MAP::const_iterator it = aliases.begin(); it != aliases.end(); ++it )
3476  aAliasList.push_back( it->second );
3477 }
3478 
3479 
3480 LIB_ALIAS* SCH_LEGACY_PLUGIN::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
3481  const PROPERTIES* aProperties )
3482 {
3483  LOCALE_IO toggle; // toggles on, then off, the C locale.
3484 
3485  m_props = aProperties;
3486 
3487  cacheLib( aLibraryPath );
3488 
3489  LIB_ALIAS_MAP::const_iterator it = m_cache->m_aliases.find( aAliasName );
3490 
3491  if( it == m_cache->m_aliases.end() )
3492  return NULL;
3493 
3494  return it->second;
3495 }
3496 
3497 
3498 void SCH_LEGACY_PLUGIN::SaveSymbol( const wxString& aLibraryPath, const LIB_PART* aSymbol,
3499  const PROPERTIES* aProperties )
3500 {
3501  m_props = aProperties;
3502 
3503  cacheLib( aLibraryPath );
3504 
3505  m_cache->AddSymbol( aSymbol );
3506 
3507  if( !isBuffering( aProperties ) )
3508  m_cache->Save( writeDocFile( aProperties ) );
3509 }
3510 
3511 
3512 void SCH_LEGACY_PLUGIN::DeleteAlias( const wxString& aLibraryPath, const wxString& aAliasName,
3513  const PROPERTIES* aProperties )
3514 {
3515  m_props = aProperties;
3516 
3517  cacheLib( aLibraryPath );
3518 
3519  m_cache->DeleteAlias( aAliasName );
3520 
3521  if( !isBuffering( aProperties ) )
3522  m_cache->Save( writeDocFile( aProperties ) );
3523 }
3524 
3525 
3526 void SCH_LEGACY_PLUGIN::DeleteSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
3527  const PROPERTIES* aProperties )
3528 {
3529  m_props = aProperties;
3530 
3531  cacheLib( aLibraryPath );
3532 
3533  m_cache->DeleteSymbol( aAliasName );
3534 
3535  if( !isBuffering( aProperties ) )
3536  m_cache->Save( writeDocFile( aProperties ) );
3537 }
3538 
3539 
3540 void SCH_LEGACY_PLUGIN::CreateSymbolLib( const wxString& aLibraryPath,
3541  const PROPERTIES* aProperties )
3542 {
3543  if( wxFileExists( aLibraryPath ) )
3544  {
3546  _( "symbol library '%s' already exists, cannot create a new library" ),
3547  aLibraryPath.GetData() ) );
3548  }
3549 
3550  LOCALE_IO toggle;
3551 
3552  m_props = aProperties;
3553 
3554  delete m_cache;
3555  m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
3556  m_cache->SetModified();
3557  m_cache->Save( writeDocFile( aProperties ) );
3558  m_cache->Load(); // update m_writable and m_mod_time
3559 }
3560 
3561 
3562 bool SCH_LEGACY_PLUGIN::DeleteSymbolLib( const wxString& aLibraryPath,
3563  const PROPERTIES* aProperties )
3564 {
3565  wxFileName fn = aLibraryPath;
3566 
3567  if( !fn.FileExists() )
3568  return false;
3569 
3570  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3571  // we don't want that. we want bare metal portability with no UI here.
3572  if( wxRemove( aLibraryPath ) )
3573  {
3574  THROW_IO_ERROR( wxString::Format( _( "library '%s' cannot be deleted" ),
3575  aLibraryPath.GetData() ) );
3576  }
3577 
3578  if( m_cache && m_cache->IsFile( aLibraryPath ) )
3579  {
3580  delete m_cache;
3581  m_cache = 0;
3582  }
3583 
3584  return true;
3585 }
3586 
3587 
3588 void SCH_LEGACY_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const PROPERTIES* aProperties )
3589 {
3590  if( !m_cache )
3591  m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
3592 
3593  wxString oldFileName = m_cache->GetFileName();
3594 
3595  if( !m_cache->IsFile( aLibraryPath ) )
3596  {
3597  m_cache->SetFileName( aLibraryPath );
3598  }
3599 
3600  // This is a forced save.
3601  m_cache->SetModified();
3602  m_cache->Save( writeDocFile( aProperties ) );
3603  m_cache->SetFileName( oldFileName );
3604 }
3605 
3606 
3607 const char* SCH_LEGACY_PLUGIN::PropBuffering = "buffering";
3608 const char* SCH_LEGACY_PLUGIN::PropNoDocFile = "no_doc_file";
void SetComment1(const wxString &aComment)
Definition of the SCH_SHEET class for Eeschema.
Class SCH_BUS_WIRE_ENTRY.
CITER next(CITER it)
Definition: ptree.cpp:130
FILE_OUTPUTFORMATTER * m_out
The output formatter for saving SCH_SCREEN objects.
PINSHEETLABEL_SHAPE GetShape() const
Definition: sch_text.h:121
#define TEXT_ANGLE_HORIZ
Frequent text rotations, used with {Set,Get}TextAngle(), in 0.1 degrees for now, hoping to migrate to...
Definition: common.h:91
Class SCH_FIELD instances are attached to a component and provide a place for the component's value...
Definition: sch_field.h:56
SCH_BITMAP * loadBitmap(FILE_LINE_READER &aReader)
LIB_PIN * loadPin(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
Part library alias object definition.
char * ReadLine() override
Function ReadLine reads a line of text into the buffer and increments the line number counter...
Definition: richio.cpp:196
wxString GetName(bool aUseDefaultName=true) const
Function GetName returns the field name.
Definition: sch_field.cpp:473
bool SearchHierarchy(const wxString &aFilename, SCH_SCREEN **aScreen)
Function SearchHierarchy search the existing hierarchy for an instance of screen "FileName".
Definition: sch_sheet.cpp:744
wxString m_Text
Definition: eda_text.h:346
TRANSFORM & GetTransform() const
SCH_SHEET_PINS & GetPins()
Definition: sch_sheet.h:348
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes...
Definition: macros.h:53
int y2
Definition: transform.h:51
bool IsItalic() const
Definition: eda_text.h:170
Class LOCALE_IO is a class that can be instantiated within a scope in which you are expecting excepti...
Definition: common.h:166
const wxString & GetCompany() const
wxString m_error
For throwing exceptions.
static int s_modify_generation
helper for GetModifyHash()
static int parseInt(FILE_LINE_READER &aReader, const char *aLine, const char **aOutput=NULL)
Function parseInt.
LIB_BEZIER * loadBezier(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
#define LIBFILE_IDENT
Definition: class_library.h:59
wxPoint GetPosition() const override
Function GetPosition.
void SetRevision(const wxString &aRevision)
Class LIB_TEXT defines a component library graphical text item.
Definition: lib_text.h:45
void saveLine(SCH_LINE *aLine)
int GetId() const
Definition: sch_field.h:87
SCH_LEGACY_PLUGIN_CACHE * m_cache
LIB_CIRCLE * loadCircle(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
static char parseChar(FILE_LINE_READER &aReader, const char *aCurrentToken, const char **aNextToken=NULL)
Function parseChar.
The first 4 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors...
Class LIB_FIELD is used in symbol libraries.
Definition: lib_field.h:60
int GetTextWidth() const
Definition: eda_text.h:218
void SetScreen(SCH_SCREEN *aScreen)
Function SetScreen sets the screen associated with this sheet to aScreen.
Definition: sch_sheet.cpp:103
const wxString & GetFileName() const
wxString GetPrefix() const
void SaveLibrary(const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
wxImage * GetImageData()
size_t GetSymbolLibCount(const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
int GetLabelSpinStyle() const
Definition: sch_text.h:119
void SetVisible(bool aVisible)
Definition: eda_text.h:175
time_t GetNewTimeStamp()
Definition: common.cpp:166
int x2
Definition: transform.h:50
wxPoint GetLibPosition() const
Definition: sch_field.h:189
SCH_SHEET * m_rootSheet
The root sheet of the schematic being loaded..
SCH_SHEET * GetRootSheet()
Function GetRootSheet.
Definition: sch_sheet.cpp:135
void Format(SCH_SCREEN *aScreen)
void saveText(SCH_TEXT *aText)
#define LIB_VERSION_MINOR
Definition: class_library.h:56
const wxString & GetComment4() const
void SetDate(const wxString &aDate)
Function SetDate sets the date field, and defaults to the current time and date.
SCH_LEGACY_PLUGIN_CACHE(const wxString &aLibraryPath)
bool Exists(const std::string &aProperty) const
Definition: properties.h:44
void SetPageSettings(const PAGE_INFO &aPageSettings)
#define LIB_VERSION(major, minor)
Definition: class_library.h:61
void SaveSymbol(const wxString &aLibraryPath, const LIB_PART *aSymbol, const PROPERTIES *aProperties=NULL) override
Write aSymbol to an existing library located at aLibraryPath.
wxPoint GetEndPoint() const
Definition: sch_line.h:75
const wxString & GetDate() const
int x1
Definition: transform.h:48
static const wxString GetDefaultFieldName(int aFieldNdx)
Function GetDefaultFieldName returns a default symbol field name for field aFieldNdx for all componen...
SCH_LAYER_ID GetLayer() const
Function GetLayer returns the layer this item is on.
#define MAX_UNIT_COUNT_PER_PACKAGE
The maximum number of units per package.
Definition: eeschema_id.h:48
static const wxChar Custom[]
"User" defined page type
wxPoint m_End() const
void DeleteSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const PROPERTIES *aProperties=NULL) override
Delete the entire LIB_PART associated with aAliasName from the library aLibraryPath.
void Save(const wxString &aFileName, SCH_SCREEN *aScreen, KIWAY *aKiway, const PROPERTIES *aProperties=NULL) override
Write aSchematic to a storage file in a format that this SCH_PLUGIN implementation knows about...
#define SCH_LAYER_ID_COUNT
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
void saveNoConnect(SCH_NO_CONNECT *aNoConnect)
const char * SheetLabelType[]
Definition: sch_text.cpp:57
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:222
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_bitmap.h:135
Class LIB_ID.
Definition: lib_id.h:56
size_t GetAliasCount() const
Class PROPERTIES is a name/value tuple with unique names and optional values.
Definition: properties.h:34
void loadHeader(FILE_LINE_READER &aReader)
static dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3)
Definition: trace.cpp:259
SCH_SCREEN * GetScreen()
Definition: sch_sheet.h:286
Class TITLE_BLOCK holds the information shown in the lower right corner of a plot, printout, or editing view.
const wxChar traceSchLegacyPlugin[]
Flag to enable legacy schematic plugin debug output.
void SetDocFileName(const wxString &aDocFileName)
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:190
SCH_ITEM * Next() const
#define TEXT_ANGLE_VERT
Definition: common.h:92
Field Reference of part, i.e. "IC21".
void saveSheet(SCH_SHEET *aSheet)
LIB_ARC * loadArc(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
bool IsBold() const
Definition: eda_text.h:173
double GetTextAngle() const
Definition: eda_text.h:164
void SetComment4(const wxString &aComment)
void loadAliases(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
void loadPageSettings(FILE_LINE_READER &aReader, SCH_SCREEN *aScreen)
int GetThickness() const
Function GetThickness returns pen width.
Definition: eda_text.h:154
Class SCH_LEGACY_PLUGIN_CACHE is a cache assistant for the part library portion of the SCH_PLUGIN API...
std::map< wxString, LIB_ALIAS *, AliasMapSort > LIB_ALIAS_MAP
Alias map used by part library object.
void saveField(SCH_FIELD *aField)
wxPoint GetStartPoint() const
Definition: sch_line.h:71
Class SCH_BUS_ENTRY_BASE.
Definition: sch_bus_entry.h:43
void saveJunction(SCH_JUNCTION *aJunction)
#define LIB_VERSION_MAJOR
Definition: class_library.h:55
void CreateSymbolLib(const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
Create a new empty symbol library at aLibraryPath.
int m_version
Version of file being loaded.
void loadFile(const wxString &aFileName, SCH_SCREEN *aScreen)
#define SCHEMATIC_HEAD_STRING
Definition: general.h:41
static double parseDouble(FILE_LINE_READER &aReader, const char *aLine, const char **aOutput=NULL)
Function parseDouble.
wxString m_path
Root project path for loading child sheets.
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
int GetConvert() const
int y1
Definition: transform.h:49
int GetSheetNameSize() const
Definition: sch_sheet.h:278
const wxString & GetTitle() const
std::string EscapedUTF8(const wxString &aString)
Function EscapedUTF8 returns an 8 bit UTF8 string given aString in unicode form.
Definition: string.cpp:137
#define EESCHEMA_VERSION
Definition: general.h:40
SCH_SHEET * loadSheet(FILE_LINE_READER &aReader)
bool IsPortrait() const
Class FILE_LINE_READER is a LINE_READER that reads from an open file.
Definition: richio.h:180
Class for tranforming drawing coordinates for a wxDC device context.
Definition: transform.h:45
#define DOCFILE_IDENT
bool isBuffering(const PROPERTIES *aProperties)
SCH_FIELD * GetField(int aFieldNdx) const
Returns a field in this symbol.
BITMAP_BASE * GetImage()
Definition: sch_bitmap.h:61
void DeleteAlias(const wxString &aAliasName)
void loadHierarchy(SCH_SHEET *aSheet)
void SetText(const wxString &aText) override
Sets the field text to aText.
Definition: lib_field.cpp:679
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_junction.h:92
void SetParent(EDA_ITEM *aParent)
Definition: base_struct.h:227
int GetId() const
Definition: lib_field.h:136
static void parseUnquotedString(wxString &aString, FILE_LINE_READER &aReader, const char *aCurrentToken, const char **aNextToken=NULL, bool aCanBeEmpty=false)
Function parseUnquotedString.
Definitions for the Eeschema program SCH_SCREEN class.
const wxString & GetName() const
const UTF8 & GetLibItemName() const
Function GetLibItemName.
Definition: lib_id.h:129
const wxString & GetRevision() const
Class PAGE_INFO describes the page size and margins of a paper page on which to eventually print or p...
char * Line() const
Function Line returns a pointer to the last line that was read in.
Definition: richio.h:139
Class LIB_ITEM definition.
const wxString & GetText() const
Function GetText returns the string associated with the text object.
Definition: eda_text.h:130
LIB_TEXT * loadText(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
VTBL_ENTRY PROJECT & Prj() const
Function Prj returns the PROJECT associated with this KIWAY.
Definition: kiway.cpp:144
SCH_TEXT * loadText(FILE_LINE_READER &aReader)
const wxArrayString & GetPathsAndReferences() const
bool DeleteSymbolLib(const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
Delete an existing symbol library and returns true if successful, or if library does not exist return...
static bool is_eol(char c)
LIB_POLYLINE * loadPolyLine(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
std::string toUTFTildaText(const wxString &txt)
Function toUTFTildaText convert a wxString to UTF8 and replace any control characters with a ~...
void Empty()
Definition: eda_text.h:231
Class LIB_PART defines a library part object.
void SetCompany(const wxString &aCompany)
#define DOC_EXT
Definition: class_library.h:50
static unsigned long parseHex(FILE_LINE_READER &aReader, const char *aLine, const char **aOutput=NULL)
Function parseHex.
Class KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within...
Definition: kiway.h:257
void init(KIWAY *aKiway, const PROPERTIES *aProperties=NULL)
initialize PLUGIN like a constructor would.
int GetFileNameSize() const
Definition: sch_sheet.h:282
void saveBitmap(SCH_BITMAP *aBitmap)
const wxString & GetComment3() const
bool SetType(const wxString &aStandardPageDescriptionName, bool IsPortrait=false)
Function SetType sets the name of the page type and also the sizes and margins commonly associated wi...
SCH_LAYER_ID
Eeschema drawing layers.
static const char * PropNoDocFile
const char* PropBuffering
SCH_JUNCTION * loadJunction(FILE_LINE_READER &aReader)
Class SCH_SHEET_PIN defines a sheet pin (label) used in sheets to create hierarchical schematics...
Definition: sch_sheet.h:62
#define USE_OLD_DOC_FILE_FORMAT(major, minor)
Definition: class_library.h:75
void loadField(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
wxString GetFileName() const
int GetUnit() const
void SetModified(bool aModified=true)
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_sheet.h:582
void SetTitle(const wxString &aTitle)
FILL_T parseFillMode(FILE_LINE_READER &aReader, const char *aLine, const char **aOutput)
void loadDrawEntries(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
int GetWidthMils() const
LIB_ALIAS * removeAlias(LIB_ALIAS *aAlias)
void SetPortrait(bool isPortrait)
Function SetPortrait will rotate the paper page 90 degrees.
wxString GetFileName(void) const
Function GetFileName return the filename corresponding to this sheet.
Definition: sch_sheet.cpp:880
void SetDescription(const wxString &aDescription)
double GetScale() const
void SetHeightMils(int aHeightInMils)
void saveComponent(SCH_COMPONENT *aComponent)
const TITLE_BLOCK & GetTitleBlock() const
const wxString & GetComment2() const
const char * delims
time_t GetTimeStamp() const
Definition: base_struct.h:218
Class SCH_SHEET is the sheet symbol placed in a schematic, and is the entry point for a sub schematic...
Definition: sch_sheet.h:216
virtual wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_text.h:200
LIB_PART * GetPart() const
Function GetPart gets the shared LIB_PART.
void SetFileName(const wxString &aFileName)
bool checkForDuplicates(wxString &aAliasName)
Field Value of part, i.e. "3.3K".
EDA_TEXT_VJUSTIFY_T GetVertJustify() const
Definition: eda_text.h:191
SCH_ITEM * GetDrawItems() const
Function GetDrawItems().
Definition the SCH_COMPONENT class for Eeschema.
bool IsCustom() const
Function IsCustom returns true if the type is Custom.
int GetPenSizeForBold(int aTextSize)
Function GetPensizeForBold.
Definition: drawtxt.cpp:49
void SetComment2(const wxString &aComment)
const wxString & GetComment1() const
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
#define SCH_PARSE_ERROR(text, reader, pos)
void RemoveAlias(const wxString &aName)
const wxString & GetName() const
Class SCH_LINE is a segment description base class to describe items which have 2 end points (track...
Definition: sch_line.h:42
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static void parseQuotedString(wxString &aString, FILE_LINE_READER &aReader, const char *aCurrentToken, const char **aNextToken=NULL, bool aCanBeEmpty=false)
Function parseQuotedString.
int GetFieldCount() const
Return the number of fields in this symbol.
void Append(SCH_ITEM *aItem)
void loadHeader(FILE_LINE_READER &aReader, SCH_SCREEN *aScreen)
bool IsVisible() const
Definition: eda_text.h:176
void SetKeyWords(const wxString &aKeyWords)
void EnumerateSymbolLib(wxArrayString &aAliasNameList, const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
Populate a list of LIB_PART alias names contained within the library aLibraryPath.
wxArrayString GetAliasNames(bool aIncludeRoot=true) const
LIB_RECTANGLE * loadRectangle(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
void SetWidthMils(int aWidthInMils)
SCH_BUS_ENTRY_BASE * loadBusEntry(FILE_LINE_READER &aReader)
Class SCH_COMPONENT describes a real schematic component.
Definition: sch_component.h:69
wxPoint GetPosition() const override
Function GetPosition.
const LIB_ID & GetLibId() const
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:165
static const char * PropBuffering
const char* PropBuffering
void cacheLib(const wxString &aLibraryFileName)
LIB_PART * loadPart(FILE_LINE_READER &aReader)
static bool strCompare(const char *aString, const char *aLine, const char **aOutput=NULL)
Function strCompare.
const char * name
Class FILE_OUTPUTFORMATTER may be used for text file output.
Definition: richio.h:492
SCH_COMPONENT * loadComponent(FILE_LINE_READER &aReader)
void DeleteAlias(const wxString &aLibraryPath, const wxString &aAliasName, const PROPERTIES *aProperties=NULL) override
Delete aAliasName from the library at aLibraryPath.
int GetModifyHash() const override
Return the modification hash from the library cache.
FILL_T
Enum FILL_T is the set of fill types used in plotting or drawing enclosed areas.
Definition: base_struct.h:70
Class SCH_BUS_WIRE_ENTRY.
This file is part of the common libary.
const PAGE_INFO & GetPageSettings() const
SCH_LINE * loadWire(FILE_LINE_READER &aReader)
wxString GetLogicalName() const
LIB_ALIAS * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const PROPERTIES *aProperties=NULL) override
Load a LIB_ALIAS object having aAliasName from the aLibraryPath containing a library format that this...
void SetComment3(const wxString &aComment)
const wxString & GetType() const
SCH_NO_CONNECT * loadNoConnect(FILE_LINE_READER &aReader)
void SetFileName(const wxString &aFileName)
KIWAY * m_kiway
Required for path to legacy component libraries.
Definition for part library class.
void SetId(int aId)
Definition: sch_field.h:89
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Function Print formats and writes text to the output stream.
Definition: richio.cpp:408
wxString m_name
Name (not the field text value itself, that is .m_Text)
Definition: lib_field.h:63
wxString GetName() const
Definition: sch_sheet.h:274
Class PART_LIB is used to load, save, search, and otherwise manipulate part library files...
bool IsFile(const wxString &aFullPathAndFileName) const
void Save(bool aSaveDocFile=true)
Save the entire library to file m_libFileName;.
Class SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container clas...
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
Implementation of the label properties dialog.
void saveBusEntry(SCH_BUS_ENTRY_BASE *aBusEntry)
void DeleteSymbol(const wxString &aAliasName)
wxSize GetSize()
Definition: sch_sheet.h:288
int GetHeightMils() const
void AddSymbol(const LIB_PART *aPart)
bool writeDocFile(const PROPERTIES *aProperties)
SCH_SHEET * Load(const wxString &aFileName, KIWAY *aKiway, SCH_SHEET *aAppendToMe=NULL, const PROPERTIES *aProperties=NULL) override
Load information from some input file format that this SCH_PLUGIN implementation knows about...
const PROPERTIES * m_props
Passed via Save() or Load(), no ownership, may be NULL.
Class LIB_BEZIER defines bezier curve graphic body item.
Definition: lib_bezier.h:39
void loadFootprintFilters(std::unique_ptr< LIB_PART > &aPart, FILE_LINE_READER &aReader)
wxPoint GetPosition() const override
Function GetPosition.