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