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