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