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