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