KiCad PCB EDA Suite
sch_sexpr_parser.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2020 CERN
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Wayne Stambaugh <stambaughw@gmail.com>
8  *
9  * This program is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation, either version 3 of the License, or (at your
12  * option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * 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 
28 // For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
29 // base64 code.
30 #define wxUSE_BASE64 1
31 #include <wx/base64.h>
32 #include <wx/mstream.h>
33 #include <wx/tokenzr.h>
34 
35 #include <lib_id.h>
36 #include <lib_arc.h>
37 #include <lib_bezier.h>
38 #include <lib_circle.h>
39 #include <lib_pin.h>
40 #include <lib_polyline.h>
41 #include <lib_rectangle.h>
42 #include <lib_text.h>
43 #include <math/util.h> // KiROUND, Clamp
44 #include <kicad_string.h>
45 #include <sch_bitmap.h>
46 #include <sch_bus_entry.h>
47 #include <sch_symbol.h>
48 #include <sch_edit_frame.h> // SYM_ORIENT_XXX
49 #include <sch_field.h>
50 #include <sch_line.h>
51 #include <sch_junction.h>
52 #include <sch_no_connect.h>
53 #include <sch_screen.h>
54 #include <sch_sheet_pin.h>
56 #include <template_fieldnames.h>
57 #include <trigo.h>
59 
60 
61 using namespace TSCHEMATIC_T;
62 
63 
65  unsigned aLineCount ) :
66  SCHEMATIC_LEXER( aLineReader ),
67  m_requiredVersion( 0 ),
68  m_fieldId( 0 ),
69  m_unit( 1 ),
70  m_convert( 1 ),
71  m_progressReporter( aProgressReporter ),
72  m_lineReader( aLineReader ),
73  m_lastProgressLine( 0 ),
74  m_lineCount( aLineCount )
75 {
76 }
77 
78 
80 {
81  const unsigned PROGRESS_DELTA = 250;
82 
83  if( m_progressReporter )
84  {
85  unsigned curLine = m_lineReader->LineNumber();
86 
87  if( curLine > m_lastProgressLine + PROGRESS_DELTA )
88  {
89  m_progressReporter->SetCurrentProgress( ( (double) curLine )
90  / std::max( 1U, m_lineCount ) );
91 
93  THROW_IO_ERROR( ( "Open cancelled by user." ) );
94 
95  m_lastProgressLine = curLine;
96  }
97  }
98 }
99 
100 
102 {
103  T token = NextTok();
104 
105  if( token == T_yes )
106  return true;
107  else if( token == T_no )
108  return false;
109  else
110  Expecting( "yes or no" );
111 
112  return false;
113 }
114 
115 
117 {
118  T token;
119 
120  NeedLEFT();
121  NextTok();
122  parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION );
123 
124  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
125  {
126  if( token != T_LEFT )
127  Expecting( T_LEFT );
128 
129  token = NextTok();
130 
131  if( token == T_symbol )
132  {
133  m_unit = 1;
134  m_convert = 1;
135  LIB_SYMBOL* symbol = ParseSymbol( aSymbolLibMap, m_requiredVersion );
136  aSymbolLibMap[symbol->GetName()] = symbol;
137  }
138  else
139  {
140  Expecting( "symbol" );
141  }
142  }
143 }
144 
145 
146 LIB_SYMBOL* SCH_SEXPR_PARSER::ParseSymbol( LIB_SYMBOL_MAP& aSymbolLibMap, int aFileVersion )
147 {
148  wxCHECK_MSG( CurTok() == T_symbol, nullptr,
149  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
150 
151  T token;
152  long tmp;
153  wxString name;
154  wxString error;
155  LIB_ITEM* item;
156  LIB_FIELD* field;
157  std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
158  std::set<int> fieldIDsRead;
159 
160  m_requiredVersion = aFileVersion;
161  symbol->SetUnitCount( 1 );
162 
164 
165  token = NextTok();
166 
167  if( !IsSymbol( token ) )
168  {
169  THROW_PARSE_ERROR( _( "Invalid symbol name" ), CurSource(), CurLine(), CurLineNumber(),
170  CurOffset() );
171  }
172 
173  name = FromUTF8();
174 
175  LIB_ID id;
176 
177  if( id.Parse( name ) >= 0 )
178  {
179  THROW_PARSE_ERROR( _( "Invalid library identifier" ), CurSource(), CurLine(),
180  CurLineNumber(), CurOffset() );
181  }
182 
183  m_symbolName = id.GetLibItemName().wx_str();
184  symbol->SetName( m_symbolName );
185  symbol->SetLibId( id );
186 
187  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
188  {
189  if( token != T_LEFT )
190  Expecting( T_LEFT );
191 
192  token = NextTok();
193 
194  switch( token )
195  {
196  case T_power:
197  symbol->SetPower();
198  NeedRIGHT();
199  break;
200 
201  case T_pin_names:
202  parsePinNames( symbol );
203  break;
204 
205  case T_pin_numbers:
206  token = NextTok();
207 
208  if( token != T_hide )
209  Expecting( "hide" );
210 
211  symbol->SetShowPinNumbers( false );
212  NeedRIGHT();
213  break;
214 
215  case T_in_bom:
216  symbol->SetIncludeInBom( parseBool() );
217  NeedRIGHT();
218  break;
219 
220  case T_on_board:
221  symbol->SetIncludeOnBoard( parseBool() );
222  NeedRIGHT();
223  break;
224 
225  case T_property:
226  field = parseProperty( symbol );
227 
228  if( field )
229  {
230  // It would appear that at some point we allowed duplicate ids to slip through
231  // when writing files. The easiest (and most complete) solution is to disallow
232  // multiple instances of the same id (for all files since the source of the error
233  // *might* in fact be hand-edited files).
234  //
235  // While no longer used, -1 is still a valid id for user field. It gets converted
236  // to the next unused number on save.
237  if( fieldIDsRead.count( field->GetId() ) )
238  field->SetId( -1 );
239  else if( field )
240  fieldIDsRead.insert( field->GetId() );
241  }
242 
243  break;
244 
245  case T_extends:
246  {
247  token = NextTok();
248 
249  if( !IsSymbol( token ) )
250  {
251  THROW_PARSE_ERROR( _( "Invalid parent symbol name" ), CurSource(), CurLine(),
252  CurLineNumber(), CurOffset() );
253  }
254 
255  name = FromUTF8();
256  auto it = aSymbolLibMap.find( name );
257 
258  if( it == aSymbolLibMap.end() )
259  {
260  error.Printf( _( "No parent for extended symbol %s" ), name.c_str() );
261  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
262  }
263 
264  symbol->SetParent( it->second );
265  NeedRIGHT();
266  break;
267  }
268 
269  case T_symbol:
270  {
271  token = NextTok();
272 
273  if( !IsSymbol( token ) )
274  {
275  THROW_PARSE_ERROR( _( "Invalid symbol unit name" ), CurSource(), CurLine(),
276  CurLineNumber(), CurOffset() );
277  }
278 
279  name = FromUTF8();
280 
281  if( !name.StartsWith( m_symbolName ) )
282  {
283  error.Printf( _( "Invalid symbol unit name prefix %s" ), name.c_str() );
284  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
285  }
286 
287  name = name.Right( name.Length() - m_symbolName.Length() - 1 );
288 
289  wxStringTokenizer tokenizer( name, "_" );
290 
291  if( tokenizer.CountTokens() != 2 )
292  {
293  error.Printf( _( "Invalid symbol unit name suffix %s" ), name.c_str() );
294  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
295  }
296 
297  if( !tokenizer.GetNextToken().ToLong( &tmp ) )
298  {
299  error.Printf( _( "Invalid symbol unit number %s" ), name.c_str() );
300  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
301  }
302 
303  m_unit = static_cast<int>( tmp );
304 
305  if( !tokenizer.GetNextToken().ToLong( &tmp ) )
306  {
307  error.Printf( _( "Invalid symbol convert number %s" ), name.c_str() );
308  THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
309  }
310 
311  m_convert = static_cast<int>( tmp );
312 
313  if( m_convert > 1 )
314  symbol->SetConversion( true, false );
315 
316  if( m_unit > symbol->GetUnitCount() )
317  symbol->SetUnitCount( m_unit, false );
318 
319  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
320  {
321  if( token != T_LEFT )
322  Expecting( T_LEFT );
323 
324  token = NextTok();
325 
326  switch( token )
327  {
328  case T_arc:
329  case T_bezier:
330  case T_circle:
331  case T_pin:
332  case T_polyline:
333  case T_rectangle:
334  case T_text:
335  item = ParseDrawItem();
336 
337  wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
338 
339  item->SetParent( symbol.get() );
340  symbol->AddDrawItem( item, false );
341  break;
342 
343  default:
344  Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
345  };
346  }
347 
348  m_unit = 1;
349  m_convert = 1;
350  break;
351  }
352 
353  case T_arc:
354  case T_bezier:
355  case T_circle:
356  case T_pin:
357  case T_polyline:
358  case T_rectangle:
359  case T_text:
360  item = ParseDrawItem();
361 
362  wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
363 
364  item->SetParent( symbol.get() );
365  symbol->AddDrawItem( item, false );
366  break;
367 
368  default:
369  Expecting( "pin_names, pin_numbers, arc, bezier, circle, pin, polyline, "
370  "rectangle, or text" );
371  }
372  }
373 
374  symbol->GetDrawItems().sort();
375  m_symbolName.clear();
376 
377  return symbol.release();
378 }
379 
380 
382 {
383  switch( CurTok() )
384  {
385  case T_arc:
386  return static_cast<LIB_ITEM*>( parseArc() );
387  break;
388 
389  case T_bezier:
390  return static_cast<LIB_ITEM*>( parseBezier() );
391  break;
392 
393  case T_circle:
394  return static_cast<LIB_ITEM*>( parseCircle() );
395  break;
396 
397  case T_pin:
398  return static_cast<LIB_ITEM*>( parsePin() );
399  break;
400 
401  case T_polyline:
402  return static_cast<LIB_ITEM*>( parsePolyLine() );
403  break;
404 
405  case T_rectangle:
406  return static_cast<LIB_ITEM*>( parseRectangle() );
407  break;
408 
409  case T_text:
410  return static_cast<LIB_TEXT*>( parseText() );
411  break;
412 
413  default:
414  Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
415  }
416 
417  return nullptr;
418 }
419 
420 
422 {
423  char* tmp;
424 
425  // In case the file got saved with the wrong locale.
426  if( strchr( CurText(), ',' ) != nullptr )
427  {
428  THROW_PARSE_ERROR( _( "Floating point number with incorrect locale" ), CurSource(),
429  CurLine(), CurLineNumber(), CurOffset() );
430  }
431 
432  errno = 0;
433 
434  double fval = strtod( CurText(), &tmp );
435 
436  if( errno )
437  {
438  THROW_PARSE_ERROR( _( "Invalid floating point number" ), CurSource(), CurLine(),
439  CurLineNumber(), CurOffset() );
440  }
441 
442  if( CurText() == tmp )
443  {
444  THROW_PARSE_ERROR( _( "Missing floating point number" ), CurSource(), CurLine(),
445  CurLineNumber(), CurOffset() );
446  }
447 
448  return fval;
449 }
450 
451 
453 {
454  auto retval = parseDouble() * IU_PER_MM;
455 
456  // Schematic internal units are represented as integers. Any values that are
457  // larger or smaller than the schematic units represent undefined behavior for
458  // the system. Limit values to the largest that can be displayed on the screen.
459  double int_limit = std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
460 
461  return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
462 }
463 
464 
465 int SCH_SEXPR_PARSER::parseInternalUnits( const char* aExpected )
466 {
467  auto retval = parseDouble( aExpected ) * IU_PER_MM;
468 
469  double int_limit = std::numeric_limits<int>::max() * 0.7071;
470 
471  return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
472 }
473 
474 
476 {
477  wxCHECK_RET( CurTok() == T_stroke,
478  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a stroke." ) );
479 
480  aStroke.SetWidth( Mils2iu( DEFAULT_LINE_THICKNESS ) );
482  aStroke.SetColor( COLOR4D::UNSPECIFIED );
483 
484  T token;
485 
486  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
487  {
488  if( token != T_LEFT )
489  Expecting( T_LEFT );
490 
491  token = NextTok();
492 
493  switch( token )
494  {
495  case T_width:
496  aStroke.SetWidth( parseInternalUnits( "stroke width" ) );
497  NeedRIGHT();
498  break;
499 
500  case T_type:
501  {
502  token = NextTok();
503 
504  switch( token )
505  {
506  case T_dash: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DASH ); break;
507  case T_dot: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DOT ); break;
508  case T_dash_dot: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DASHDOT ); break;
509  case T_solid: aStroke.SetPlotStyle( PLOT_DASH_TYPE::SOLID ); break;
510  case T_default: aStroke.SetPlotStyle( PLOT_DASH_TYPE::DEFAULT ); break;
511  default:
512  Expecting( "solid, dash, dash_dot, dot or default" );
513  }
514 
515  NeedRIGHT();
516  break;
517  }
518 
519  case T_color:
520  {
521  COLOR4D color;
522 
523  color.r = parseInt( "red" ) / 255.0;
524  color.g = parseInt( "green" ) / 255.0;
525  color.b = parseInt( "blue" ) / 255.0;
526  color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
527 
528  aStroke.SetColor( color );
529  NeedRIGHT();
530  break;
531  }
532 
533  default:
534  Expecting( "width, type, or color" );
535  }
536 
537  }
538 }
539 
540 
542 {
543  wxCHECK_RET( CurTok() == T_fill,
544  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as fill." ) );
545 
547  aFill.m_Color = COLOR4D::UNSPECIFIED;
548 
549  T token;
550 
551  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
552  {
553  if( token != T_LEFT )
554  Expecting( T_LEFT );
555 
556  token = NextTok();
557 
558  switch( token )
559  {
560  case T_type:
561  {
562  token = NextTok();
563 
564  switch( token )
565  {
566  case T_none: aFill.m_FillType = FILL_TYPE::NO_FILL; break;
567  case T_outline: aFill.m_FillType = FILL_TYPE::FILLED_SHAPE; break;
568  case T_background: aFill.m_FillType = FILL_TYPE::FILLED_WITH_BG_BODYCOLOR; break;
569  default: Expecting( "none, outline, or background" );
570  }
571 
572  NeedRIGHT();
573  break;
574  }
575 
576  case T_color:
577  {
578  COLOR4D color;
579 
580  color.r = parseInt( "red" ) / 255.0;
581  color.g = parseInt( "green" ) / 255.0;
582  color.b = parseInt( "blue" ) / 255.0;
583  color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
584  aFill.m_Color = color;
585  NeedRIGHT();
586  break;
587  }
588 
589  default:
590  Expecting( "type or color" );
591  }
592  }
593 }
594 
595 
596 void SCH_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText, bool aConvertOverbarSyntax )
597 {
598  wxCHECK_RET( aText && CurTok() == T_effects,
599  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
600 
601  // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
602  // We need to convert the old syntax to the new one.
603  if( aConvertOverbarSyntax && m_requiredVersion < 20210606 )
604  aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
605 
606  T token;
607 
608  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
609  {
610  if( token == T_LEFT )
611  token = NextTok();
612 
613  switch( token )
614  {
615  case T_font:
616  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
617  {
618  if( token == T_LEFT )
619  token = NextTok();
620 
621  switch( token )
622  {
623  case T_size:
624  {
625  wxSize sz;
626  sz.SetHeight( parseInternalUnits( "text height" ) );
627  sz.SetWidth( parseInternalUnits( "text width" ) );
628  aText->SetTextSize( sz );
629  NeedRIGHT();
630  break;
631  }
632 
633  case T_thickness:
634  aText->SetTextThickness( parseInternalUnits( "text thickness" ) );
635  NeedRIGHT();
636  break;
637 
638  case T_bold:
639  aText->SetBold( true );
640  break;
641 
642  case T_italic:
643  aText->SetItalic( true );
644  break;
645 
646  default:
647  Expecting( "size, bold, or italic" );
648  }
649  }
650 
651  break;
652 
653  case T_justify:
654  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
655  {
656  switch( token )
657  {
658  case T_left: aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break;
659  case T_right: aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); break;
660  case T_top: aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
661  case T_bottom: aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
662  case T_mirror: aText->SetMirrored( true ); break;
663  default: Expecting( "left, right, top, bottom, or mirror" );
664  }
665  }
666 
667  break;
668 
669  case T_hide:
670  aText->SetVisible( false );
671  break;
672 
673  default:
674  Expecting( "font, justify, or hide" );
675  }
676  }
677 }
678 
679 
680 void SCH_SEXPR_PARSER::parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion )
681 {
682  wxCHECK_RET( CurTok() == aHeaderType,
683  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
684 
685  NeedLEFT();
686 
687  T tok = NextTok();
688 
689  if( tok == T_version )
690  {
691  m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
692 
693  if( m_requiredVersion > aFileVersion )
694  throw FUTURE_FORMAT_ERROR( FromUTF8() );
695 
696  NeedRIGHT();
697 
698  // Skip the host name and host build version information.
699  NeedLEFT();
700  NeedSYMBOL();
701  NeedSYMBOL();
702 
703  if( m_requiredVersion < 20200827 )
704  NeedSYMBOL();
705 
706  NeedRIGHT();
707  }
708  else
709  {
710  m_requiredVersion = aFileVersion;
711 
712  // Skip the host name and host build version information.
713  NeedSYMBOL();
714  NeedSYMBOL();
715  NeedRIGHT();
716  }
717 }
718 
719 
720 void SCH_SEXPR_PARSER::parsePinNames( std::unique_ptr<LIB_SYMBOL>& aSymbol )
721 {
722  wxCHECK_RET( CurTok() == T_pin_names,
723  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
724  wxT( " as a pin_name token." ) );
725 
726  wxString error;
727 
728  T token = NextTok();
729 
730  if( token == T_LEFT )
731  {
732  token = NextTok();
733 
734  if( token != T_offset )
735  Expecting( "offset" );
736 
737  aSymbol->SetPinNameOffset( parseInternalUnits( "pin name offset" ) );
738  NeedRIGHT();
739  token = NextTok(); // Either ) or hide
740  }
741 
742  if( token == T_hide )
743  {
744  aSymbol->SetShowPinNames( false );
745  NeedRIGHT();
746  }
747  else if( token != T_RIGHT )
748  {
749  THROW_PARSE_ERROR( _( "Invalid pin names definition" ), CurSource(), CurLine(),
750  CurLineNumber(), CurOffset() );
751  }
752 }
753 
754 
755 LIB_FIELD* SCH_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_SYMBOL>& aSymbol )
756 {
757  wxCHECK_MSG( CurTok() == T_property, nullptr,
758  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property." ) );
759  wxCHECK( aSymbol, nullptr );
760 
761  wxString error;
762  wxString name;
763  wxString value;
764  std::unique_ptr<LIB_FIELD> field = std::make_unique<LIB_FIELD>( aSymbol.get(),
766 
767  T token = NextTok();
768 
769  if( !IsSymbol( token ) )
770  {
771  THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
772  CurOffset() );
773  }
774 
775  name = FromUTF8();
776 
777  if( name.IsEmpty() )
778  {
779  THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
780  CurOffset() );
781  }
782 
783  field->SetName( name );
784  token = NextTok();
785 
786  if( !IsSymbol( token ) )
787  {
788  THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
789  CurOffset() );
790  }
791 
792  // Empty property values are valid.
793  value = FromUTF8();
794 
795  field->SetText( value );
796 
797  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
798  {
799  if( token != T_LEFT )
800  Expecting( T_LEFT );
801 
802  token = NextTok();
803 
804  switch( token )
805  {
806  case T_id:
807  field->SetId( parseInt( "field ID" ) );
808  NeedRIGHT();
809  break;
810 
811  case T_at:
812  field->SetPosition( parseXY() );
813  field->SetTextAngle( static_cast<int>( parseDouble( "text angle" ) * 10.0 ) );
814  NeedRIGHT();
815  break;
816 
817  case T_effects:
818  parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ), field->GetId() == VALUE_FIELD );
819  break;
820 
821  default:
822  Expecting( "id, at or effects" );
823  }
824  }
825 
826  LIB_FIELD* existingField;
827 
828  if( field->GetId() < MANDATORY_FIELDS )
829  {
830  existingField = aSymbol->GetFieldById( field->GetId() );
831 
832  *existingField = *field;
833  return existingField;
834  }
835  else if( name == "ki_keywords" )
836  {
837  // Not a LIB_FIELD object yet.
838  aSymbol->SetKeyWords( value );
839  return nullptr;
840  }
841  else if( name == "ki_description" )
842  {
843  // Not a LIB_FIELD object yet.
844  aSymbol->SetDescription( value );
845  return nullptr;
846  }
847  else if( name == "ki_fp_filters" )
848  {
849  // Not a LIB_FIELD object yet.
850  wxArrayString filters;
851  wxStringTokenizer tokenizer( value );
852 
853  while( tokenizer.HasMoreTokens() )
854  filters.Add( tokenizer.GetNextToken() );
855 
856  aSymbol->SetFPFilters( filters );
857  return nullptr;
858  }
859  else if( name == "ki_locked" )
860  {
861  // This is a temporary LIB_FIELD object until interchangeable units are determined on
862  // the fly.
863  aSymbol->LockUnits( true );
864  return nullptr;
865  }
866  else
867  {
868  // At this point, a user field is read.
869  existingField = aSymbol->FindField( field->GetCanonicalName() );
870 
871 #if 1 // Enable it to modify the name of the field to add if already existing
872  // Disable it to skip the field having the same name as previous field
873  if( existingField )
874  {
875  // We cannot handle 2 fields with the same name, so because the field name
876  // is already in use, try to build a new name (oldname_x)
877  wxString base_name = field->GetCanonicalName();
878 
879  // Arbitrary limit 10 attempts to find a new name
880  for( int ii = 1; ii < 10 && existingField ; ii++ )
881  {
882  wxString newname = base_name;
883  newname << '_' << ii;
884 
885  existingField = aSymbol->FindField( newname );
886 
887  if( !existingField ) // the modified name is not found, use it
888  field->SetName( newname );
889  }
890  }
891 #endif
892  if( !existingField )
893  {
894  aSymbol->AddDrawItem( field.get(), false );
895  return field.release();
896  }
897  else
898  {
899  // We cannot handle 2 fields with the same name, so skip this one
900  return nullptr;
901  }
902  }
903 }
904 
905 
907 {
908  wxCHECK_MSG( CurTok() == T_arc, nullptr,
909  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc token." ) );
910 
911  T token;
912  wxPoint startPoint;
913  wxPoint midPoint;
914  wxPoint endPoint;
915  wxPoint pos;
916  FILL_PARAMS fill;
917  bool hasMidPoint = false;
918  std::unique_ptr<LIB_ARC> arc = std::make_unique<LIB_ARC>( nullptr );
919 
920  arc->SetUnit( m_unit );
921  arc->SetConvert( m_convert );
922 
923  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
924  {
925  if( token != T_LEFT )
926  Expecting( T_LEFT );
927 
928  token = NextTok();
929 
930  switch( token )
931  {
932  case T_start:
933  startPoint = parseXY();
934  NeedRIGHT();
935  break;
936 
937  case T_mid:
938  midPoint = parseXY();
939  NeedRIGHT();
940  hasMidPoint = true;
941  break;
942 
943  case T_end:
944  endPoint = parseXY();
945  NeedRIGHT();
946  break;
947 
948  case T_radius:
949  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
950  {
951  if( token != T_LEFT )
952  Expecting( T_LEFT );
953 
954  token = NextTok();
955 
956  switch( token )
957  {
958  case T_at:
959  pos = parseXY();
960  NeedRIGHT();
961  break;
962 
963  case T_length:
964  arc->SetRadius( parseInternalUnits( "radius length" ) );
965  NeedRIGHT();
966  break;
967 
968  case T_angles:
969  {
970  int angle1 = KiROUND( parseDouble( "start radius angle" ) * 10.0 );
971  int angle2 = KiROUND( parseDouble( "end radius angle" ) * 10.0 );
972 
973  NORMALIZE_ANGLE_POS( angle1 );
974  NORMALIZE_ANGLE_POS( angle2 );
975  arc->SetFirstRadiusAngle( angle1 );
976  arc->SetSecondRadiusAngle( angle2 );
977  NeedRIGHT();
978  break;
979  }
980 
981  default:
982  Expecting( "at, length, or angle" );
983  }
984  }
985 
986  break;
987 
988  case T_stroke:
989  NeedLEFT();
990  token = NextTok();
991 
992  if( token != T_width )
993  Expecting( "width" );
994 
995  arc->SetWidth( parseInternalUnits( "stroke width" ) );
996  NeedRIGHT(); // Closes width token;
997  NeedRIGHT(); // Closes stroke token;
998  break;
999 
1000  case T_fill:
1001  parseFill( fill );
1002  arc->SetFillMode( fill.m_FillType );
1003  break;
1004 
1005  default:
1006  Expecting( "start, end, radius, stroke, or fill" );
1007  }
1008  }
1009 
1010  arc->SetPosition( pos );
1011  arc->SetStart( startPoint );
1012  arc->SetEnd( endPoint );
1013 
1014  if( hasMidPoint )
1015  {
1016  VECTOR2I center = GetArcCenter( arc->GetStart(), midPoint, arc->GetEnd() );
1017 
1018  arc->SetPosition( wxPoint( center.x, center.y ) );
1019 
1020  // @todo Calculate the radius.
1021 
1022  arc->CalcRadiusAngles();
1023  }
1024 
1025  return arc.release();
1026 }
1027 
1028 
1030 {
1031  wxCHECK_MSG( CurTok() == T_bezier, nullptr,
1032  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
1033 
1034  T token;
1035  FILL_PARAMS fill;
1036  std::unique_ptr<LIB_BEZIER> bezier = std::make_unique<LIB_BEZIER>( nullptr );
1037 
1038  bezier->SetUnit( m_unit );
1039  bezier->SetConvert( m_convert );
1040 
1041  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1042  {
1043  if( token != T_LEFT )
1044  Expecting( T_LEFT );
1045 
1046  token = NextTok();
1047 
1048  switch( token )
1049  {
1050  case T_pts:
1051  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1052  {
1053  if( token != T_LEFT )
1054  Expecting( T_LEFT );
1055 
1056  token = NextTok();
1057 
1058  if( token != T_xy )
1059  Expecting( "xy" );
1060 
1061  bezier->AddPoint( parseXY() );
1062 
1063  NeedRIGHT();
1064  }
1065 
1066  break;
1067 
1068  case T_stroke:
1069  NeedLEFT();
1070  token = NextTok();
1071 
1072  if( token != T_width )
1073  Expecting( "width" );
1074 
1075  bezier->SetWidth( parseInternalUnits( "stroke width" ) );
1076  NeedRIGHT(); // Closes width token;
1077  NeedRIGHT(); // Closes stroke token;
1078  break;
1079 
1080  case T_fill:
1081  parseFill( fill );
1082  bezier->SetFillMode( fill.m_FillType );
1083  break;
1084 
1085  default:
1086  Expecting( "pts, stroke, or fill" );
1087  }
1088  }
1089 
1090  return bezier.release();
1091 }
1092 
1093 
1095 {
1096  wxCHECK_MSG( CurTok() == T_circle, nullptr,
1097  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1098  wxT( " as a circle token." ) );
1099 
1100  T token;
1101  FILL_PARAMS fill;
1102  std::unique_ptr<LIB_CIRCLE> circle = std::make_unique<LIB_CIRCLE>( nullptr );
1103 
1104  circle->SetUnit( m_unit );
1105  circle->SetConvert( m_convert );
1106 
1107  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1108  {
1109  if( token != T_LEFT )
1110  Expecting( T_LEFT );
1111 
1112  token = NextTok();
1113 
1114  switch( token )
1115  {
1116  case T_center:
1117  circle->SetPosition( parseXY() );
1118  NeedRIGHT();
1119  break;
1120 
1121  case T_radius:
1122  circle->SetRadius( parseInternalUnits( "radius length" ) );
1123  NeedRIGHT();
1124  break;
1125 
1126  case T_stroke:
1127  NeedLEFT();
1128  token = NextTok();
1129 
1130  if( token != T_width )
1131  Expecting( "width" );
1132 
1133  circle->SetWidth( parseInternalUnits( "stroke width" ) );
1134  NeedRIGHT(); // Closes width token;
1135  NeedRIGHT(); // Closes stroke token;
1136  break;
1137 
1138  case T_fill:
1139  parseFill( fill );
1140  circle->SetFillMode( fill.m_FillType );
1141  break;
1142 
1143  default:
1144  Expecting( "start, end, radius, stroke, or fill" );
1145  }
1146  }
1147 
1148  return circle.release();
1149 }
1150 
1151 
1153 {
1154  auto parseType = [&]( T token ) -> ELECTRICAL_PINTYPE
1155  {
1156  switch( token )
1157  {
1158  case T_input: return ELECTRICAL_PINTYPE::PT_INPUT;
1159  case T_output: return ELECTRICAL_PINTYPE::PT_OUTPUT;
1160  case T_bidirectional: return ELECTRICAL_PINTYPE::PT_BIDI;
1161  case T_tri_state: return ELECTRICAL_PINTYPE::PT_TRISTATE;
1162  case T_passive: return ELECTRICAL_PINTYPE::PT_PASSIVE;
1163  case T_unspecified: return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1164  case T_power_in: return ELECTRICAL_PINTYPE::PT_POWER_IN;
1165  case T_power_out: return ELECTRICAL_PINTYPE::PT_POWER_OUT;
1166  case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
1167  case T_open_emitter: return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
1168  case T_unconnected:
1169  case T_no_connect: return ELECTRICAL_PINTYPE::PT_NC;
1170  case T_free: return ELECTRICAL_PINTYPE::PT_NIC;
1171 
1172  default:
1173  Expecting( "input, output, bidirectional, tri_state, passive, "
1174  "unspecified, power_in, power_out, open_collector, "
1175  "open_emitter, free or no_connect" );
1177  }
1178  };
1179 
1180  auto parseShape = [&]( T token ) -> GRAPHIC_PINSHAPE
1181  {
1182  switch( token )
1183  {
1184  case T_line: return GRAPHIC_PINSHAPE::LINE;
1185  case T_inverted: return GRAPHIC_PINSHAPE::INVERTED;
1186  case T_clock: return GRAPHIC_PINSHAPE::CLOCK;
1187  case T_inverted_clock: return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
1188  case T_input_low: return GRAPHIC_PINSHAPE::INPUT_LOW;
1189  case T_clock_low: return GRAPHIC_PINSHAPE::CLOCK_LOW;
1190  case T_output_low: return GRAPHIC_PINSHAPE::OUTPUT_LOW;
1191  case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
1192  case T_non_logic: return GRAPHIC_PINSHAPE::NONLOGIC;
1193 
1194  default:
1195  Expecting( "line, inverted, clock, inverted_clock, input_low, "
1196  "clock_low, output_low, edge_clock_high, non_logic" );
1197  return GRAPHIC_PINSHAPE::LINE;
1198  }
1199  };
1200 
1201  wxCHECK_MSG( CurTok() == T_pin, nullptr,
1202  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
1203 
1204  T token;
1205  wxString tmp;
1206  wxString error;
1207  std::unique_ptr<LIB_PIN> pin = std::make_unique<LIB_PIN>( nullptr );
1208 
1209  pin->SetUnit( m_unit );
1210  pin->SetConvert( m_convert );
1211 
1212  // Pin electrical type.
1213  token = NextTok();
1214  pin->SetType( parseType( token ) );
1215 
1216  // Pin shape.
1217  token = NextTok();
1218  pin->SetShape( parseShape( token ) );
1219 
1220  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1221  {
1222  if( token == T_hide )
1223  {
1224  pin->SetVisible( false );
1225  continue;
1226  }
1227 
1228  if( token != T_LEFT )
1229  Expecting( T_LEFT );
1230 
1231  token = NextTok();
1232 
1233  switch( token )
1234  {
1235  case T_at:
1236  pin->SetPosition( parseXY() );
1237 
1238  switch( parseInt( "pin orientation" ) )
1239  {
1240  case 0:
1241  pin->SetOrientation( PIN_RIGHT );
1242  break;
1243 
1244  case 90:
1245  pin->SetOrientation( PIN_UP );
1246  break;
1247 
1248  case 180:
1249  pin->SetOrientation( PIN_LEFT );
1250  break;
1251 
1252  case 270:
1253  pin->SetOrientation( PIN_DOWN );
1254  break;
1255 
1256  default:
1257  Expecting( "0, 90, 180, or 270" );
1258  }
1259 
1260  NeedRIGHT();
1261  break;
1262 
1263  case T_length:
1264  pin->SetLength( parseInternalUnits( "pin length" ) );
1265  NeedRIGHT();
1266  break;
1267 
1268  case T_name:
1269  token = NextTok();
1270 
1271  if( !IsSymbol( token ) )
1272  {
1273  THROW_PARSE_ERROR( _( "Invalid pin name" ), CurSource(), CurLine(), CurLineNumber(),
1274  CurOffset() );
1275  }
1276 
1277  if( m_requiredVersion < 20210606 )
1278  pin->SetName( ConvertToNewOverbarNotation( FromUTF8() ) );
1279  else
1280  pin->SetName( FromUTF8() );
1281 
1282  token = NextTok();
1283 
1284  if( token != T_RIGHT )
1285  {
1286  token = NextTok();
1287 
1288  if( token == T_effects )
1289  {
1290  // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1291  // so duplicate parsing is not required.
1292  EDA_TEXT text;
1293 
1294  parseEDA_TEXT( &text, true );
1295  pin->SetNameTextSize( text.GetTextHeight() );
1296  NeedRIGHT();
1297  }
1298  else
1299  {
1300  Expecting( "effects" );
1301  }
1302  }
1303 
1304  break;
1305 
1306  case T_number:
1307  token = NextTok();
1308 
1309  if( !IsSymbol( token ) )
1310  {
1311  THROW_PARSE_ERROR( _( "Invalid pin number" ), CurSource(), CurLine(),
1312  CurLineNumber(), CurOffset() );
1313  }
1314 
1315  pin->SetNumber( FromUTF8() );
1316  token = NextTok();
1317 
1318  if( token != T_RIGHT )
1319  {
1320  token = NextTok();
1321 
1322  if( token == T_effects )
1323  {
1324  // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1325  // so duplicate parsing is not required.
1326  EDA_TEXT text;
1327 
1328  parseEDA_TEXT( &text, false );
1329  pin->SetNumberTextSize( text.GetTextHeight() );
1330  NeedRIGHT();
1331  }
1332  else
1333  {
1334  Expecting( "effects" );
1335  }
1336  }
1337 
1338  break;
1339 
1340  case T_alternate:
1341  {
1342  LIB_PIN::ALT alt;
1343 
1344  token = NextTok();
1345 
1346  if( !IsSymbol( token ) )
1347  {
1348  THROW_PARSE_ERROR( _( "Invalid alternate pin name" ), CurSource(), CurLine(),
1349  CurLineNumber(), CurOffset() );
1350  }
1351 
1352  alt.m_Name = FromUTF8();
1353 
1354  token = NextTok();
1355  alt.m_Type = parseType( token );
1356 
1357  token = NextTok();
1358  alt.m_Shape = parseShape( token );
1359 
1360  pin->GetAlternates()[ alt.m_Name ] = alt;
1361 
1362  NeedRIGHT();
1363  }
1364  break;
1365 
1366  default:
1367  Expecting( "at, name, number, length, or alternate" );
1368  }
1369  }
1370 
1371  return pin.release();
1372 }
1373 
1374 
1376 {
1377  wxCHECK_MSG( CurTok() == T_polyline, nullptr,
1378  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a polyline." ) );
1379 
1380  T token;
1381  FILL_PARAMS fill;
1382  std::unique_ptr<LIB_POLYLINE> polyLine = std::make_unique<LIB_POLYLINE>( nullptr );
1383 
1384  polyLine->SetUnit( m_unit );
1385  polyLine->SetConvert( m_convert );
1386 
1387  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1388  {
1389  if( token != T_LEFT )
1390  Expecting( T_LEFT );
1391 
1392  token = NextTok();
1393 
1394  switch( token )
1395  {
1396  case T_pts:
1397  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1398  {
1399  if( token != T_LEFT )
1400  Expecting( T_LEFT );
1401 
1402  token = NextTok();
1403 
1404  if( token != T_xy )
1405  Expecting( "xy" );
1406 
1407  polyLine->AddPoint( parseXY() );
1408 
1409  NeedRIGHT();
1410  }
1411 
1412  break;
1413 
1414  case T_stroke:
1415  NeedLEFT();
1416  token = NextTok();
1417 
1418  if( token != T_width )
1419  Expecting( "width" );
1420 
1421  polyLine->SetWidth( parseInternalUnits( "stroke width" ) );
1422  NeedRIGHT(); // Closes width token;
1423  NeedRIGHT(); // Closes stroke token;
1424  break;
1425 
1426  case T_fill:
1427  parseFill( fill );
1428  polyLine->SetFillMode( fill.m_FillType );
1429  break;
1430 
1431  default:
1432  Expecting( "pts, stroke, or fill" );
1433  }
1434  }
1435 
1436  return polyLine.release();
1437 }
1438 
1439 
1441 {
1442  wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
1443  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1444  wxT( " as a rectangle token." ) );
1445 
1446  T token;
1447  FILL_PARAMS fill;
1448  std::unique_ptr<LIB_RECTANGLE> rectangle = std::make_unique<LIB_RECTANGLE>( nullptr );
1449 
1450  rectangle->SetUnit( m_unit );
1451  rectangle->SetConvert( m_convert );
1452 
1453  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1454  {
1455  if( token != T_LEFT )
1456  Expecting( T_LEFT );
1457 
1458  token = NextTok();
1459 
1460  switch( token )
1461  {
1462  case T_start:
1463  rectangle->SetPosition( parseXY() );
1464  NeedRIGHT();
1465  break;
1466 
1467  case T_end:
1468  rectangle->SetEnd( parseXY() );
1469  NeedRIGHT();
1470  break;
1471 
1472  case T_stroke:
1473  NeedLEFT();
1474  token = NextTok();
1475 
1476  if( token != T_width )
1477  Expecting( "width" );
1478 
1479  rectangle->SetWidth( parseInternalUnits( "stroke width" ) );
1480  NeedRIGHT(); // Closes width token;
1481  NeedRIGHT(); // Closes stroke token;
1482  break;
1483 
1484  case T_fill:
1485  parseFill( fill );
1486  rectangle->SetFillMode( fill.m_FillType );
1487  break;
1488 
1489  default:
1490  Expecting( "start, end, stroke, or fill" );
1491  }
1492  }
1493 
1494  return rectangle.release();
1495 }
1496 
1497 
1499 {
1500  wxCHECK_MSG( CurTok() == T_text, nullptr,
1501  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
1502 
1503  T token;
1504  wxString tmp;
1505  std::unique_ptr<LIB_TEXT> text = std::make_unique<LIB_TEXT>( nullptr );
1506 
1507  text->SetUnit( m_unit );
1508  text->SetConvert( m_convert );
1509  token = NextTok();
1510 
1511  if( !IsSymbol( token ) )
1512  {
1513  THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
1514  CurOffset() );
1515  }
1516 
1517  text->SetText( FromUTF8() );
1518 
1519  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1520  {
1521  if( token != T_LEFT )
1522  Expecting( T_LEFT );
1523 
1524  token = NextTok();
1525 
1526  switch( token )
1527  {
1528  case T_at:
1529  text->SetPosition( parseXY() );
1530  text->SetTextAngle( parseDouble( "text angle" ) );
1531  NeedRIGHT();
1532  break;
1533 
1534  case T_effects:
1535  parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
1536  break;
1537 
1538  default:
1539  Expecting( "at or effects" );
1540  }
1541  }
1542 
1543  return text.release();
1544 }
1545 
1546 
1548 {
1549  wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
1550  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
1551 
1552  T token;
1553 
1554  NeedSYMBOL();
1555 
1556  wxString pageType = FromUTF8();
1557 
1558  if( !aPageInfo.SetType( pageType ) )
1559  {
1560  THROW_PARSE_ERROR( _( "Invalid page type" ), CurSource(), CurLine(), CurLineNumber(),
1561  CurOffset() );
1562  }
1563 
1564  if( pageType == PAGE_INFO::Custom )
1565  {
1566  double width = parseDouble( "width" ); // width in mm
1567 
1568  // Perform some controls to avoid crashes if the size is edited by hands
1569  if( width < 100.0 )
1570  width = 100.0;
1571  else if( width > 1200.0 )
1572  width = 1200.0;
1573 
1574  double height = parseDouble( "height" ); // height in mm
1575 
1576  if( height < 100.0 )
1577  height = 100.0;
1578  else if( height > 1200.0 )
1579  height = 1200.0;
1580 
1581  aPageInfo.SetWidthMils( Mm2mils( width ) );
1582  aPageInfo.SetHeightMils( Mm2mils( height ) );
1583  }
1584 
1585  token = NextTok();
1586 
1587  if( token == T_portrait )
1588  {
1589  aPageInfo.SetPortrait( true );
1590  NeedRIGHT();
1591  }
1592  else if( token != T_RIGHT )
1593  {
1594  Expecting( "portrait" );
1595  }
1596 }
1597 
1598 
1600 {
1601  wxCHECK_RET( CurTok() == T_title_block,
1602  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1603  wxT( " as TITLE_BLOCK." ) );
1604 
1605  T token;
1606 
1607  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1608  {
1609  if( token != T_LEFT )
1610  Expecting( T_LEFT );
1611 
1612  token = NextTok();
1613 
1614  switch( token )
1615  {
1616  case T_title:
1617  NextTok();
1618  aTitleBlock.SetTitle( FromUTF8() );
1619  break;
1620 
1621  case T_date:
1622  NextTok();
1623  aTitleBlock.SetDate( FromUTF8() );
1624  break;
1625 
1626  case T_rev:
1627  NextTok();
1628  aTitleBlock.SetRevision( FromUTF8() );
1629  break;
1630 
1631  case T_company:
1632  NextTok();
1633  aTitleBlock.SetCompany( FromUTF8() );
1634  break;
1635 
1636  case T_comment:
1637  {
1638  int commentNumber = parseInt( "comment" );
1639 
1640  switch( commentNumber )
1641  {
1642  case 1:
1643  NextTok();
1644  aTitleBlock.SetComment( 0, FromUTF8() );
1645  break;
1646 
1647  case 2:
1648  NextTok();
1649  aTitleBlock.SetComment( 1, FromUTF8() );
1650  break;
1651 
1652  case 3:
1653  NextTok();
1654  aTitleBlock.SetComment( 2, FromUTF8() );
1655  break;
1656 
1657  case 4:
1658  NextTok();
1659  aTitleBlock.SetComment( 3, FromUTF8() );
1660  break;
1661 
1662  case 5:
1663  NextTok();
1664  aTitleBlock.SetComment( 4, FromUTF8() );
1665  break;
1666 
1667  case 6:
1668  NextTok();
1669  aTitleBlock.SetComment( 5, FromUTF8() );
1670  break;
1671 
1672  case 7:
1673  NextTok();
1674  aTitleBlock.SetComment( 6, FromUTF8() );
1675  break;
1676 
1677  case 8:
1678  NextTok();
1679  aTitleBlock.SetComment( 7, FromUTF8() );
1680  break;
1681 
1682  case 9:
1683  NextTok();
1684  aTitleBlock.SetComment( 8, FromUTF8() );
1685  break;
1686 
1687  default:
1688  THROW_PARSE_ERROR( _( "Invalid title block comment number" ), CurSource(),
1689  CurLine(), CurLineNumber(), CurOffset() );
1690  }
1691 
1692  break;
1693  }
1694 
1695  default:
1696  Expecting( "title, date, rev, company, or comment" );
1697  }
1698 
1699  NeedRIGHT();
1700  }
1701 }
1702 
1703 
1705 {
1706  wxCHECK_MSG( CurTok() == T_property, nullptr,
1707  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1708  wxT( " as a property token." ) );
1709 
1710  T token = NextTok();
1711 
1712  if( !IsSymbol( token ) )
1713  {
1714  THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
1715  CurOffset() );
1716  }
1717 
1718  wxString name = FromUTF8();
1719 
1720  if( name.IsEmpty() )
1721  {
1722  THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
1723  CurOffset() );
1724  }
1725 
1726  token = NextTok();
1727 
1728  if( !IsSymbol( token ) )
1729  {
1730  THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
1731  CurOffset() );
1732  }
1733 
1734  // Empty property values are valid.
1735  wxString value = FromUTF8();
1736 
1737  std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( wxDefaultPosition, -1,
1738  aParent, name );
1739 
1740  field->SetText( value );
1741  field->SetVisible( true );
1742 
1743  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1744  {
1745  if( token != T_LEFT )
1746  Expecting( T_LEFT );
1747 
1748  token = NextTok();
1749 
1750  switch( token )
1751  {
1752  case T_id:
1753  field->SetId( parseInt( "field ID" ) );
1754  NeedRIGHT();
1755  break;
1756 
1757  case T_at:
1758  field->SetPosition( parseXY() );
1759  field->SetTextAngle( static_cast<int>( parseDouble( "text angle" ) * 10.0 ) );
1760  NeedRIGHT();
1761  break;
1762 
1763  case T_effects:
1764  parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ), field->GetId() == VALUE_FIELD );
1765  break;
1766 
1767  default:
1768  Expecting( "at or effects" );
1769  }
1770  }
1771 
1772  return field.release();
1773 }
1774 
1775 
1777 {
1778  wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
1779  wxCHECK_MSG( CurTok() == T_pin, nullptr,
1780  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1781  wxT( " as a sheet pin token." ) );
1782 
1783  T token = NextTok();
1784 
1785  if( !IsSymbol( token ) )
1786  {
1787  THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
1788  CurOffset() );
1789  }
1790 
1791  wxString name = FromUTF8();
1792 
1793  if( name.IsEmpty() )
1794  {
1795  THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
1796  CurOffset() );
1797  }
1798 
1799  auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, wxPoint( 0, 0 ), name );
1800 
1801  token = NextTok();
1802 
1803  switch( token )
1804  {
1805  case T_input: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT ); break;
1806  case T_output: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT ); break;
1807  case T_bidirectional: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI ); break;
1808  case T_tri_state: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE ); break;
1809  case T_passive: sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED ); break;
1810  default:
1811  Expecting( "input, output, bidirectional, tri_state, or passive" );
1812  }
1813 
1814  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1815  {
1816  if( token != T_LEFT )
1817  Expecting( T_LEFT );
1818 
1819  token = NextTok();
1820 
1821  switch( token )
1822  {
1823  case T_at:
1824  {
1825  sheetPin->SetPosition( parseXY() );
1826 
1827  double angle = parseDouble( "sheet pin angle (side)" );
1828 
1829  if( angle == 0.0 )
1830  sheetPin->SetEdge( SHEET_SIDE::RIGHT );
1831  else if( angle == 90.0 )
1832  sheetPin->SetEdge( SHEET_SIDE::TOP );
1833  else if( angle == 180.0 )
1834  sheetPin->SetEdge( SHEET_SIDE::LEFT );
1835  else if( angle == 270.0 )
1836  sheetPin->SetEdge( SHEET_SIDE::BOTTOM );
1837  else
1838  Expecting( "0, 90, 180, or 270" );
1839 
1840  NeedRIGHT();
1841  break;
1842  }
1843 
1844  case T_effects:
1845  parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
1846  break;
1847 
1848  case T_uuid:
1849  NeedSYMBOL();
1850  const_cast<KIID&>( sheetPin->m_Uuid ) = KIID( FromUTF8() );
1851  NeedRIGHT();
1852  break;
1853 
1854  default:
1855  Expecting( "at, uuid or effects" );
1856  }
1857  }
1858 
1859  return sheetPin.release();
1860 }
1861 
1862 
1864 {
1865  wxCHECK_RET( CurTok() == T_sheet_instances,
1866  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1867  wxT( " as a instances token." ) );
1868  wxCHECK( aScreen, /* void */ );
1869 
1870  T token;
1871 
1872  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1873  {
1874  if( token != T_LEFT )
1875  Expecting( T_LEFT );
1876 
1877  token = NextTok();
1878 
1879  switch( token )
1880  {
1881  case T_path:
1882  {
1883  NeedSYMBOL();
1884 
1885  SCH_SHEET_INSTANCE instance;
1886 
1887  instance.m_Path = KIID_PATH( FromUTF8() );
1888 
1889  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1890  {
1891  if( token != T_LEFT )
1892  Expecting( T_LEFT );
1893 
1894  token = NextTok();
1895 
1896  std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
1897  wxT( " " ) };
1898 
1899  size_t numReplacements = 0;
1900 
1901  switch( token )
1902  {
1903  case T_page:
1904  NeedSYMBOL();
1905  instance.m_PageNumber = FromUTF8();
1906 
1907  // Whitespaces are not permitted
1908  for( wxString ch : whitespaces )
1909  numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
1910 
1911 
1912  // Empty page numbers are not permitted
1913  if( instance.m_PageNumber.IsEmpty() )
1914  {
1915  // Use hash character instead
1916  instance.m_PageNumber = wxT( "#" );
1917  numReplacements++;
1918  }
1919 
1920  // Set the file as modified so the user can be warned.
1921  if( numReplacements > 0 )
1922  aScreen->SetContentModified();
1923 
1924  NeedRIGHT();
1925  break;
1926 
1927  default:
1928  Expecting( "path or page" );
1929  }
1930  }
1931 
1932  aScreen->m_sheetInstances.emplace_back( instance );
1933  break;
1934  }
1935 
1936  default:
1937  Expecting( "path" );
1938  }
1939  }
1940 
1941  // We don't store the root sheet's KIID, so pick it up from any sheet instance paths so
1942  // that it doesn't change on every round-trip.
1943  for( const SCH_SHEET_INSTANCE& instance : aScreen->m_sheetInstances )
1944  {
1945  if( instance.m_Path.size() > 0 )
1946  {
1947  const_cast<KIID&>( aRootSheet->m_Uuid ) = instance.m_Path[0];
1948  break;
1949  }
1950  }
1951 }
1952 
1953 
1955 {
1956  wxCHECK_RET( CurTok() == T_symbol_instances,
1957  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1958  wxT( " as a instances token." ) );
1959  wxCHECK( aScreen, /* void */ );
1960 
1961  T token;
1962 
1963  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1964  {
1965  if( token != T_LEFT )
1966  Expecting( T_LEFT );
1967 
1968  token = NextTok();
1969 
1970  switch( token )
1971  {
1972  case T_path:
1973  {
1974  NeedSYMBOL();
1975 
1976  SYMBOL_INSTANCE_REFERENCE instance;
1977 
1978  instance.m_Path = KIID_PATH( FromUTF8() );
1979 
1980  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1981  {
1982  if( token != T_LEFT )
1983  Expecting( T_LEFT );
1984 
1985  token = NextTok();
1986 
1987  switch( token )
1988  {
1989  case T_reference:
1990  NeedSYMBOL();
1991  instance.m_Reference = FromUTF8();
1992  NeedRIGHT();
1993  break;
1994 
1995  case T_unit:
1996  instance.m_Unit = parseInt( "symbol unit" );
1997  NeedRIGHT();
1998  break;
1999 
2000  case T_value:
2001  NeedSYMBOL();
2002  instance.m_Value = FromUTF8();
2003  NeedRIGHT();
2004  break;
2005 
2006  case T_footprint:
2007  NeedSYMBOL();
2008  instance.m_Footprint = FromUTF8();
2009  NeedRIGHT();
2010  break;
2011 
2012  default:
2013  Expecting( "path, unit, value or footprint" );
2014  }
2015  }
2016 
2017  aScreen->m_symbolInstances.emplace_back( instance );
2018  break;
2019  }
2020 
2021  default:
2022  Expecting( "path" );
2023  }
2024  }
2025 }
2026 
2027 
2028 void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly, int aFileVersion )
2029 {
2030  wxCHECK( aSheet != nullptr, /* void */ );
2031 
2032  SCH_SCREEN* screen = aSheet->GetScreen();
2033 
2034  wxCHECK( screen != nullptr, /* void */ );
2035 
2036  if( aIsCopyableOnly )
2037  m_requiredVersion = aFileVersion;
2038 
2039  T token;
2040 
2041  if( !aIsCopyableOnly )
2042  {
2043  NeedLEFT();
2044  NextTok();
2045 
2046  if( CurTok() != T_kicad_sch )
2047  Expecting( "kicad_sch" );
2048 
2049  parseHeader( T_kicad_sch, SEXPR_SCHEMATIC_FILE_VERSION );
2050  }
2051 
2053 
2054  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2055  {
2056  if( aIsCopyableOnly && token == T_EOF )
2057  break;
2058 
2059  if( token != T_LEFT )
2060  Expecting( T_LEFT );
2061 
2062  token = NextTok();
2063 
2064  checkpoint();
2065 
2066  if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
2067  token = T_paper;
2068 
2069  switch( token )
2070  {
2071  case T_uuid:
2072  NeedSYMBOL();
2073  screen->m_uuid = KIID( FromUTF8() );
2074  NeedRIGHT();
2075  break;
2076 
2077  case T_paper:
2078  {
2079  if( aIsCopyableOnly )
2080  Unexpected( T_paper );
2081 
2082  PAGE_INFO pageInfo;
2083  parsePAGE_INFO( pageInfo );
2084  screen->SetPageSettings( pageInfo );
2085  break;
2086  }
2087 
2088  case T_page:
2089  {
2090  if( aIsCopyableOnly )
2091  Unexpected( T_page );
2092 
2093  // Only saved for top-level sniffing in Kicad Manager frame and other external
2094  // tool usage with flat hierarchies
2095  NeedSYMBOLorNUMBER();
2096  NeedSYMBOLorNUMBER();
2097  NeedRIGHT();
2098  break;
2099  }
2100 
2101  case T_title_block:
2102  {
2103  if( aIsCopyableOnly )
2104  Unexpected( T_title_block );
2105 
2106  TITLE_BLOCK tb;
2107  parseTITLE_BLOCK( tb );
2108  screen->SetTitleBlock( tb );
2109  break;
2110  }
2111 
2112  case T_lib_symbols:
2113  {
2114  // Dummy map. No derived symbols are allowed in the library cache.
2115  LIB_SYMBOL_MAP symbolLibMap;
2116 
2117  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2118  {
2119  if( token != T_LEFT )
2120  Expecting( T_LEFT );
2121 
2122  token = NextTok();
2123 
2124  switch( token )
2125  {
2126  case T_symbol:
2127  screen->AddLibSymbol( ParseSymbol( symbolLibMap, m_requiredVersion ) );
2128  break;
2129 
2130  default:
2131  Expecting( "symbol" );
2132  }
2133  }
2134 
2135  break;
2136  }
2137 
2138  case T_symbol:
2139  screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
2140  break;
2141 
2142  case T_image:
2143  screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
2144  break;
2145 
2146  case T_sheet:
2147  {
2148  SCH_SHEET* sheet = parseSheet();
2149 
2150  // Set the parent to aSheet. This effectively creates a method to find
2151  // the root sheet from any sheet so a pointer to the root sheet does not
2152  // need to be stored globally. Note: this is not the same as a hierarchy.
2153  // Complex hierarchies can have multiple copies of a sheet. This only
2154  // provides a simple tree to find the root sheet.
2155  sheet->SetParent( aSheet );
2156  screen->Append( static_cast<SCH_ITEM*>( sheet ) );
2157  break;
2158  }
2159 
2160  case T_junction:
2161  screen->Append( static_cast<SCH_ITEM*>( parseJunction() ) );
2162  break;
2163 
2164  case T_no_connect:
2165  screen->Append( static_cast<SCH_ITEM*>( parseNoConnect() ) );
2166  break;
2167 
2168  case T_bus_entry:
2169  screen->Append( static_cast<SCH_ITEM*>( parseBusEntry() ) );
2170  break;
2171 
2172  case T_polyline:
2173  case T_bus:
2174  case T_wire:
2175  screen->Append( static_cast<SCH_ITEM*>( parseLine() ) );
2176  break;
2177 
2178  case T_text:
2179  case T_label:
2180  case T_global_label:
2181  case T_hierarchical_label:
2182  screen->Append( static_cast<SCH_ITEM*>( parseSchText() ) );
2183  break;
2184 
2185  case T_sheet_instances:
2186  parseSchSheetInstances( aSheet, screen );
2187  break;
2188 
2189  case T_symbol_instances:
2190  parseSchSymbolInstances( screen );
2191  break;
2192 
2193  case T_bus_alias:
2194  if( aIsCopyableOnly )
2195  Unexpected( T_bus_alias );
2196 
2197  parseBusAlias( screen );
2198  break;
2199 
2200  default:
2201  Expecting( "symbol, paper, page, title_block, bitmap, sheet, junction, no_connect, "
2202  "bus_entry, line, bus, text, label, global_label, hierarchical_label, "
2203  "symbol_instances, or bus_alias" );
2204  }
2205  }
2206 
2207  screen->UpdateLocalLibSymbolLinks();
2208 }
2209 
2210 
2212 {
2213  wxCHECK_MSG( CurTok() == T_symbol, nullptr,
2214  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
2215 
2216  T token;
2217  wxString tmp;
2218  wxString libName;
2219  SCH_FIELD* field;
2220  std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
2221  TRANSFORM transform;
2222  std::set<int> fieldIDsRead;
2223 
2224  // We'll reset this if we find a fields_autoplaced token
2225  symbol->ClearFieldsAutoplaced();
2226 
2228 
2229  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2230  {
2231  if( token != T_LEFT )
2232  Expecting( T_LEFT );
2233 
2234  token = NextTok();
2235 
2236  switch( token )
2237  {
2238  case T_lib_name:
2239  {
2240  LIB_ID libId;
2241 
2242  token = NextTok();
2243 
2244  if( !IsSymbol( token ) )
2245  {
2246  THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
2247  CurLineNumber(), CurOffset() );
2248  }
2249 
2250  libName = FromUTF8();
2251  NeedRIGHT();
2252  break;
2253  }
2254 
2255  case T_lib_id:
2256  {
2257  token = NextTok();
2258 
2259  if( !IsSymbol( token ) && token != T_NUMBER )
2260  Expecting( "symbol|number" );
2261 
2262  LIB_ID libId;
2263 
2264  if( libId.Parse( FromUTF8() ) >= 0 )
2265  {
2266  THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
2267  CurLineNumber(), CurOffset() );
2268  }
2269 
2270  symbol->SetLibId( libId );
2271  NeedRIGHT();
2272  break;
2273  }
2274 
2275  case T_at:
2276  symbol->SetPosition( parseXY() );
2277 
2278  switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
2279  {
2280  case 0: transform = TRANSFORM(); break;
2281  case 90: transform = TRANSFORM( 0, -1, -1, 0 ); break;
2282  case 180: transform = TRANSFORM( -1, 0, 0, 1 ); break;
2283  case 270: transform = TRANSFORM( 0, 1, 1, 0 ); break;
2284  default: Expecting( "0, 90, 180, or 270" );
2285  }
2286 
2287  symbol->SetTransform( transform );
2288  NeedRIGHT();
2289  break;
2290 
2291  case T_mirror:
2292  token = NextTok();
2293 
2294  if( token == T_x )
2295  symbol->SetOrientation( SYM_MIRROR_X );
2296  else if( token == T_y )
2297  symbol->SetOrientation( SYM_MIRROR_Y );
2298  else
2299  Expecting( "x or y" );
2300 
2301  NeedRIGHT();
2302  break;
2303 
2304  case T_unit:
2305  symbol->SetUnit( parseInt( "symbol unit" ) );
2306  NeedRIGHT();
2307  break;
2308 
2309  case T_convert:
2310  symbol->SetConvert( parseInt( "symbol convert" ) );
2311  NeedRIGHT();
2312  break;
2313 
2314  case T_in_bom:
2315  symbol->SetIncludeInBom( parseBool() );
2316  NeedRIGHT();
2317  break;
2318 
2319  case T_on_board:
2320  symbol->SetIncludeOnBoard( parseBool() );
2321  NeedRIGHT();
2322  break;
2323 
2324  case T_fields_autoplaced:
2325  symbol->SetFieldsAutoplaced();
2326  NeedRIGHT();
2327  break;
2328 
2329  case T_uuid:
2330  NeedSYMBOL();
2331  const_cast<KIID&>( symbol->m_Uuid ) = KIID( FromUTF8() );
2332  NeedRIGHT();
2333  break;
2334 
2335  case T_property:
2336  // The field parent symbol must be set and its orientation must be set before
2337  // the field positions are set.
2338  field = parseSchField( symbol.get() );
2339 
2340  // It would appear that at some point we allowed duplicate ids to slip through
2341  // when writing files. The easiest (and most complete) solution is to disallow
2342  // multiple instances of the same id (for all files since the source of the error
2343  // *might* in fact be hand-edited files).
2344  //
2345  // While no longer used, -1 is still a valid id for user field. It gets converted
2346  // to the next unused number on save.
2347  if( fieldIDsRead.count( field->GetId() ) )
2348  field->SetId( -1 );
2349  else
2350  fieldIDsRead.insert( field->GetId() );
2351 
2352  // Set the default symbol reference prefix.
2353  if( field->GetId() == REFERENCE_FIELD )
2354  {
2355  wxString refDesignator = field->GetText();
2356 
2357  refDesignator.Replace( "~", " " );
2358 
2359  wxString prefix = refDesignator;
2360 
2361  while( prefix.Length() )
2362  {
2363  if( ( prefix.Last() < '0' || prefix.Last() > '9') && prefix.Last() != '?' )
2364  break;
2365 
2366  prefix.RemoveLast();
2367  }
2368 
2369  // Avoid a prefix containing trailing/leading spaces
2370  prefix.Trim( true );
2371  prefix.Trim( false );
2372 
2373  if( prefix.IsEmpty() )
2374  symbol->SetPrefix( wxString( "U" ) );
2375  else
2376  symbol->SetPrefix( prefix );
2377  }
2378 
2379  if( symbol->GetFieldById( field->GetId() ) )
2380  *symbol->GetFieldById( field->GetId() ) = *field;
2381  else
2382  symbol->AddField( *field );
2383 
2384  delete field;
2385  break;
2386 
2387  case T_pin:
2388  {
2389  // Read an alternate pin designation
2390  wxString number;
2391  KIID uuid;
2392  wxString alt;
2393 
2394  NeedSYMBOL();
2395  number = FromUTF8();
2396 
2397  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2398  {
2399  if( token != T_LEFT )
2400  Expecting( T_LEFT );
2401 
2402  token = NextTok();
2403 
2404  switch( token )
2405  {
2406  case T_alternate:
2407  NeedSYMBOL();
2408  alt = FromUTF8();
2409  NeedRIGHT();
2410  break;
2411 
2412  case T_uuid:
2413  NeedSYMBOL();
2414 
2415  // First version to write out pin uuids accidentally wrote out the symbol's
2416  // uuid for each pin, so ignore uuids coming from that version.
2417  if( m_requiredVersion >= 20210126 )
2418  uuid = KIID( FromUTF8() );
2419 
2420  NeedRIGHT();
2421  break;
2422 
2423  default:
2424  Expecting( "alternate or uuid" );
2425  }
2426  }
2427 
2428  symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(),
2429  number, alt ) );
2430 
2431  const_cast<KIID&>( symbol->GetRawPins().back()->m_Uuid ) = uuid;
2432  }
2433  break;
2434 
2435  default:
2436  Expecting( "lib_id, lib_name, at, mirror, uuid, property, pin, or instances" );
2437  }
2438  }
2439 
2440  if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
2441  symbol->SetSchSymbolLibraryName( libName );
2442 
2443  // Ensure edit/status flags are cleared after these initializations:
2444  symbol->ClearFlags();
2445 
2446  return symbol.release();
2447 }
2448 
2449 
2451 {
2452  wxCHECK_MSG( CurTok() == T_image, nullptr,
2453  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
2454 
2455  T token;
2456  std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
2457 
2458  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2459  {
2460  if( token != T_LEFT )
2461  Expecting( T_LEFT );
2462 
2463  token = NextTok();
2464 
2465  switch( token )
2466  {
2467  case T_at:
2468  bitmap->SetPosition( parseXY() );
2469  NeedRIGHT();
2470  break;
2471 
2472  case T_scale:
2473  bitmap->GetImage()->SetScale( parseDouble( "image scale factor" ) );
2474 
2475  if( !std::isnormal( bitmap->GetImage()->GetScale() ) )
2476  bitmap->GetImage()->SetScale( 1.0 );
2477 
2478  NeedRIGHT();
2479  break;
2480 
2481  case T_uuid:
2482  NeedSYMBOL();
2483  const_cast<KIID&>( bitmap->m_Uuid ) = KIID( FromUTF8() );
2484  NeedRIGHT();
2485  break;
2486 
2487  case T_data:
2488  {
2489  token = NextTok();
2490 
2491  wxString data;
2492 
2493  // Reserve 128K because most image files are going to be larger than the default
2494  // 1K that wxString reserves.
2495  data.reserve( 1 << 17 );
2496 
2497  while( token != T_RIGHT )
2498  {
2499  if( !IsSymbol( token ) )
2500  Expecting( "base64 image data" );
2501 
2502  data += FromUTF8();
2503  token = NextTok();
2504  }
2505 
2506  wxMemoryBuffer buffer = wxBase64Decode( data );
2507  wxMemoryOutputStream stream( buffer.GetData(), buffer.GetBufSize() );
2508  wxImage* image = new wxImage();
2509  wxMemoryInputStream istream( stream );
2510  image->LoadFile( istream, wxBITMAP_TYPE_PNG );
2511  bitmap->GetImage()->SetImage( image );
2512  bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
2513  break;
2514  }
2515 
2516  default:
2517  Expecting( "at, scale, uuid or data" );
2518  }
2519  }
2520 
2521  return bitmap.release();
2522 }
2523 
2524 
2526 {
2527  wxCHECK_MSG( CurTok() == T_sheet, nullptr,
2528  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
2529 
2530  T token;
2531  STROKE_PARAMS stroke;
2532  FILL_PARAMS fill;
2533  SCH_FIELD* field;
2534  std::vector<SCH_FIELD> fields;
2535  std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
2536  std::set<int> fieldIDsRead;
2537 
2538  // We'll reset this if we find a fields_autoplaced token
2539  sheet->ClearFieldsAutoplaced();
2540 
2541  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2542  {
2543  if( token != T_LEFT )
2544  Expecting( T_LEFT );
2545 
2546  token = NextTok();
2547 
2548  switch( token )
2549  {
2550  case T_at:
2551  sheet->SetPosition( parseXY() );
2552  NeedRIGHT();
2553  break;
2554 
2555  case T_size:
2556  {
2557  wxSize size;
2558  size.SetWidth( parseInternalUnits( "sheet width" ) );
2559  size.SetHeight( parseInternalUnits( "sheet height" ) );
2560  sheet->SetSize( size );
2561  NeedRIGHT();
2562  break;
2563  }
2564 
2565  case T_fields_autoplaced:
2566  sheet->SetFieldsAutoplaced();
2567  NeedRIGHT();
2568  break;
2569 
2570  case T_stroke:
2571  parseStroke( stroke );
2572  sheet->SetBorderWidth( stroke.GetWidth() );
2573  sheet->SetBorderColor( stroke.GetColor() );
2574  break;
2575 
2576  case T_fill:
2577  parseFill( fill );
2578  sheet->SetBackgroundColor( fill.m_Color );
2579  break;
2580 
2581  case T_uuid:
2582  NeedSYMBOL();
2583  const_cast<KIID&>( sheet->m_Uuid ) = KIID( FromUTF8() );
2584  NeedRIGHT();
2585  break;
2586 
2587  case T_property:
2588  field = parseSchField( sheet.get() );
2589 
2590  if( m_requiredVersion <= 20200310 )
2591  {
2592  // Earlier versions had the wrong ids (and names) saved for sheet fields.
2593  // Fortunately they only saved the sheetname and sheetfilepath (and always
2594  // in that order), so we can hack in a recovery.
2595  if( fields.empty() )
2596  field->SetId( SHEETNAME );
2597  else
2598  field->SetId( SHEETFILENAME );
2599  }
2600 
2601  // It would appear the problem persists past 20200310, but this time with the
2602  // earlier ids being re-used for later (user) fields. The easiest (and most
2603  // complete) solution is to disallow multiple instances of the same id (for all
2604  // files since the source of the error *might* in fact be hand-edited files).
2605  //
2606  // While no longer used, -1 is still a valid id for user field. It gets converted
2607  // to the next unused number on save.
2608  if( fieldIDsRead.count( field->GetId() ) )
2609  field->SetId( -1 );
2610  else
2611  fieldIDsRead.insert( field->GetId() );
2612 
2613  fields.emplace_back( *field );
2614  delete field;
2615  break;
2616 
2617  case T_pin:
2618  sheet->AddPin( parseSchSheetPin( sheet.get() ) );
2619  break;
2620 
2621  default:
2622  Expecting( "at, size, stroke, background, uuid, property, or pin" );
2623  }
2624  }
2625 
2626  sheet->SetFields( fields );
2627 
2628  return sheet.release();
2629 }
2630 
2631 
2633 {
2634  wxCHECK_MSG( CurTok() == T_junction, nullptr,
2635  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
2636 
2637  T token;
2638  std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
2639 
2640  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2641  {
2642  if( token != T_LEFT )
2643  Expecting( T_LEFT );
2644 
2645  token = NextTok();
2646 
2647  switch( token )
2648  {
2649  case T_at:
2650  junction->SetPosition( parseXY() );
2651  NeedRIGHT();
2652  break;
2653 
2654  case T_diameter:
2655  junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
2656  NeedRIGHT();
2657  break;
2658 
2659  case T_color:
2660  {
2661  COLOR4D color;
2662 
2663  color.r = parseInt( "red" ) / 255.0;
2664  color.g = parseInt( "green" ) / 255.0;
2665  color.b = parseInt( "blue" ) / 255.0;
2666  color.a = Clamp( parseDouble( "alpha" ), 0.0, 1.0 );
2667 
2668  junction->SetColor( color );
2669  NeedRIGHT();
2670  break;
2671  }
2672 
2673  default:
2674  Expecting( "at" );
2675  }
2676  }
2677 
2678  return junction.release();
2679 }
2680 
2681 
2683 {
2684  wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
2685  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
2686 
2687  T token;
2688  std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
2689 
2690  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2691  {
2692  if( token != T_LEFT )
2693  Expecting( T_LEFT );
2694 
2695  token = NextTok();
2696 
2697  switch( token )
2698  {
2699  case T_at:
2700  no_connect->SetPosition( parseXY() );
2701  NeedRIGHT();
2702  break;
2703 
2704  case T_uuid:
2705  NeedSYMBOL();
2706  const_cast<KIID&>( no_connect->m_Uuid ) = KIID( FromUTF8() );
2707  NeedRIGHT();
2708  break;
2709 
2710  default:
2711  Expecting( "at or uuid" );
2712  }
2713  }
2714 
2715  return no_connect.release();
2716 }
2717 
2718 
2720 {
2721  wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
2722  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
2723 
2724  T token;
2725  STROKE_PARAMS stroke;
2726  std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
2727 
2728  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2729  {
2730  if( token != T_LEFT )
2731  Expecting( T_LEFT );
2732 
2733  token = NextTok();
2734 
2735  switch( token )
2736  {
2737  case T_at:
2738  busEntry->SetPosition( parseXY() );
2739  NeedRIGHT();
2740  break;
2741 
2742  case T_size:
2743  {
2744  wxSize size;
2745 
2746  size.SetWidth( parseInternalUnits( "bus entry height" ) );
2747  size.SetHeight( parseInternalUnits( "bus entry width" ) );
2748  busEntry->SetSize( size );
2749  NeedRIGHT();
2750  break;
2751  }
2752 
2753  case T_stroke:
2754  parseStroke( stroke );
2755  busEntry->SetStroke( stroke );
2756  break;
2757 
2758  case T_uuid:
2759  NeedSYMBOL();
2760  const_cast<KIID&>( busEntry->m_Uuid ) = KIID( FromUTF8() );
2761  NeedRIGHT();
2762  break;
2763 
2764  default:
2765  Expecting( "at, size, uuid or stroke" );
2766  }
2767  }
2768 
2769  return busEntry.release();
2770 }
2771 
2772 
2774 {
2775  T token;
2776  STROKE_PARAMS stroke;
2777  std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>();
2778 
2779  switch( CurTok() )
2780  {
2781  case T_polyline: line->SetLayer( LAYER_NOTES ); break;
2782  case T_wire: line->SetLayer( LAYER_WIRE ); break;
2783  case T_bus: line->SetLayer( LAYER_BUS ); break;
2784  default:
2785  wxCHECK_MSG( false, nullptr,
2786  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a line." ) );
2787  }
2788 
2789  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2790  {
2791  if( token != T_LEFT )
2792  Expecting( T_LEFT );
2793 
2794  token = NextTok();
2795 
2796  switch( token )
2797  {
2798  case T_pts:
2799  NeedLEFT();
2800  token = NextTok();
2801 
2802  if( token != T_xy )
2803  Expecting( "xy" );
2804 
2805  line->SetStartPoint( parseXY() );
2806  NeedRIGHT();
2807  NeedLEFT();
2808  token = NextTok();
2809 
2810  if( token != T_xy )
2811  Expecting( "xy" );
2812 
2813  line->SetEndPoint( parseXY() );
2814  NeedRIGHT();
2815  NeedRIGHT();
2816  break;
2817 
2818  case T_stroke:
2819  parseStroke( stroke );
2820  line->SetStroke( stroke );
2821  break;
2822 
2823  case T_uuid:
2824  NeedSYMBOL();
2825  const_cast<KIID&>( line->m_Uuid ) = KIID( FromUTF8() );
2826  NeedRIGHT();
2827  break;
2828 
2829  default:
2830  Expecting( "at, uuid or stroke" );
2831  }
2832  }
2833 
2834  return line.release();
2835 }
2836 
2837 
2839 {
2840  T token;
2841  std::unique_ptr<SCH_TEXT> text;
2842 
2843  switch( CurTok() )
2844  {
2845  case T_text: text = std::make_unique<SCH_TEXT>(); break;
2846  case T_label: text = std::make_unique<SCH_LABEL>(); break;
2847  case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
2848  case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
2849  default:
2850  wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
2851  }
2852 
2853  // We'll reset this if we find a fields_autoplaced token
2854  text->ClearFieldsAutoplaced();
2855 
2856  NeedSYMBOL();
2857 
2858  text->SetText( FromUTF8() );
2859 
2860  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2861  {
2862  if( token != T_LEFT )
2863  Expecting( T_LEFT );
2864 
2865  token = NextTok();
2866 
2867  switch( token )
2868  {
2869  case T_at:
2870  text->SetPosition( parseXY() );
2871 
2872  switch( static_cast<int>( parseDouble( "text angle" ) ) )
2873  {
2874  case 0: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT ); break;
2875  case 90: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::UP ); break;
2876  case 180: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT ); break;
2877  case 270: text->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM ); break;
2878  default:
2879  wxFAIL;
2880  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::RIGHT );
2881  break;
2882  }
2883 
2884  NeedRIGHT();
2885  break;
2886 
2887  case T_shape:
2888  if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
2889  Unexpected( T_shape );
2890 
2891  token = NextTok();
2892 
2893  switch( token )
2894  {
2895  case T_input: text->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT ); break;
2896  case T_output: text->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT ); break;
2897  case T_bidirectional: text->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI ); break;
2898  case T_tri_state: text->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE ); break;
2899  case T_passive: text->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED ); break;
2900  default:
2901  Expecting( "input, output, bidirectional, tri_state, or passive" );
2902  }
2903 
2904  NeedRIGHT();
2905  break;
2906 
2907  case T_fields_autoplaced:
2908  text->SetFieldsAutoplaced();
2909  NeedRIGHT();
2910  break;
2911 
2912  case T_effects:
2913  parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
2914 
2915  // Spin style is defined differently for graphical text (#SCH_TEXT) objects.
2916  if( text->Type() == SCH_TEXT_T )
2917  {
2918  if( text->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT
2919  && text->GetTextAngle() == TEXT_ANGLE_VERT )
2920  {
2921  // The vertically aligned text angle is always 90 (labels use 270 for the
2922  // down direction) combined with the text justification flags.
2923  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::BOTTOM );
2924  }
2925  else if( text->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT
2926  && text->GetTextAngle() == TEXT_ANGLE_HORIZ )
2927  {
2928  // The horizontally aligned text angle is always 0 (labels use 180 for the
2929  // left direction) combined with the text justification flags.
2930  text->SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
2931  }
2932  }
2933 
2934  break;
2935 
2936  case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
2937  if( text->Type() == SCH_GLOBAL_LABEL_T )
2938  {
2939  SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
2940  SCH_FIELD* field = label->GetIntersheetRefs();
2941 
2942  field->SetTextPos( parseXY() );
2943  NeedRIGHT();
2944 
2945  field->SetVisible( true );
2946  }
2947  break;
2948 
2949  case T_uuid:
2950  NeedSYMBOL();
2951  const_cast<KIID&>( text->m_Uuid ) = KIID( FromUTF8() );
2952  NeedRIGHT();
2953  break;
2954 
2955  case T_property:
2956  if( text->Type() == SCH_GLOBAL_LABEL_T )
2957  {
2958  SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
2959  SCH_FIELD* field = parseSchField( label );
2960 
2961  field->SetLayer( LAYER_GLOBLABEL );
2962  label->SetIntersheetRefs( *field );
2963 
2964  delete field;
2965  }
2966  break;
2967 
2968  default:
2969  Expecting( "at, shape, iref, uuid or effects" );
2970  }
2971  }
2972 
2973  return text.release();
2974 }
2975 
2976 
2978 {
2979  wxCHECK_RET( CurTok() == T_bus_alias,
2980  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
2981  wxCHECK( aScreen, /* void */ );
2982 
2983  T token;
2984  std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>( aScreen );
2985  wxString alias;
2986  wxString member;
2987 
2988  NeedSYMBOL();
2989 
2990  alias = FromUTF8();
2991 
2992  if( m_requiredVersion < 20210621 )
2993  alias = ConvertToNewOverbarNotation( alias );
2994 
2995  busAlias->SetName( alias );
2996 
2997  NeedLEFT();
2998  token = NextTok();
2999 
3000  if( token != T_members )
3001  Expecting( "members" );
3002 
3003  token = NextTok();
3004 
3005  while( token != T_RIGHT )
3006  {
3007  if( !IsSymbol( token ) )
3008  Expecting( "quoted string" );
3009 
3010  member = FromUTF8();
3011 
3012  if( m_requiredVersion < 20210621 )
3013  member = ConvertToNewOverbarNotation( member );
3014 
3015  busAlias->AddMember( member );
3016 
3017  token = NextTok();
3018  }
3019 
3020  NeedRIGHT();
3021 
3022  aScreen->AddBusAlias( busAlias );
3023 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:188
Field Reference of part, i.e. "IC21".
int m_fieldId
The current field ID.
power input (GND, VCC for ICs). Must be connected to a power output.
#define TEXT_ANGLE_HORIZ
Frequent text rotations, used with {Set,Get}TextAngle(), in 0.1 degrees for now, hoping to migrate to...
Definition: eda_text.h:50
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
LIB_FIELD * parseProperty(std::unique_ptr< LIB_SYMBOL > &aSymbol)
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
Symbol map used by symbol library object.
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:80
void SetWidth(int aWidth)
Definition: sch_item.h:168
Schematic and symbol library s-expression file format parser definitions.
std::vector< SYMBOL_INSTANCE_REFERENCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition: sch_screen.h:529
void SetRevision(const wxString &aRevision)
Definition: title_block.h:81
wxString GetName() const override
Definition: lib_symbol.h:133
Define a symbol library graphical text item.
Definition: lib_text.h:39
SCH_SHEET_PIN * parseSchSheetPin(SCH_SHEET *aSheet)
static constexpr double IU_PER_MM
Mock up a conversion function.
A progress reporter for use in multi-threaded environments.
void parseBusAlias(SCH_SCREEN *aScreen)
void parseEDA_TEXT(EDA_TEXT *aText, bool aConvertOverbarSyntax)
void SetItalic(bool isItalic)
Definition: eda_text.h:179
int color
Definition: DXF_plotter.cpp:60
LIB_POLYLINE * parsePolyLine()
Field object used in symbol libraries.
Definition: lib_field.h:59
unsigned m_lastProgressLine
pin for passive symbols: must be connected, and can be connected to any pin
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:246
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:119
LIB_SYMBOL * ParseSymbol(LIB_SYMBOL_MAP &aSymbolLibMap, int aFileVersion=SEXPR_SYMBOL_LIB_FILE_VERSION)
int GetId() const
Definition: sch_field.h:113
int m_unit
The current unit being parsed.
void parseTITLE_BLOCK(TITLE_BLOCK &aTitleBlock)
unknown electrical properties: creates always a warning when connected
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition: title_block.h:71
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: sch_screen.h:125
void SetPlotStyle(PLOT_DASH_TYPE aPlotStyle)
Definition: sch_item.h:171
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:77
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:237
Definition: lib_pin.h:48
int GetId() const
Definition: lib_field.h:115
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:288
SCH_SYMBOL * parseSchematicSymbol()
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
GRAPHIC_PINSHAPE
Definition: pin_type.h:55
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:103
Define a library symbol object.
Definition: lib_symbol.h:96
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:40
ELECTRICAL_PINTYPE m_Type
Definition: lib_pin.h:60
void parseSchSheetInstances(SCH_SHEET *aRootSheet, SCH_SCREEN *aScreen)
void parseSchSymbolInstances(SCH_SCREEN *aScreen)
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:116
SCH_BUS_WIRE_ENTRY * parseBusEntry()
LIB_RECTANGLE * parseRectangle()
The base class for drawable items used by schematic library symbols.
Definition: lib_item.h:61
int Mm2mils(double x)
Convert mm to mils.
Definition: base_units.cpp:56
FILL_TYPE m_FillType
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:119
not internally connected (may be connected to anything)
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:185
Definition: kiid.h:44
virtual void SetCurrentProgress(double aProgress)
Set the progress value to aProgress (0..1).
Simple container to manage fill parameters.
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
Definition: sch_screen.cpp:693
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:45
void parseHeader(TSCHEMATIC_T::T aHeaderType, int aFileVersion)
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
void ParseLib(LIB_SYMBOL_MAP &aSymbolLibMap)
Field Value of part, i.e. "3.3K".
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
wxString m_symbolName
The current symbol name.
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition: richio.h:135
SCH_JUNCTION * parseJunction()
int m_convert
The current body style being parsed.
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition: sch_screen.h:530
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:53
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:202
SCH_NO_CONNECT * parseNoConnect()
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
Definition: lib_field.cpp:372
LIB_BEZIER * parseBezier()
void SetIntersheetRefs(const SCH_FIELD &aField)
Definition: sch_text.h:404
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
wxString m_Name
Definition: lib_pin.h:58
A simple container for schematic symbol instance information.
#define _(s)
const LINE_READER * m_lineReader
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:65
int GetWidth() const
Definition: sch_item.h:167
void SetFileFormatVersionAtLoad(int aVersion)
Definition: sch_screen.h:121
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: sch_screen.h:144
void SetTitle(const wxString &aTitle)
Definition: title_block.h:58
#define DEFAULT_LINE_THICKNESS
The default wire width in mils. (can be changed in preference menu)
Object to handle a bitmap image that can be inserted in a schematic.
Definition: sch_bitmap.h:40
void SetHeightMils(int aHeightInMils)
Definition: page_info.cpp:257
SCH_SEXPR_PARSER(LINE_READER *aLineReader=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, unsigned aLineCount=0)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
const KIID m_Uuid
Definition: eda_item.h:475
void SetContentModified(bool aModified=true)
Definition: base_screen.h:59
The first 4 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
SCH_FIELD * GetIntersheetRefs()
Definition: sch_text.h:403
void AddLibSymbol(LIB_SYMBOL *aLibSymbol)
Add aLibSymbol to the library symbol map.
LIB_ITEM * ParseDrawItem()
COLOR4D GetColor() const
Definition: sch_item.h:173
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
A simple container for sheet instance information.
void parsePAGE_INFO(PAGE_INFO &aPageInfo)
Schematic symbol object.
Definition: sch_symbol.h:78
int m_requiredVersion
Set to the symbol library file version required.
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition (and transfers ownership of the pointer).
const char * name
Definition: DXF_plotter.cpp:59
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:35
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:144
SCH_BITMAP * parseImage()
void parsePinNames(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:201
Simple container to manage line stroke parameters.
Definition: sch_item.h:155
usual pin input: must be connected
KIID m_uuid
A unique identifier for each schematic file.
Definition: sch_screen.h:538
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetWidthMils(int aWidthInMils)
Definition: page_info.cpp:243
SCH_FIELD * parseSchField(SCH_ITEM *aParent)
bool KeepRefreshing(bool aWait=false)
Update the UI dialog.
LIB_CIRCLE * parseCircle()
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
const T & Clamp(const T &lower, const T &value, const T &upper)
Limit value within the range lower <= value <= upper.
Definition: util.h:52
void parseFill(FILL_PARAMS &aFill)
void ParseSchematic(SCH_SHEET *aSheet, bool aIsCopyablyOnly=false, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Parse the internal LINE_READER object into aSheet.
Class for a wire to bus entry.
SCH_TEXT * parseSchText()
void SetColor(const COLOR4D &aColor)
Definition: sch_item.h:174
input or output (like port for a microprocessor)
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:159
double parseDouble()
Parse the current token as an ASCII numeric string with possible leading whitespace into a double pre...
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
Definition: string.cpp:44
#define TEXT_ANGLE_VERT
Definition: eda_text.h:51
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:174
void SetId(int aId)
Definition: sch_field.cpp:81
const VECTOR2I GetArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:450
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
not connected (must be left open)
void SetBold(bool aBold)
Definition: eda_text.h:182
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:186
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:197
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void SetId(int aId)
Definition: lib_field.h:116
output of a regulator: intended to be connected to power input pins
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
GRAPHIC_PINSHAPE m_Shape
Definition: lib_pin.h:59
void parseStroke(STROKE_PARAMS &aStroke)
Parse stroke definition aStroke.
PROGRESS_REPORTER * m_progressReporter
Define a bezier curve graphic body item.
Definition: lib_bezier.h:34
SCH_SHEET * parseSheet()
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103