KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_kicad_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 The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Wayne Stambaugh <[email protected]>
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
26
27// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
28// base64 code.
29#include <charconv>
30
31#include <fmt/format.h>
32#define wxUSE_BASE64 1
33#include <wx/base64.h>
34#include <wx/log.h>
35#include <wx/mstream.h>
36#include <wx/tokenzr.h>
37
38#include <base_units.h>
39#include <bitmap_base.h>
41#include <lib_id.h>
42#include <sch_pin.h>
43#include <math/util.h> // KiROUND
44#include <font/font.h>
45#include <font/fontconfig.h>
46#include <pgm_base.h>
47#include <string_utils.h>
48#include <sch_bitmap.h>
49#include <sch_bus_entry.h>
50#include <sch_symbol.h>
51#include <sch_edit_frame.h> // SYM_ORIENT_XXX
52#include <sch_field.h>
53#include <sch_group.h>
54#include <sch_line.h>
55#include <sch_rule_area.h>
56#include <sch_textbox.h>
57#include <sch_table.h>
58#include <sch_tablecell.h>
59#include <sch_label.h>
60#include <sch_junction.h>
61#include <sch_no_connect.h>
62#include <sch_screen.h>
63#include <sch_shape.h>
64#include <sch_sheet_pin.h>
67#include <template_fieldnames.h>
68#include <trigo.h>
69#include <progress_reporter.h>
70#include <sim/sim_model.h>
71
72
73using namespace TSCHEMATIC_T;
74
75
77 PROGRESS_REPORTER* aProgressReporter,
78 unsigned aLineCount, SCH_SHEET* aRootSheet,
79 bool aIsAppending ) :
80 SCHEMATIC_LEXER( aLineReader ),
82 m_unit( 1 ),
83 m_bodyStyle( 1 ),
84 m_appending( aIsAppending ),
85 m_progressReporter( aProgressReporter ),
86 m_lineReader( aLineReader ),
88 m_lineCount( aLineCount ),
89 m_rootSheet( aRootSheet ),
91{
92}
93
94
96{
98 {
99 unsigned progressDelta = std::max( 50u, m_lineCount / 10 );
100 unsigned curLine = m_lineReader->LineNumber();
101
102 if( m_lastProgressLine == 0 || curLine > m_lastProgressLine + progressDelta )
103 {
104 m_progressReporter->SetCurrentProgress( ( (double) curLine )
105 / std::max( 1U, m_lineCount ) );
106
107 if( !m_progressReporter->KeepRefreshing() )
108 THROW_IO_ERROR( _( "Open canceled by user." ) );
109
110 m_lastProgressLine = curLine;
111 }
112 }
113}
114
115
117{
118 KIID id( FromUTF8() );
119
120 while( m_uuids.count( id ) )
121 id.Increment();
122
123 m_uuids.insert( id );
124
125 return id;
126}
127
128
130{
131 T token = NextTok();
132
133 if( token == T_yes )
134 return true;
135 else if( token == T_no )
136 return false;
137 else
138 Expecting( "yes or no" );
139
140 return false;
141}
142
143
144/*
145 * e.g. "hide", "hide)", "(hide yes)"
146 */
148{
149 bool ret = aDefaultValue;
150
151 if( PrevTok() == T_LEFT )
152 {
153 T token = NextTok();
154
155 // "hide)"
156 if( static_cast<int>( token ) == DSN_RIGHT )
157 return aDefaultValue;
158
159 if( token == T_yes )
160 ret = true;
161 else if( token == T_no )
162 ret = false;
163 else
164 Expecting( "yes or no" );
165
166 NeedRIGHT();
167 }
168 else
169 {
170 // "hide"
171 return aDefaultValue;
172 }
173
174 return ret;
175}
176
177
179{
180 T token;
181
182 NeedLEFT();
183 NextTok();
184 parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION );
185
186 // Prior to this, bar was a valid string char for unquoted strings.
187 SetKnowsBar( m_requiredVersion >= 20240529 );
188
189 bool versionChecked = false;
190
191 auto checkVersion =
192 [&]()
193 {
194 if( !versionChecked && m_requiredVersion > SEXPR_SYMBOL_LIB_FILE_VERSION )
195 {
196 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
198 }
199
200 versionChecked = true;
201 };
202
203 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
204 {
205 if( token != T_LEFT )
206 Expecting( T_LEFT );
207
208 token = NextTok();
209
210 switch( token )
211 {
212 case T_generator:
213 // (generator "genname"); we don't care about it at the moment.
214 NeedSYMBOL();
215 NeedRIGHT();
216 break;
217
218 case T_host:
219 {
220 // (host eeschema ["5.99"]); old version of generator token
221 NeedSYMBOL();
222
223 // Really old versions also included a host version
224 if( m_requiredVersion < 20200827 )
225 NeedSYMBOL();
226
227 NeedRIGHT();
228 break;
229 }
230
231 case T_generator_version:
232 {
233 NextTok();
234 m_generatorVersion = FromUTF8();
235 NeedRIGHT();
236
237 // If the format includes a generator version, by this point we have enough info to
238 // do the version check here
239 checkVersion();
240 break;
241 }
242
243 case T_symbol:
244 {
245 // By the time we get to the first symbol, we can check the version
246 checkVersion();
247
248 m_unit = 1;
249 m_bodyStyle = 1;
250
251 try
252 {
253 LIB_SYMBOL* symbol = parseLibSymbol( aSymbolLibMap );
254 aSymbolLibMap[symbol->GetName()] = symbol;
255 }
256 catch( const IO_ERROR& e )
257 {
258 // Record the error and skip to the end of this symbol block
259 wxString warning = wxString::Format(
260 _( "Error parsing symbol at line %d: %s\nSkipping symbol and continuing." ),
261 CurLineNumber(), e.What() );
262 m_parseWarnings.push_back( warning );
263
264 // Skip to the end of this symbol's S-expression block
265 // We're already past T_symbol, so we're inside the symbol definition
266 skipToBlockEnd( 1 );
267 }
268
269 break;
270 }
271
272 default:
273 Expecting( "symbol, generator, or generator_version" );
274 }
275 }
276}
277
278
280 int aFileVersion )
281{
282 LIB_SYMBOL* newSymbol = nullptr;
283
284 NextTok();
285
286 // If there actually isn't anything here, don't throw just return a nullptr
287 if( CurTok() == T_LEFT )
288 {
289 NextTok();
290
291 if( CurTok() == T_symbol )
292 {
293 m_requiredVersion = aFileVersion;
294 newSymbol = parseLibSymbol( aSymbolLibMap );
295
296 const std::vector<wxString>* embeddedFonts =
297 newSymbol->GetEmbeddedFiles()->UpdateFontFiles();
298
299 newSymbol->RunOnChildren(
300 [&]( SCH_ITEM* aChild )
301 {
302 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
303 textItem->ResolveFont( embeddedFonts );
304 },
306 }
307 else
308 {
309 wxString msg = wxString::Format( _( "Cannot parse %s as a symbol" ),
310 GetTokenString( CurTok() ) );
311 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
312 }
313 }
314
315 return newSymbol;
316}
317
318
320{
321 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
322 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
323
324 T token;
325 long tmp;
326 wxString name;
327 wxString error;
328 SCH_ITEM* item;
329 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
330
331 symbol->SetUnitCount( 1, true );
332
333 token = NextTok();
334
335 if( !IsSymbol( token ) )
336 THROW_PARSE_ERROR( _( "Invalid symbol name" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
337
338 name = FromUTF8();
339
340 // Some symbol LIB_IDs have the '/' character escaped which can break derived symbol links.
341 // The '/' character is no longer an illegal LIB_ID character so it doesn't need to be
342 // escaped.
343 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
344
345 LIB_ID id;
346 int bad_pos = id.Parse( name );
347
348 if( bad_pos >= 0 )
349 {
350 if( static_cast<int>( name.size() ) > bad_pos )
351 {
352 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
353 name,
354 name[bad_pos] );
355
356 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
357 }
358
359
360 THROW_PARSE_ERROR( _( "Invalid library identifier" ), CurSource(), CurLine(),
361 CurLineNumber(), CurOffset() );
362 }
363
364 m_symbolName = id.GetLibItemName().wx_str();
365 symbol->SetName( m_symbolName );
366 symbol->SetLibId( id );
367
368 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
369 {
370 if( token != T_LEFT )
371 Expecting( T_LEFT );
372
373 token = NextTok();
374
375 switch( token )
376 {
377 case T_power:
378 symbol->SetGlobalPower();
379 token = NextTok();
380
381 if( token == T_RIGHT )
382 break;
383
384 if( token == T_local )
385 symbol->SetLocalPower();
386 else if( token != T_global )
387 Expecting( "global or local" );
388
389 NeedRIGHT();
390 break;
391
392 case T_body_styles:
393 parseBodyStyles( symbol );
394 break;
395
396 case T_pin_names:
397 parsePinNames( symbol );
398 break;
399
400 case T_pin_numbers:
401 parsePinNumbers( symbol );
402 break;
403
404 case T_exclude_from_sim:
405 symbol->SetExcludedFromSim( parseBool() );
406 NeedRIGHT();
407 break;
408
409 case T_in_bom:
410 symbol->SetExcludedFromBOM( !parseBool() );
411 NeedRIGHT();
412 break;
413
414 case T_on_board:
415 symbol->SetExcludedFromBoard( !parseBool() );
416 NeedRIGHT();
417 break;
418
419 case T_in_pos_files:
420 symbol->SetExcludedFromPosFiles( !parseBool() );
421 NeedRIGHT();
422 break;
423
424 case T_duplicate_pin_numbers_are_jumpers:
425 symbol->SetDuplicatePinNumbersAreJumpers( parseBool() );
426 NeedRIGHT();
427 break;
428
429 case T_jumper_pin_groups:
430 {
431 std::vector<std::set<wxString>>& groups = symbol->JumperPinGroups();
432 std::set<wxString>* currentGroup = nullptr;
433
434 for( token = NextTok(); currentGroup || token != T_RIGHT; token = NextTok() )
435 {
436 switch( static_cast<int>( token ) )
437 {
438 case T_LEFT:
439 currentGroup = &groups.emplace_back();
440 break;
441
442 case DSN_STRING:
443 currentGroup->insert( FromUTF8() );
444 break;
445
446 case T_RIGHT:
447 currentGroup = nullptr;
448 break;
449
450 default:
451 Expecting( "list of pin names" );
452 }
453 }
454
455 break;
456 }
457
458 case T_property:
459 parseProperty( symbol );
460 break;
461
462 case T_extends:
463 {
464 token = NextTok();
465
466 if( !IsSymbol( token ) )
467 {
468 THROW_PARSE_ERROR( _( "Invalid parent symbol name" ), CurSource(), CurLine(),
469 CurLineNumber(), CurOffset() );
470 }
471
472 name = FromUTF8();
473
474 // Some symbol LIB_IDs have the '/' character escaped which can break derived
475 // symbol links. The '/' character is no longer an illegal LIB_ID character so
476 // it doesn't need to be escaped.
477 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
478
479 symbol->SetParentName( name );
480 NeedRIGHT();
481 break;
482 }
483
484 case T_symbol:
485 {
486 token = NextTok();
487
488 if( !IsSymbol( token ) )
489 {
490 THROW_PARSE_ERROR( _( "Invalid symbol unit name" ), CurSource(), CurLine(),
491 CurLineNumber(), CurOffset() );
492 }
493
494 name = FromUTF8();
495
496 // Some symbol LIB_IDs have the '/' character escaped which can break derived
497 // symbol links. The '/' character is no longer an illegal LIB_ID character so
498 // it doesn't need to be escaped.
499 name.Replace( wxS( "{slash}" ), wxT( "/" ) );
500
501 if( !name.StartsWith( m_symbolName ) )
502 {
503 error.Printf( _( "Invalid symbol unit name prefix %s" ), name.c_str() );
504 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
505 }
506
507 name = name.Right( name.Length() - m_symbolName.Length() - 1 );
508
509 wxStringTokenizer tokenizer( name, "_" );
510
511 if( tokenizer.CountTokens() != 2 )
512 {
513 error.Printf( _( "Invalid symbol unit name suffix %s" ), name.c_str() );
514 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
515 }
516
517 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
518 {
519 error.Printf( _( "Invalid symbol unit number %s" ), name.c_str() );
520 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
521 }
522
523 m_unit = static_cast<int>( tmp );
524
525 if( !tokenizer.GetNextToken().ToLong( &tmp ) )
526 {
527 error.Printf( _( "Invalid symbol body style number %s" ), name.c_str() );
528 THROW_PARSE_ERROR( error, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
529 }
530
531 m_bodyStyle = static_cast<int>( tmp );
532
533 if( m_bodyStyle > 1 )
534 symbol->SetBodyStyleCount( m_bodyStyle, false, false );
535
536 if( m_unit > symbol->GetUnitCount() )
537 symbol->SetUnitCount( m_unit, false );
538
539 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
540 {
541 if( token != T_LEFT )
542 Expecting( T_LEFT );
543
544 token = NextTok();
545
546 switch( token )
547 {
548 case T_unit_name:
549 token = NextTok();
550
551 if( IsSymbol( token ) )
552 symbol->GetUnitDisplayNames()[m_unit] = FromUTF8();
553
554 NeedRIGHT();
555 break;
556
557 case T_arc:
558 case T_bezier:
559 case T_circle:
560 case T_pin:
561 case T_polyline:
562 case T_rectangle:
563 case T_text:
564 case T_text_box:
565 item = ParseSymbolDrawItem();
566
567 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
568
569 item->SetParent( symbol.get() );
570 symbol->AddDrawItem( item, false );
571 break;
572
573 default:
574 Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
575 };
576 }
577
578 m_unit = 1;
579 m_bodyStyle = 1;
580 break;
581 }
582
583 case T_arc:
584 case T_bezier:
585 case T_circle:
586 case T_pin:
587 case T_polyline:
588 case T_rectangle:
589 case T_text:
590 case T_text_box:
591 item = ParseSymbolDrawItem();
592
593 wxCHECK_MSG( item, nullptr, "Invalid draw item pointer." );
594
595 item->SetParent( symbol.get() );
596 symbol->AddDrawItem( item, false );
597 break;
598
599 case T_embedded_fonts:
600 {
601 symbol->SetAreFontsEmbedded( parseBool() );
602 NeedRIGHT();
603 break;
604 }
605
606 case T_embedded_files:
607 {
608 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
609 embeddedFilesParser.SyncLineReaderWith( *this );
610
611 try
612 {
613 embeddedFilesParser.ParseEmbedded( symbol->GetEmbeddedFiles() );
614 }
615 catch( const IO_ERROR& e )
616 {
617 m_parseWarnings.push_back( e.What() );
618
619 int depth = 0;
620
621 for( int tok = embeddedFilesParser.NextTok();
622 tok != DSN_EOF;
623 tok = embeddedFilesParser.NextTok() )
624 {
625 if( tok == DSN_LEFT )
626 depth++;
627 else if( tok == DSN_RIGHT && --depth < 0 )
628 break;
629 }
630 }
631
632 SyncLineReaderWith( embeddedFilesParser );
633 break;
634 }
635
636 default:
637 Expecting( "pin_names, pin_numbers, arc, bezier, circle, pin, polyline, "
638 "rectangle, or text" );
639 }
640 }
641
642 symbol->GetDrawItems().sort();
643 m_symbolName.clear();
644
645 const std::vector<wxString>* embeddedFonts = symbol->GetEmbeddedFiles()->UpdateFontFiles();
646
647 symbol->RunOnChildren(
648 [&]( SCH_ITEM* aChild )
649 {
650 if( EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aChild ) )
651 textItem->ResolveFont( embeddedFonts );
652 },
654
655 // Before V10 we didn't store the number of body styles in a symbol, we just looked at all its
656 // drawings each time we wanted to know.
657 if( m_requiredVersion < 20250827 )
658 symbol->SetHasDeMorganBodyStyles( symbol->HasLegacyAlternateBodyStyle() );
659
660 symbol->RefreshLibraryTreeCaches();
661
662 return symbol.release();
663}
664
665
667{
668 switch( CurTok() )
669 {
670 case T_arc: return parseSymbolArc(); break;
671 case T_bezier: return parseSymbolBezier(); break;
672 case T_circle: return parseSymbolCircle(); break;
673 case T_pin: return parseSymbolPin(); break;
674 case T_polyline: return parseSymbolPolyLine(); break;
675 case T_rectangle: return parseSymbolRectangle(); break;
676 case T_text: return parseSymbolText(); break;
677 case T_text_box: return parseSymbolTextBox(); break;
678 default: Expecting( "arc, bezier, circle, pin, polyline, rectangle, or text" );
679 }
680
681 return nullptr;
682}
683
684
686{
687 auto retval = parseDouble() * schIUScale.IU_PER_MM;
688
689 // Schematic internal units are represented as integers. Any values that are
690 // larger or smaller than the schematic units represent undefined behavior for
691 // the system. Limit values to the largest that can be displayed on the screen.
692 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
693
694 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
695}
696
697
699{
700 auto retval = parseDouble( aExpected ) * schIUScale.IU_PER_MM;
701
702 constexpr double int_limit = std::numeric_limits<int>::max() * 0.7071;
703
704 return KiROUND( std::clamp( retval, -int_limit, int_limit ) );
705}
706
707
709{
710 STROKE_PARAMS_PARSER strokeParser( reader, schIUScale.IU_PER_MM );
711 strokeParser.SyncLineReaderWith( *this );
712
713 strokeParser.ParseStroke( aStroke );
714 SyncLineReaderWith( strokeParser );
715}
716
717
719{
720 wxCHECK_RET( CurTok() == T_fill, "Cannot parse " + GetTokenString( CurTok() ) + " as a fill." );
721
724
725 T token;
726
727 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
728 {
729 if( token != T_LEFT )
730 Expecting( T_LEFT );
731
732 token = NextTok();
733
734 switch( token )
735 {
736 case T_type:
737 {
738 token = NextTok();
739
740 switch( token )
741 {
742 case T_none: aFill.m_FillType = FILL_T::NO_FILL; break;
743 case T_outline: aFill.m_FillType = FILL_T::FILLED_SHAPE; break;
744 case T_background: aFill.m_FillType = FILL_T::FILLED_WITH_BG_BODYCOLOR; break;
745 case T_color: aFill.m_FillType = FILL_T::FILLED_WITH_COLOR; break;
746 case T_hatch: aFill.m_FillType = FILL_T::HATCH; break;
747 case T_reverse_hatch: aFill.m_FillType = FILL_T::REVERSE_HATCH; break;
748 case T_cross_hatch: aFill.m_FillType = FILL_T::CROSS_HATCH; break;
749 default: Expecting( "none, outline, hatch, reverse_hatch, "
750 "cross_hatch, color or background" );
751 }
752
753 NeedRIGHT();
754 break;
755 }
756
757 case T_color:
758 {
759 COLOR4D color;
760
761 color.r = parseInt( "red" ) / 255.0;
762 color.g = parseInt( "green" ) / 255.0;
763 color.b = parseInt( "blue" ) / 255.0;
764 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
765 aFill.m_Color = color;
766 NeedRIGHT();
767 break;
768 }
769
770 default:
771 Expecting( "type or color" );
772 }
773 }
774}
775
776
777void SCH_IO_KICAD_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText, bool aConvertOverbarSyntax,
778 bool aEnforceMinTextSize )
779{
780 wxCHECK_RET( aText && ( CurTok() == T_effects || CurTok() == T_href ),
781 "Cannot parse " + GetTokenString( CurTok() ) + " as an EDA_TEXT." );
782
783 // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
784 // We need to convert the old syntax to the new one.
785 if( aConvertOverbarSyntax && m_requiredVersion < 20210606 )
786 aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
787
788 T token;
789 bool bold = false;
790 bool italic = false;
792
793 // Various text objects (text boxes, schematic text, etc.) all have their own defaults,
794 // but the file format default is {center,center} so we have to set that before parsing.
797
798 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
799 {
800 if( token == T_LEFT )
801 token = NextTok();
802
803 switch( token )
804 {
805 case T_font:
806 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
807 {
808 if( token == T_LEFT )
809 token = NextTok();
810
811 switch( token )
812 {
813 case T_face:
814 NeedSYMBOL();
815 aText->SetUnresolvedFontName( FromUTF8() );
816 NeedRIGHT();
817 break;
818
819 case T_size:
820 {
821 VECTOR2I sz;
822 sz.y = parseInternalUnits( "text height" );
823 sz.x = parseInternalUnits( "text width" );
824 aText->SetTextSize( sz, aEnforceMinTextSize );
825 NeedRIGHT();
826 break;
827 }
828
829 case T_thickness:
830 aText->SetTextThickness( parseInternalUnits( "text thickness" ) );
831 NeedRIGHT();
832 break;
833
834 case T_bold:
835 bold = parseMaybeAbsentBool( true );
836 aText->SetBoldFlag( bold );
837 break;
838
839 case T_italic:
840 italic = parseMaybeAbsentBool( true );
841 aText->SetItalicFlag( italic );
842 break;
843
844 case T_color:
845 color.r = parseInt( "red" ) / 255.0;
846 color.g = parseInt( "green" ) / 255.0;
847 color.b = parseInt( "blue" ) / 255.0;
848 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
849 aText->SetTextColor( color );
850 NeedRIGHT();
851 break;
852
853 case T_line_spacing:
854 aText->SetLineSpacing( parseDouble( "line spacing" ) );
855 NeedRIGHT();
856 break;
857
858 default:
859 Expecting( "face, size, thickness, line_spacing, bold, or italic" );
860 }
861 }
862
863 break;
864
865 case T_justify:
866 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
867 {
868 switch( token )
869 {
870 case T_left: aText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
871 case T_right: aText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
872 case T_top: aText->SetVertJustify( GR_TEXT_V_ALIGN_TOP ); break;
873 case T_bottom: aText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); break;
874 // Do not set mirror property for schematic text elements
875 case T_mirror: break;
876 default: Expecting( "left, right, top, bottom, or mirror" );
877 }
878 }
879
880 break;
881
882 case T_href:
883 {
884 NeedSYMBOL();
885 wxString hyperlink = FromUTF8();
886
887 if( !EDA_TEXT::ValidateHyperlink( hyperlink ) )
888 {
889 THROW_PARSE_ERROR( wxString::Format( _( "Invalid hyperlink url '%s'" ), hyperlink ),
890 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
891 }
892 else
893 {
894 aText->SetHyperlink( hyperlink );
895 }
896
897 NeedRIGHT();
898 }
899 break;
900
901 case T_hide:
902 {
903 bool hide = parseMaybeAbsentBool( true );
904 aText->SetVisible( !hide );
905 break;
906 }
907
908 default:
909 Expecting( "font, justify, hide or href" );
910 }
911 }
912}
913
914
915void SCH_IO_KICAD_SEXPR_PARSER::parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion )
916{
917 if( CurTok() != aHeaderType )
918 {
919 THROW_PARSE_ERROR( wxString::Format( _( "Cannot parse '%s' as a header." ),
920 GetTokenString( CurTok() ) ),
921 CurSource(), CurLine(), CurLineNumber(), CurOffset() );
922 }
923
924 NeedLEFT();
925
926 T tok = NextTok();
927
928 if( tok == T_version )
929 {
930 m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
931 NeedRIGHT();
932 }
933 else
934 {
935 m_requiredVersion = aFileVersion;
936 }
937}
938
939
940void SCH_IO_KICAD_SEXPR_PARSER::parseBodyStyles( std::unique_ptr<LIB_SYMBOL>& aSymbol )
941{
942 wxCHECK_RET( CurTok() == T_body_styles,
943 "Cannot parse " + GetTokenString( CurTok() ) + " as a body_styles token." );
944
945 std::vector<wxString> names;
946
947 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
948 {
949 if( token == T_demorgan )
950 {
951 aSymbol->SetHasDeMorganBodyStyles( true );
952 continue;
953 }
954 else if( !IsSymbol( token ) )
955 {
956 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
957 CurOffset() );
958 }
959
960 names.push_back( FromUTF8() );
961 }
962
963 if( !names.empty() )
964 aSymbol->SetBodyStyleNames( names );
965}
966
967
968void SCH_IO_KICAD_SEXPR_PARSER::parsePinNames( std::unique_ptr<LIB_SYMBOL>& aSymbol )
969{
970 wxCHECK_RET( CurTok() == T_pin_names,
971 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_name token." );
972
980
981 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
982 {
983 // Pre-20241004 format - bare 'hide' keyword
984 if( token == T_hide )
985 {
986 aSymbol->SetShowPinNames( false );
987 continue;
988 }
989
990 if( token != T_LEFT )
991 {
992 Expecting( T_LEFT );
993 }
994
995 token = NextTok();
996
997 switch( token )
998 {
999 case T_offset:
1000 aSymbol->SetPinNameOffset( parseInternalUnits( "pin name offset" ) );
1001 NeedRIGHT();
1002 break;
1003
1004 case T_hide:
1005 aSymbol->SetShowPinNames( !parseBool() );
1006 NeedRIGHT();
1007 break;
1008
1009 default:
1010 Expecting( "offset or hide" );
1011 }
1012 }
1013}
1014
1015
1016void SCH_IO_KICAD_SEXPR_PARSER::parsePinNumbers( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1017{
1018 wxCHECK_RET( CurTok() == T_pin_numbers,
1019 "Cannot parse " + GetTokenString( CurTok() ) + " as a pin_number token." );
1020
1027
1028 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1029 {
1030 // Pre-20241004 format - bare 'hide' keyword
1031 if( token == T_hide )
1032 {
1033 aSymbol->SetShowPinNumbers( false );
1034 continue;
1035 }
1036
1037 if( token != T_LEFT )
1038 {
1039 Expecting( T_LEFT );
1040 }
1041
1042 token = NextTok();
1043
1044 switch( token )
1045 {
1046 case T_hide:
1047 aSymbol->SetShowPinNumbers( !parseBool() );
1048 NeedRIGHT();
1049 break;
1050
1051 default:
1052 Expecting( "hide" );
1053 break;
1054 }
1055 }
1056}
1057
1058
1059SCH_FIELD* SCH_IO_KICAD_SEXPR_PARSER::parseProperty( std::unique_ptr<LIB_SYMBOL>& aSymbol )
1060{
1061 wxCHECK_MSG( CurTok() == T_property, nullptr,
1062 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a property." ) );
1063 wxCHECK( aSymbol, nullptr );
1064
1065 FIELD_T fieldId = FIELD_T::USER;
1066 wxString name;
1067 wxString value;
1068 bool isPrivate = false;
1069 bool isVisible = true;
1070
1071 T token = NextTok();
1072
1073 if( token == T_private )
1074 {
1075 isPrivate = true;
1076 token = NextTok();
1077 }
1078
1079 if( !IsSymbol( token ) )
1080 {
1081 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
1082 CurOffset() );
1083 }
1084
1085 name = FromUTF8();
1086
1087 if( name.IsEmpty() )
1088 {
1089 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
1090 CurOffset() );
1091 }
1092
1093 // Correctly set the ID based on canonical (untranslated) field name
1094 for( FIELD_T id : MANDATORY_FIELDS )
1095 {
1096 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
1097 {
1098 fieldId = id;
1099 break;
1100 }
1101 }
1102
1103 auto field = std::make_unique<SCH_FIELD>( aSymbol.get(), fieldId, name );
1104 field->SetPrivate( isPrivate );
1105 field->SetVisible( isVisible );
1106
1107 token = NextTok();
1108
1109 if( !IsSymbol( token ) )
1110 {
1111 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
1112 CurOffset() );
1113 }
1114
1115 // Empty property values are valid.
1116
1117 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1118 value = wxEmptyString;
1119 else
1120 value = FromUTF8();
1121
1122 field->SetText( value );
1123
1124 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1125 {
1126 if( token != T_LEFT )
1127 Expecting( T_LEFT );
1128
1129 token = NextTok();
1130
1131 switch( token )
1132 {
1133 case T_id: // legacy token; ignore
1134 parseInt( "field ID" );
1135 NeedRIGHT();
1136 break;
1137
1138 case T_at:
1139 field->SetPosition( parseXY( true ) );
1140 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
1141 NeedRIGHT();
1142 break;
1143
1144 case T_hide:
1145 field->SetVisible( !parseBool() );
1146 NeedRIGHT();
1147 break;
1148
1149 case T_effects:
1150 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
1151 field->GetId() == FIELD_T::VALUE );
1152 break;
1153
1154 case T_show_name:
1155 {
1156 bool show = parseMaybeAbsentBool( true );
1157 field->SetNameShown( show );
1158 break;
1159 }
1160
1161 case T_do_not_autoplace:
1162 {
1163 bool doNotAutoplace = parseMaybeAbsentBool( true );
1164 field->SetCanAutoplace( !doNotAutoplace );
1165 break;
1166 }
1167
1168 default:
1169 Expecting( "id, at, hide, show_name, do_not_autoplace, or effects" );
1170 }
1171 }
1172
1173 SCH_FIELD* existingField;
1174
1175 if( field->IsMandatory() )
1176 {
1177 existingField = aSymbol->GetField( field->GetId() );
1178
1179 *existingField = *field;
1180 return existingField;
1181 }
1182 else if( name == "ki_keywords" )
1183 {
1184 // Not a SCH_FIELD object yet.
1185 aSymbol->SetKeyWords( value );
1186 return nullptr;
1187 }
1188 // In v7 and earlier the description field didn't exist and was a key/value
1189 else if( name == "ki_description" )
1190 {
1191 aSymbol->SetDescription( value );
1192 return nullptr;
1193 }
1194 else if( name == "ki_fp_filters" )
1195 {
1196 // Not a SCH_FIELD object yet.
1197 wxArrayString filters;
1198 wxStringTokenizer tokenizer( value, " \t\r\n", wxTOKEN_STRTOK );
1199
1200 while( tokenizer.HasMoreTokens() )
1201 {
1202 wxString curr_token = UnescapeString( tokenizer.GetNextToken() );
1203 filters.Add( curr_token );
1204 }
1205
1206 aSymbol->SetFPFilters( filters );
1207 return nullptr;
1208 }
1209 else if( name == "ki_locked" )
1210 {
1211 // This is a temporary SCH_FIELD object until interchangeable units are determined on
1212 // the fly.
1213 aSymbol->LockUnits( true );
1214 return nullptr;
1215 }
1216 else
1217 {
1218 // At this point, a user field is read.
1219 existingField = aSymbol->GetField( field->GetCanonicalName() );
1220
1221#if 1 // Enable it to modify the name of the field to add if already existing
1222 // Disable it to skip the field having the same name as previous field
1223 if( existingField )
1224 {
1225 // We cannot handle 2 fields with the same name, so because the field name
1226 // is already in use, try to build a new name (oldname_x)
1227 wxString base_name = field->GetCanonicalName();
1228
1229 // Arbitrary limit 10 attempts to find a new name
1230 for( int ii = 1; ii < 10 && existingField; ii++ )
1231 {
1232 wxString newname = base_name;
1233 newname << '_' << ii;
1234
1235 existingField = aSymbol->GetField( newname );
1236
1237 if( !existingField ) // the modified name is not found, use it
1238 field->SetName( newname );
1239 }
1240 }
1241#endif
1242 if( !existingField )
1243 {
1244 aSymbol->AddDrawItem( field.get(), false );
1245 return field.release();
1246 }
1247 else
1248 {
1249 // We cannot handle 2 fields with the same name, so skip this one
1250 return nullptr;
1251 }
1252 }
1253}
1254
1255
1257{
1258 wxCHECK_MSG( CurTok() == T_arc, nullptr,
1259 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
1260
1261 T token;
1262 VECTOR2I startPoint( 1, 0 ); // Initialize to a non-degenerate arc just for safety
1263 VECTOR2I midPoint( 1, 1 );
1264 VECTOR2I endPoint( 0, 1 );
1265 bool hasMidPoint = false;
1267 FILL_PARAMS fill;
1268
1269 // Parameters for legacy format
1270 VECTOR2I center( 0, 0 );
1271 EDA_ANGLE startAngle = ANGLE_0;
1272 EDA_ANGLE endAngle = ANGLE_90;
1273 bool hasAngles = false;
1274
1275 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
1276
1277 arc->SetUnit( m_unit );
1278 arc->SetBodyStyle( m_bodyStyle );
1279
1280 token = NextTok();
1281
1282 if( token == T_private )
1283 {
1284 arc->SetPrivate( true );
1285 token = NextTok();
1286 }
1287
1288 for( ; token != T_RIGHT; token = NextTok() )
1289 {
1290 if( token != T_LEFT )
1291 Expecting( T_LEFT );
1292
1293 token = NextTok();
1294
1295 switch( token )
1296 {
1297 case T_start:
1298 startPoint = parseXY( true );
1299 NeedRIGHT();
1300 break;
1301
1302 case T_mid:
1303 midPoint = parseXY( true );
1304 NeedRIGHT();
1305 hasMidPoint = true;
1306 break;
1307
1308 case T_end:
1309 endPoint = parseXY( true );
1310 NeedRIGHT();
1311 break;
1312
1313 case T_radius:
1314 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1315 {
1316 if( token != T_LEFT )
1317 Expecting( T_LEFT );
1318
1319 token = NextTok();
1320
1321 switch( token )
1322 {
1323 case T_at:
1324 center = parseXY( true );
1325 NeedRIGHT();
1326 break;
1327
1328 case T_length:
1329 parseInternalUnits( "radius length" );
1330 NeedRIGHT();
1331 break;
1332
1333 case T_angles:
1334 {
1335 startAngle = EDA_ANGLE( parseDouble( "start radius angle" ), DEGREES_T );
1336 endAngle = EDA_ANGLE( parseDouble( "end radius angle" ), DEGREES_T );
1337 startAngle.Normalize();
1338 endAngle.Normalize();
1339 NeedRIGHT();
1340 hasAngles = true;
1341 break;
1342 }
1343
1344 default:
1345 Expecting( "at, length, or angles" );
1346 }
1347 }
1348
1349 break;
1350
1351 case T_stroke:
1352 parseStroke( stroke );
1353 arc->SetStroke( stroke );
1354 break;
1355
1356 case T_fill:
1357 parseFill( fill );
1358 arc->SetFillMode( fill.m_FillType );
1359 arc->SetFillColor( fill.m_Color );
1360 break;
1361
1362 default:
1363 Expecting( "start, mid, end, radius, stroke, or fill" );
1364 }
1365 }
1366
1367 if( hasMidPoint )
1368 {
1369 arc->SetArcGeometry( startPoint, midPoint, endPoint );
1370
1371 if( m_requiredVersion <= 20230121 ) // Versions before 7.0
1372 {
1373 // Should be not required. Unfortunately it is needed because some bugs created
1374 // incorrect data after conversion of old libraries to the new arc format using
1375 // startPoint, midPoint, endPoint
1376 // Until now, in Eeschema the arc angle should be <= 180 deg.
1377 // If > 180 (bug...) we need to swap arc ends.
1378 // However arc angle == 180 deg can also create issues in some cases (plotters, hittest)
1379 // so also avoid arc == 180 deg
1380 EDA_ANGLE arc_start, arc_end, arc_angle;
1381 arc->CalcArcAngles( arc_start, arc_end );
1382 arc_angle = arc_end - arc_start;
1383
1384 if( arc_angle > ANGLE_180 )
1385 {
1386 // Change arc to its complement (360deg - arc_angle)
1387 arc->SetStart( endPoint );
1388 arc->SetEnd( startPoint );
1389 VECTOR2I new_center =
1390 CalcArcCenter( arc->GetStart(), arc->GetEnd(), ANGLE_360 - arc_angle );
1391 arc->SetCenter( new_center );
1392 }
1393 else if( arc_angle == ANGLE_180 )
1394 {
1395 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1396 EDA_ANGLE( 179.5, DEGREES_T ) );
1397 arc->SetCenter( new_center );
1398 }
1399 }
1400 }
1401 else if( hasAngles )
1402 {
1403 arc->SetCenter( center );
1404 /*
1405 * Older versions stored start-end with an implied winding, but the winding was different
1406 * between LibEdit and PCBNew. Since we now use a common class (EDA_SHAPE) for both we
1407 * need to flip one of them. LibEdit drew the short straw.
1408 */
1409 arc->SetStart( endPoint );
1410 arc->SetEnd( startPoint );
1411
1412 // Like previously, 180 degrees arcs that create issues are just modified
1413 // to be < 180 degrees to do not break some other functions ( Draw, Plot, HitTest)
1414 EDA_ANGLE arc_start, arc_end, arc_angle;
1415 arc->CalcArcAngles( arc_start, arc_end );
1416 arc_angle = arc_end - arc_start;
1417
1418 // The arc angle should be <= 180 deg in old libraries.
1419 // If > 180 we need to swap arc ends (the first choice was not good)
1420 if( arc_angle > ANGLE_180 )
1421 {
1422 arc->SetStart( startPoint );
1423 arc->SetEnd( endPoint );
1424 }
1425 else if( arc_angle == ANGLE_180 )
1426 {
1427 // Disabled (since the Y axis was reversed in library editor
1428 // and arcs having a 180 deg arc do not create issues
1429 // However keep it for info, just in case
1430 #if 0
1431 arc->SetStart( startPoint ); // not working with Y axis reversed
1432 arc->SetEnd( endPoint ); // not working with Y axis reversed
1433 // Useless now arcs >= 180 deg are well handled
1434 VECTOR2I new_center = CalcArcCenter( arc->GetStart(), arc->GetEnd(),
1435 EDA_ANGLE( 179.5, DEGREES_T ) );
1436 arc->SetCenter( new_center );
1437 #endif
1438 }
1439 }
1440 else
1441 {
1442 wxFAIL_MSG( "Setting arc without either midpoint or angles not implemented." );
1443 }
1444
1445 return arc.release();
1446}
1447
1448
1450{
1451 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
1452 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
1453
1454 T token;
1456 FILL_PARAMS fill;
1457
1458 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
1459
1460 bezier->SetUnit( m_unit );
1461 bezier->SetBodyStyle( m_bodyStyle );
1462
1463 token = NextTok();
1464
1465 if( token == T_private )
1466 {
1467 bezier->SetPrivate( true );
1468 token = NextTok();
1469 }
1470
1471 for( ; token != T_RIGHT; token = NextTok() )
1472 {
1473 if( token != T_LEFT )
1474 Expecting( T_LEFT );
1475
1476 token = NextTok();
1477
1478 switch( token )
1479 {
1480 case T_pts:
1481 {
1482 int ii = 0;
1483
1484 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
1485 {
1486 if( token != T_LEFT )
1487 Expecting( T_LEFT );
1488
1489 token = NextTok();
1490
1491 if( token != T_xy )
1492 Expecting( "xy" );
1493
1494 switch( ii )
1495 {
1496 case 0: bezier->SetStart( parseXY( true ) ); break;
1497 case 1: bezier->SetBezierC1( parseXY( true ) ); break;
1498 case 2: bezier->SetBezierC2( parseXY( true ) ); break;
1499 case 3: bezier->SetEnd( parseXY( true ) ); break;
1500 default: Unexpected( "control point" ); break;
1501 }
1502
1503 NeedRIGHT();
1504 }
1505 }
1506 break;
1507
1508 case T_stroke:
1509 parseStroke( stroke );
1510 bezier->SetStroke( stroke );
1511 break;
1512
1513 case T_fill:
1514 parseFill( fill );
1515 bezier->SetFillMode( fill.m_FillType );
1516 bezier->SetFillColor( fill.m_Color );
1517 break;
1518
1519 default:
1520 Expecting( "pts, stroke, or fill" );
1521 }
1522 }
1523
1524 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
1525
1526 return bezier.release();
1527}
1528
1529
1531{
1532 wxCHECK_MSG( CurTok() == T_circle, nullptr,
1533 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
1534
1535 T token;
1536 VECTOR2I center( 0, 0 );
1537 int radius = 1; // defaulting to 0 could result in troublesome math....
1539 FILL_PARAMS fill;
1540
1541 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
1542
1543 circle->SetUnit( m_unit );
1544 circle->SetBodyStyle( m_bodyStyle );
1545
1546 token = NextTok();
1547
1548 if( token == T_private )
1549 {
1550 circle->SetPrivate( true );
1551 token = NextTok();
1552 }
1553
1554 for( ; token != T_RIGHT; token = NextTok() )
1555 {
1556 if( token != T_LEFT )
1557 Expecting( T_LEFT );
1558
1559 token = NextTok();
1560
1561 switch( token )
1562 {
1563 case T_center:
1564 center = parseXY( true );
1565 NeedRIGHT();
1566 break;
1567
1568 case T_radius:
1569 radius = parseInternalUnits( "radius length" );
1570 NeedRIGHT();
1571 break;
1572
1573 case T_stroke:
1574 parseStroke( stroke );
1575 circle->SetStroke( stroke );
1576 break;
1577
1578 case T_fill:
1579 parseFill( fill );
1580 circle->SetFillMode( fill.m_FillType );
1581 circle->SetFillColor( fill.m_Color );
1582 break;
1583
1584 default:
1585 Expecting( "center, radius, stroke, or fill" );
1586 }
1587 }
1588
1589 circle->SetCenter( center );
1590 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
1591
1592 return circle.release();
1593}
1594
1595
1597{
1598 auto parseType =
1599 [&]( T token ) -> ELECTRICAL_PINTYPE
1600 {
1601 switch( token )
1602 {
1603 case T_input: return ELECTRICAL_PINTYPE::PT_INPUT;
1604 case T_output: return ELECTRICAL_PINTYPE::PT_OUTPUT;
1605 case T_bidirectional: return ELECTRICAL_PINTYPE::PT_BIDI;
1606 case T_tri_state: return ELECTRICAL_PINTYPE::PT_TRISTATE;
1607 case T_passive: return ELECTRICAL_PINTYPE::PT_PASSIVE;
1608 case T_unspecified: return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
1609 case T_power_in: return ELECTRICAL_PINTYPE::PT_POWER_IN;
1610 case T_power_out: return ELECTRICAL_PINTYPE::PT_POWER_OUT;
1611 case T_open_collector: return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
1612 case T_open_emitter: return ELECTRICAL_PINTYPE::PT_OPENEMITTER;
1613 case T_unconnected:
1614 case T_no_connect: return ELECTRICAL_PINTYPE::PT_NC;
1615 case T_free: return ELECTRICAL_PINTYPE::PT_NIC;
1616
1617 default:
1618 Expecting( "input, output, bidirectional, tri_state, passive, unspecified, "
1619 "power_in, power_out, open_collector, open_emitter, free or "
1620 "no_connect" );
1622 }
1623 };
1624
1625 auto parseShape =
1626 [&]( T token ) -> GRAPHIC_PINSHAPE
1627 {
1628 switch( token )
1629 {
1630 case T_line: return GRAPHIC_PINSHAPE::LINE;
1631 case T_inverted: return GRAPHIC_PINSHAPE::INVERTED;
1632 case T_clock: return GRAPHIC_PINSHAPE::CLOCK;
1633 case T_inverted_clock: return GRAPHIC_PINSHAPE::INVERTED_CLOCK;
1634 case T_input_low: return GRAPHIC_PINSHAPE::INPUT_LOW;
1635 case T_clock_low: return GRAPHIC_PINSHAPE::CLOCK_LOW;
1636 case T_output_low: return GRAPHIC_PINSHAPE::OUTPUT_LOW;
1637 case T_edge_clock_high: return GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
1638 case T_non_logic: return GRAPHIC_PINSHAPE::NONLOGIC;
1639
1640 default:
1641 Expecting( "line, inverted, clock, inverted_clock, input_low, clock_low, "
1642 "output_low, edge_clock_high, non_logic" );
1644 }
1645 };
1646
1647 wxCHECK_MSG( CurTok() == T_pin, nullptr,
1648 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a pin token." ) );
1649
1650 T token;
1651 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( nullptr );
1652
1653 pin->SetUnit( m_unit );
1654 pin->SetBodyStyle( m_bodyStyle );
1655
1656 // Pin electrical type.
1657 token = NextTok();
1658 pin->SetType( parseType( token ) );
1659
1660 // Pin shape.
1661 token = NextTok();
1662 pin->SetShape( parseShape( token ) );
1663
1664 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1665 {
1666 // Pre-2024104 format (bare 'hide' keyword)
1667 if( token == T_hide )
1668 {
1669 pin->SetVisible( false );
1670 continue;
1671 }
1672
1673 if( token != T_LEFT )
1674 Expecting( T_LEFT );
1675
1676 token = NextTok();
1677
1678 switch( token )
1679 {
1680 case T_at:
1681 pin->SetPosition( parseXY( true ) );
1682
1683 switch( parseInt( "pin orientation" ) )
1684 {
1685 case 0: pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); break;
1686 case 90: pin->SetOrientation( PIN_ORIENTATION::PIN_UP ); break;
1687 case 180: pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); break;
1688 case 270: pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); break;
1689 default: Expecting( "0, 90, 180, or 270" );
1690 }
1691
1692 NeedRIGHT();
1693 break;
1694
1695 case T_length:
1696 pin->SetLength( parseInternalUnits( "pin length" ) );
1697 NeedRIGHT();
1698 break;
1699
1700 case T_hide:
1701 pin->SetVisible( !parseBool() );
1702 NeedRIGHT();
1703 break;
1704
1705 case T_name:
1706 token = NextTok();
1707
1708 if( !IsSymbol( token ) )
1709 {
1710 THROW_PARSE_ERROR( _( "Invalid pin name" ), CurSource(), CurLine(), CurLineNumber(),
1711 CurOffset() );
1712 }
1713
1714 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1715 pin->SetName( wxEmptyString );
1716 else if( m_requiredVersion < 20210606 )
1717 pin->SetName( ConvertToNewOverbarNotation( FromUTF8() ) );
1718 else
1719 pin->SetName( FromUTF8() );
1720
1721 token = NextTok();
1722
1723 if( token != T_RIGHT )
1724 {
1725 token = NextTok();
1726
1727 if( token == T_effects )
1728 {
1729 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1730 // so duplicate parsing is not required.
1732
1733 parseEDA_TEXT( &text, true, false );
1734 pin->SetNameTextSize( text.GetTextHeight() );
1735 NeedRIGHT();
1736 }
1737 else
1738 {
1739 Expecting( "effects" );
1740 }
1741 }
1742
1743 break;
1744
1745 case T_number:
1746 token = NextTok();
1747
1748 if( !IsSymbol( token ) )
1749 {
1750 THROW_PARSE_ERROR( _( "Invalid pin number" ), CurSource(), CurLine(),
1751 CurLineNumber(), CurOffset() );
1752 }
1753
1754 if( m_requiredVersion < 20250318 && CurStr() == "~" )
1755 pin->SetNumber( wxEmptyString );
1756 else if( m_requiredVersion < 20210606 )
1757 pin->SetNumber( ConvertToNewOverbarNotation( FromUTF8() ) );
1758 else
1759 pin->SetNumber( FromUTF8() );
1760
1761 token = NextTok();
1762
1763 if( token != T_RIGHT )
1764 {
1765 token = NextTok();
1766
1767 if( token == T_effects )
1768 {
1769 // The EDA_TEXT font effects formatting is used so use and EDA_TEXT object
1770 // so duplicate parsing is not required.
1772
1773 parseEDA_TEXT( &text, false, false );
1774 pin->SetNumberTextSize( text.GetTextHeight() );
1775 NeedRIGHT();
1776 }
1777 else
1778 {
1779 Expecting( "effects" );
1780 }
1781 }
1782
1783 break;
1784
1785 case T_alternate:
1786 {
1787 SCH_PIN::ALT alt;
1788
1789 token = NextTok();
1790
1791 if( !IsSymbol( token ) )
1792 {
1793 THROW_PARSE_ERROR( _( "Invalid alternate pin name" ), CurSource(), CurLine(),
1794 CurLineNumber(), CurOffset() );
1795 }
1796
1797 alt.m_Name = FromUTF8();
1798
1799 token = NextTok();
1800 alt.m_Type = parseType( token );
1801
1802 token = NextTok();
1803 alt.m_Shape = parseShape( token );
1804
1805 pin->GetAlternates()[ alt.m_Name ] = alt;
1806
1807 NeedRIGHT();
1808 break;
1809 }
1810
1811 default:
1812 Expecting( "at, name, number, hide, length, or alternate" );
1813 }
1814 }
1815
1816 return pin.release();
1817}
1818
1819
1821{
1822 wxCHECK_MSG( CurTok() == T_polyline, nullptr,
1823 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a poly." ) );
1824
1825 T token;
1827 FILL_PARAMS fill;
1828 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
1829
1830 poly->SetUnit( m_unit );
1831 poly->SetBodyStyle( m_bodyStyle );
1832
1833 token = NextTok();
1834
1835 if( token == T_private )
1836 {
1837 poly->SetPrivate( true );
1838 token = NextTok();
1839 }
1840
1841 for( ; token != T_RIGHT; token = NextTok() )
1842 {
1843 if( token != T_LEFT )
1844 Expecting( T_LEFT );
1845
1846 token = NextTok();
1847
1848 switch( token )
1849 {
1850 case T_pts:
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 if( token != T_xy )
1859 Expecting( "xy" );
1860
1861 poly->AddPoint( parseXY( true ) );
1862
1863 NeedRIGHT();
1864 }
1865
1866 break;
1867
1868 case T_stroke:
1869 parseStroke( stroke );
1870 poly->SetStroke( stroke );
1871 break;
1872
1873 case T_fill:
1874 parseFill( fill );
1875 poly->SetFillMode( fill.m_FillType );
1876 poly->SetFillColor( fill.m_Color );
1877 break;
1878
1879 default:
1880 Expecting( "pts, stroke, or fill" );
1881 }
1882 }
1883
1884 return poly.release();
1885}
1886
1887
1889{
1890 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
1891 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
1892
1893 T token;
1895 FILL_PARAMS fill;
1896 auto rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
1897
1898 rectangle->SetUnit( m_unit );
1899 rectangle->SetBodyStyle( m_bodyStyle );
1900
1901 token = NextTok();
1902
1903 if( token == T_private )
1904 {
1905 rectangle->SetPrivate( true );
1906 token = NextTok();
1907 }
1908
1909 for( ; token != T_RIGHT; token = NextTok() )
1910 {
1911 if( token != T_LEFT )
1912 Expecting( T_LEFT );
1913
1914 token = NextTok();
1915
1916 switch( token )
1917 {
1918 case T_start:
1919 rectangle->SetPosition( parseXY( true ) );
1920 NeedRIGHT();
1921 break;
1922
1923 case T_end:
1924 rectangle->SetEnd( parseXY( true ) );
1925 NeedRIGHT();
1926 break;
1927
1928 case T_radius:
1929 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
1930 NeedRIGHT();
1931 break;
1932
1933 case T_stroke:
1934 parseStroke( stroke );
1935 rectangle->SetStroke( stroke );
1936 break;
1937
1938 case T_fill:
1939 parseFill( fill );
1940 rectangle->SetFillMode( fill.m_FillType );
1941 rectangle->SetFillColor( fill.m_Color );
1942 break;
1943
1944 default:
1945 Expecting( "start, end, stroke, or fill" );
1946 }
1947 }
1948
1949 return rectangle.release();
1950}
1951
1952
1954{
1955 wxCHECK_MSG( CurTok() == T_text, nullptr,
1956 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text token." ) );
1957
1958 T token;
1959 std::unique_ptr<SCH_TEXT> text = std::make_unique<SCH_TEXT>();
1960
1961 text->SetLayer( LAYER_DEVICE );
1962 text->SetUnit( m_unit );
1963 text->SetBodyStyle( m_bodyStyle );
1964 token = NextTok();
1965
1966 if( token == T_private )
1967 {
1968 text->SetPrivate( true );
1969 token = NextTok();
1970 }
1971
1972 if( !IsSymbol( token ) )
1973 {
1974 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
1975 CurOffset() );
1976 }
1977
1978 text->SetText( FromUTF8() );
1979
1980 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1981 {
1982 if( token != T_LEFT )
1983 Expecting( T_LEFT );
1984
1985 token = NextTok();
1986
1987 switch( token )
1988 {
1989 case T_at:
1990 text->SetPosition( parseXY( true ) );
1991 // Yes, LIB_TEXT is really decidegrees even though all the others are degrees. :(
1992 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), TENTHS_OF_A_DEGREE_T ) );
1993 NeedRIGHT();
1994 break;
1995
1996 case T_effects:
1997 parseEDA_TEXT( text.get(), true );
1998 break;
1999
2000 default:
2001 Expecting( "at or effects" );
2002 }
2003 }
2004
2005 // Convert hidden symbol text (which is no longer supported) into a hidden field
2006 if( !text->IsVisible() )
2007 return new SCH_FIELD( text.get(), FIELD_T::USER );
2008
2009 return text.release();
2010}
2011
2012
2014{
2015 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
2016 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
2017
2018 T token;
2019 VECTOR2I pos;
2020 VECTOR2I end;
2021 VECTOR2I size;
2022 int left;
2023 int top;
2024 int right;
2025 int bottom;
2027 FILL_PARAMS fill;
2028 bool foundEnd = false;
2029 bool foundSize = false;
2030 bool foundMargins = false;
2031
2032 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>( LAYER_DEVICE );
2033
2034 textBox->SetUnit( m_unit );
2035 textBox->SetBodyStyle( m_bodyStyle );
2036 token = NextTok();
2037
2038 if( token == T_private )
2039 {
2040 textBox->SetPrivate( true );
2041 token = NextTok();
2042 }
2043
2044 if( !IsSymbol( token ) )
2045 {
2046 THROW_PARSE_ERROR( _( "Invalid text string" ), CurSource(), CurLine(), CurLineNumber(),
2047 CurOffset() );
2048 }
2049
2050 textBox->SetText( FromUTF8() );
2051
2052 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2053 {
2054 if( token != T_LEFT )
2055 Expecting( T_LEFT );
2056
2057 token = NextTok();
2058
2059 switch( token )
2060 {
2061 case T_start: // Legacy token during 6.99 development; fails to handle angle
2062 pos = parseXY( true );
2063 NeedRIGHT();
2064 break;
2065
2066 case T_end: // Legacy token during 6.99 development; fails to handle angle
2067 end = parseXY( true );
2068 foundEnd = true;
2069 NeedRIGHT();
2070 break;
2071
2072 case T_at:
2073 pos = parseXY( true );
2074 textBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
2075 NeedRIGHT();
2076 break;
2077
2078 case T_size:
2079 size = parseXY( true );
2080 foundSize = true;
2081 NeedRIGHT();
2082 break;
2083
2084 case T_stroke:
2085 parseStroke( stroke );
2086 textBox->SetStroke( stroke );
2087 break;
2088
2089 case T_fill:
2090 parseFill( fill );
2091 textBox->SetFillMode( fill.m_FillType );
2092 textBox->SetFillColor( fill.m_Color );
2093 break;
2094
2095 case T_margins:
2096 parseMargins( left, top, right, bottom );
2097 textBox->SetMarginLeft( left );
2098 textBox->SetMarginTop( top );
2099 textBox->SetMarginRight( right );
2100 textBox->SetMarginBottom( bottom );
2101 foundMargins = true;
2102 NeedRIGHT();
2103 break;
2104
2105 case T_effects:
2106 parseEDA_TEXT( static_cast<EDA_TEXT*>( textBox.get() ), false );
2107 break;
2108
2109 default:
2110 Expecting( "at, size, stroke, fill or effects" );
2111 }
2112 }
2113
2114 textBox->SetPosition( pos );
2115
2116 if( foundEnd )
2117 textBox->SetEnd( end );
2118 else if( foundSize )
2119 textBox->SetEnd( pos + size );
2120 else
2121 Expecting( "size" );
2122
2123 if( !foundMargins )
2124 {
2125 int margin = textBox->GetLegacyTextMargin();
2126 textBox->SetMarginLeft( margin );
2127 textBox->SetMarginTop( margin );
2128 textBox->SetMarginRight( margin );
2129 textBox->SetMarginBottom( margin );
2130 }
2131
2132 return textBox.release();
2133}
2134
2135
2137{
2138 wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200506 ) || CurTok() == T_paper,
2139 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
2140
2141 T token;
2142
2143 NeedSYMBOL();
2144
2145 wxString pageType = FromUTF8();
2146
2147 if( !aPageInfo.SetType( pageType ) )
2148 {
2149 THROW_PARSE_ERROR( _( "Invalid page type" ), CurSource(), CurLine(), CurLineNumber(),
2150 CurOffset() );
2151 }
2152
2153 if( aPageInfo.GetType() == PAGE_SIZE_TYPE::User )
2154 {
2155 double width = parseDouble( "width" );
2156
2157 // Perform some controls to avoid crashes if the size is edited by hands
2158 if( width < MIN_PAGE_SIZE_MM )
2159 width = MIN_PAGE_SIZE_MM;
2160 else if( width > MAX_PAGE_SIZE_EESCHEMA_MM )
2162
2163 double height = parseDouble( "height" );
2164
2165 if( height < MIN_PAGE_SIZE_MM )
2166 height = MIN_PAGE_SIZE_MM;
2167 else if( height > MAX_PAGE_SIZE_EESCHEMA_MM )
2169
2170 aPageInfo.SetWidthMM( width );
2171 aPageInfo.SetHeightMM( height );
2172 }
2173
2174 token = NextTok();
2175
2176 if( token == T_portrait )
2177 {
2178 aPageInfo.SetPortrait( true );
2179 NeedRIGHT();
2180 }
2181 else if( token != T_RIGHT )
2182 {
2183 Expecting( "portrait" );
2184 }
2185}
2186
2187
2189{
2190 wxCHECK_RET( CurTok() == T_title_block,
2191 "Cannot parse " + GetTokenString( CurTok() ) + " as a TITLE_BLOCK." );
2192
2193 T token;
2194
2195 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2196 {
2197 if( token != T_LEFT )
2198 Expecting( T_LEFT );
2199
2200 token = NextTok();
2201
2202 switch( token )
2203 {
2204 case T_title:
2205 NextTok();
2206 aTitleBlock.SetTitle( FromUTF8() );
2207 break;
2208
2209 case T_date:
2210 NextTok();
2211 aTitleBlock.SetDate( FromUTF8() );
2212 break;
2213
2214 case T_rev:
2215 NextTok();
2216 aTitleBlock.SetRevision( FromUTF8() );
2217 break;
2218
2219 case T_company:
2220 NextTok();
2221 aTitleBlock.SetCompany( FromUTF8() );
2222 break;
2223
2224 case T_comment:
2225 {
2226 int commentNumber = parseInt( "comment" );
2227
2228 switch( commentNumber )
2229 {
2230 case 1:
2231 NextTok();
2232 aTitleBlock.SetComment( 0, FromUTF8() );
2233 break;
2234
2235 case 2:
2236 NextTok();
2237 aTitleBlock.SetComment( 1, FromUTF8() );
2238 break;
2239
2240 case 3:
2241 NextTok();
2242 aTitleBlock.SetComment( 2, FromUTF8() );
2243 break;
2244
2245 case 4:
2246 NextTok();
2247 aTitleBlock.SetComment( 3, FromUTF8() );
2248 break;
2249
2250 case 5:
2251 NextTok();
2252 aTitleBlock.SetComment( 4, FromUTF8() );
2253 break;
2254
2255 case 6:
2256 NextTok();
2257 aTitleBlock.SetComment( 5, FromUTF8() );
2258 break;
2259
2260 case 7:
2261 NextTok();
2262 aTitleBlock.SetComment( 6, FromUTF8() );
2263 break;
2264
2265 case 8:
2266 NextTok();
2267 aTitleBlock.SetComment( 7, FromUTF8() );
2268 break;
2269
2270 case 9:
2271 NextTok();
2272 aTitleBlock.SetComment( 8, FromUTF8() );
2273 break;
2274
2275 default:
2276 THROW_PARSE_ERROR( _( "Invalid title block comment number" ), CurSource(),
2277 CurLine(), CurLineNumber(), CurOffset() );
2278 }
2279
2280 break;
2281 }
2282
2283 default:
2284 Expecting( "title, date, rev, company, or comment" );
2285 }
2286
2287 NeedRIGHT();
2288 }
2289}
2290
2291
2293{
2294 wxCHECK_MSG( CurTok() == T_property, nullptr,
2295 "Cannot parse " + GetTokenString( CurTok() ) + " as a property token." );
2296
2297 bool is_private = false;
2298
2299 T token = NextTok();
2300
2301 if( token == T_private )
2302 {
2303 is_private = true;
2304 token = NextTok();
2305 }
2306
2307 if( !IsSymbol( token ) )
2308 {
2309 THROW_PARSE_ERROR( _( "Invalid property name" ), CurSource(), CurLine(), CurLineNumber(),
2310 CurOffset() );
2311 }
2312
2313 wxString name = FromUTF8();
2314
2315 if( name.IsEmpty() )
2316 {
2317 THROW_PARSE_ERROR( _( "Empty property name" ), CurSource(), CurLine(), CurLineNumber(),
2318 CurOffset() );
2319 }
2320
2321 token = NextTok();
2322
2323 if( !IsSymbol( token ) )
2324 {
2325 THROW_PARSE_ERROR( _( "Invalid property value" ), CurSource(), CurLine(), CurLineNumber(),
2326 CurOffset() );
2327 }
2328
2329 // Empty property values are valid.
2330 wxString value;
2331
2332 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2333 value = wxEmptyString;
2334 else
2335 value = FromUTF8();
2336
2337 FIELD_T fieldId = FIELD_T::USER;
2338
2339 // Correctly set the ID based on canonical (untranslated) field name
2340 if( aParent->Type() == SCH_SYMBOL_T )
2341 {
2342 for( FIELD_T id : MANDATORY_FIELDS )
2343 {
2344 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2345 {
2346 fieldId = id;
2347 break;
2348 }
2349 }
2350 }
2351 else if( aParent->Type() == SCH_SHEET_T )
2352 {
2353 fieldId = FIELD_T::SHEET_USER; // This is the default id for user fields
2354
2356 {
2357 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2358 {
2359 fieldId = id;
2360 break;
2361 }
2362 }
2363
2364 // Legacy support for old field names
2365 if( name.CmpNoCase( wxT( "Sheet name" ) ) == 0 )
2366 fieldId = FIELD_T::SHEET_NAME;
2367 else if( name.CmpNoCase( wxT( "Sheet file" ) ) == 0 )
2368 fieldId = FIELD_T::SHEET_FILENAME;
2369 }
2370 else if( aParent->Type() == SCH_GLOBAL_LABEL_T )
2371 {
2373 {
2374 if( name.CmpNoCase( GetCanonicalFieldName( id ) ) == 0 )
2375 {
2376 fieldId = id;
2377 break;
2378 }
2379 }
2380
2381 // Legacy support for old field names
2382 if( name.CmpNoCase( wxT( "Intersheet References" ) ) == 0 )
2383 fieldId = FIELD_T::INTERSHEET_REFS;
2384 }
2385
2386 std::unique_ptr<SCH_FIELD> field = std::make_unique<SCH_FIELD>( aParent, fieldId, name );
2387 field->SetText( value );
2388
2389 if( fieldId == FIELD_T::USER )
2390 field->SetPrivate( is_private );
2391
2392 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2393 {
2394 if( token != T_LEFT )
2395 Expecting( T_LEFT );
2396
2397 token = NextTok();
2398
2399 switch( token )
2400 {
2401 case T_id: // legacy token; ignore
2402 parseInt( "field ID" );
2403 NeedRIGHT();
2404 break;
2405
2406 case T_at:
2407 field->SetPosition( parseXY() );
2408 field->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ) );
2409 NeedRIGHT();
2410 break;
2411
2412 case T_hide:
2413 field->SetVisible( !parseBool() );
2414 NeedRIGHT();
2415 break;
2416
2417 case T_effects:
2418 parseEDA_TEXT( static_cast<EDA_TEXT*>( field.get() ),
2419 field->GetId() == FIELD_T::VALUE );
2420 break;
2421
2422 case T_show_name:
2423 {
2424 bool show = parseMaybeAbsentBool( true );
2425 field->SetNameShown( show );
2426 break;
2427 }
2428
2429 case T_do_not_autoplace:
2430 {
2431 bool doNotAutoplace = parseMaybeAbsentBool( true );
2432 field->SetCanAutoplace( !doNotAutoplace );
2433 break;
2434 }
2435
2436 default:
2437 Expecting( "id, at, hide, show_name, do_not_autoplace or effects" );
2438 }
2439 }
2440
2441 return field.release();
2442}
2443
2444
2446{
2447 wxCHECK_MSG( aSheet != nullptr, nullptr, "" );
2448 wxCHECK_MSG( CurTok() == T_pin, nullptr,
2449 "Cannot parse " + GetTokenString( CurTok() ) + " as a sheet pin token." );
2450
2451 T token = NextTok();
2452
2453 if( !IsSymbol( token ) )
2454 {
2455 THROW_PARSE_ERROR( _( "Invalid sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2456 CurOffset() );
2457 }
2458
2459 wxString name = FromUTF8();
2460
2461 if( name.IsEmpty() )
2462 {
2463 THROW_PARSE_ERROR( _( "Empty sheet pin name" ), CurSource(), CurLine(), CurLineNumber(),
2464 CurOffset() );
2465 }
2466
2467 auto sheetPin = std::make_unique<SCH_SHEET_PIN>( aSheet, VECTOR2I( 0, 0 ), name );
2468
2469 token = NextTok();
2470
2471 switch( token )
2472 {
2473 case T_input: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
2474 case T_output: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
2475 case T_bidirectional: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
2476 case T_tri_state: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
2477 case T_passive: sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
2478 default:
2479 Expecting( "input, output, bidirectional, tri_state, or passive" );
2480 }
2481
2482 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2483 {
2484 if( token != T_LEFT )
2485 Expecting( T_LEFT );
2486
2487 token = NextTok();
2488
2489 switch( token )
2490 {
2491 case T_at:
2492 {
2493 sheetPin->SetPosition( parseXY() );
2494
2495 double angle = parseDouble( "sheet pin angle (side)" );
2496
2497 if( angle == 0.0 )
2498 sheetPin->SetSide( SHEET_SIDE::RIGHT );
2499 else if( angle == 90.0 )
2500 sheetPin->SetSide( SHEET_SIDE::TOP );
2501 else if( angle == 180.0 )
2502 sheetPin->SetSide( SHEET_SIDE::LEFT );
2503 else if( angle == 270.0 )
2504 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
2505 else
2506 Expecting( "0, 90, 180, or 270" );
2507
2508 NeedRIGHT();
2509 break;
2510 }
2511
2512 case T_effects:
2513 parseEDA_TEXT( static_cast<EDA_TEXT*>( sheetPin.get() ), true );
2514 break;
2515
2516 case T_uuid:
2517 NeedSYMBOL();
2518 const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
2519 NeedRIGHT();
2520 break;
2521
2522 default:
2523 Expecting( "at, uuid or effects" );
2524 }
2525 }
2526
2527 return sheetPin.release();
2528}
2529
2530
2532{
2533 wxCHECK_RET( CurTok() == T_sheet_instances,
2534 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2535 wxCHECK( aScreen, /* void */ );
2536
2537 T token;
2538
2539 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2540 {
2541 if( token != T_LEFT )
2542 Expecting( T_LEFT );
2543
2544 token = NextTok();
2545
2546 switch( token )
2547 {
2548 case T_path:
2549 {
2550 NeedSYMBOL();
2551
2552 SCH_SHEET_INSTANCE instance;
2553
2554 instance.m_Path = KIID_PATH( FromUTF8() );
2555
2556 if( ( !m_appending && aRootSheet->GetScreen() == aScreen ) &&
2557 ( aScreen->GetFileFormatVersionAtLoad() < 20221002 ) )
2558 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2559
2560 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2561 {
2562 if( token != T_LEFT )
2563 Expecting( T_LEFT );
2564
2565 token = NextTok();
2566
2567 std::vector<wxString> whitespaces = { wxT( "\r" ), wxT( "\n" ), wxT( "\t" ),
2568 wxT( " " ) };
2569
2570 size_t numReplacements = 0;
2571
2572 switch( token )
2573 {
2574 case T_page:
2575 NeedSYMBOL();
2576 instance.m_PageNumber = FromUTF8();
2577
2578 // Empty page numbers are not permitted
2579 if( instance.m_PageNumber.IsEmpty() )
2580 {
2581 // Use hash character instead
2582 instance.m_PageNumber = wxT( "#" );
2583 numReplacements++;
2584 }
2585 else
2586 {
2587 // Whitespaces are not permitted
2588 for( const wxString& ch : whitespaces )
2589 numReplacements += instance.m_PageNumber.Replace( ch, wxEmptyString );
2590
2591 }
2592
2593 // Set the file as modified so the user can be warned.
2594 if( numReplacements > 0 )
2595 aScreen->SetContentModified();
2596
2597 NeedRIGHT();
2598 break;
2599
2600 default:
2601 Expecting( "path or page" );
2602 }
2603 }
2604
2605 if( ( aScreen->GetFileFormatVersionAtLoad() >= 20221110 )
2606 && ( instance.m_Path.empty() ) )
2607 {
2608 SCH_SHEET_PATH rootSheetPath;
2609
2610 rootSheetPath.push_back( aRootSheet );
2611 rootSheetPath.SetPageNumber( instance.m_PageNumber );
2612 }
2613 else
2614 {
2615 aScreen->m_sheetInstances.emplace_back( instance );
2616 }
2617
2618 break;
2619 }
2620
2621 default:
2622 Expecting( "path" );
2623 }
2624 }
2625}
2626
2627
2629{
2630 wxCHECK_RET( CurTok() == T_symbol_instances,
2631 "Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
2632 wxCHECK( aScreen, /* void */ );
2633 wxCHECK( m_rootUuid != NilUuid(), /* void */ );
2634
2635 T token;
2636
2637 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2638 {
2639 if( token != T_LEFT )
2640 Expecting( T_LEFT );
2641
2642 token = NextTok();
2643
2644 switch( token )
2645 {
2646 case T_path:
2647 {
2648 NeedSYMBOL();
2649
2650 SCH_SYMBOL_INSTANCE instance;
2651
2652 instance.m_Path = KIID_PATH( FromUTF8() );
2653
2654 if( !m_appending )
2655 instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
2656
2657 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2658 {
2659 if( token != T_LEFT )
2660 Expecting( T_LEFT );
2661
2662 token = NextTok();
2663
2664 switch( token )
2665 {
2666 case T_reference:
2667 NeedSYMBOL();
2668 instance.m_Reference = FromUTF8();
2669 NeedRIGHT();
2670 break;
2671
2672 case T_unit:
2673 instance.m_Unit = parseInt( "symbol unit" );
2674 NeedRIGHT();
2675 break;
2676
2677 case T_value:
2678 NeedSYMBOL();
2679
2680 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2681 instance.m_Value = wxEmptyString;
2682 else
2683 instance.m_Value = FromUTF8();
2684
2685 NeedRIGHT();
2686 break;
2687
2688 case T_footprint:
2689 NeedSYMBOL();
2690
2691 if( m_requiredVersion < 20250318 && CurStr() == "~" )
2692 instance.m_Footprint = wxEmptyString;
2693 else
2694 instance.m_Footprint = FromUTF8();
2695
2696 NeedRIGHT();
2697 break;
2698
2699 default:
2700 Expecting( "path, unit, value or footprint" );
2701 }
2702 }
2703
2704 aScreen->m_symbolInstances.emplace_back( instance );
2705 break;
2706 }
2707
2708 default:
2709 Expecting( "path" );
2710 }
2711 }
2712}
2713
2714
2715void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
2716 int aFileVersion )
2717{
2718 wxCHECK( aSheet != nullptr, /* void */ );
2719
2720 SCH_SCREEN* screen = aSheet->GetScreen();
2721
2722 wxCHECK( screen != nullptr, /* void */ );
2723
2724 if( SCHEMATIC* schematic = dynamic_cast<SCHEMATIC*>( screen->GetParent() ) )
2725 m_maxError = schematic->Settings().m_MaxError;
2726
2727 if( aIsCopyableOnly )
2728 m_requiredVersion = aFileVersion;
2729
2730 bool fileHasUuid = false;
2731
2732 auto checkVersion =
2733 [&]()
2734 {
2736 {
2737 throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
2739 }
2740 };
2741
2742 T token;
2743
2744 if( !aIsCopyableOnly )
2745 {
2746 NeedLEFT();
2747 NextTok();
2748
2749 if( CurTok() != T_kicad_sch )
2750 Expecting( "kicad_sch" );
2751
2753
2754 // Prior to this, bar was a valid string char for unquoted strings.
2755 SetKnowsBar( m_requiredVersion >= 20240620 );
2756
2757 // Prior to schematic file version 20210406, schematics did not have UUIDs so we need
2758 // to generate one for the root schematic for instance paths.
2759 if( m_requiredVersion < 20210406 )
2760 m_rootUuid = screen->GetUuid();
2761
2762 // Prior to version 20231120, generator_version was not present, so check the date here
2763 if( m_requiredVersion < 20231120 )
2764 checkVersion();
2765 }
2766
2768
2769 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2770 {
2771 if( aIsCopyableOnly && token == T_EOF )
2772 break;
2773
2774 if( token != T_LEFT )
2775 Expecting( T_LEFT );
2776
2777 token = NextTok();
2778
2779 checkpoint();
2780
2781 if( !aIsCopyableOnly && token == T_page && m_requiredVersion <= 20200506 )
2782 token = T_paper;
2783
2784 switch( token )
2785 {
2786 case T_group:
2787 parseGroup();
2788 break;
2789
2790 case T_generator:
2791 // (generator "genname"); we don't care about it at the moment.
2792 NeedSYMBOL();
2793 NeedRIGHT();
2794 break;
2795
2796 case T_host:
2797 {
2798 // (host eeschema ["5.99"]); old version of generator token
2799 NeedSYMBOL();
2800
2801 // Really old versions also included a host version
2802 if( m_requiredVersion < 20200827 )
2803 NeedSYMBOL();
2804
2805 NeedRIGHT();
2806 break;
2807 }
2808
2809 case T_generator_version:
2810 NextTok();
2811 m_generatorVersion = FromUTF8();
2812 NeedRIGHT();
2813 checkVersion();
2814 break;
2815
2816 case T_uuid:
2817 NeedSYMBOL();
2818 screen->m_uuid = parseKIID();
2819
2820 // Set the root sheet UUID with the schematic file UUID. Root sheets are virtual
2821 // and always get a new UUID so this prevents file churn now that the root UUID
2822 // is saved in the symbol instance path.
2823 if( aSheet == m_rootSheet )
2824 {
2825 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
2826 m_rootUuid = screen->GetUuid();
2827 fileHasUuid = true;
2828 }
2829
2830 NeedRIGHT();
2831 break;
2832
2833 case T_paper:
2834 {
2835 if( aIsCopyableOnly )
2836 Unexpected( T_paper );
2837
2838 PAGE_INFO pageInfo;
2839 parsePAGE_INFO( pageInfo );
2840 screen->SetPageSettings( pageInfo );
2841 break;
2842 }
2843
2844 case T_page:
2845 {
2846 if( aIsCopyableOnly )
2847 Unexpected( T_page );
2848
2849 // Only saved for top-level sniffing in Kicad Manager frame and other external
2850 // tool usage with flat hierarchies
2851 NeedSYMBOLorNUMBER();
2852 NeedSYMBOLorNUMBER();
2853 NeedRIGHT();
2854 break;
2855 }
2856
2857 case T_title_block:
2858 {
2859 if( aIsCopyableOnly )
2860 Unexpected( T_title_block );
2861
2862 TITLE_BLOCK tb;
2863 parseTITLE_BLOCK( tb );
2864 screen->SetTitleBlock( tb );
2865 break;
2866 }
2867
2868 case T_lib_symbols:
2869 {
2870 // Dummy map. No derived symbols are allowed in the library cache.
2871 LIB_SYMBOL_MAP symbolLibMap;
2872 LIB_SYMBOL* symbol;
2873
2874 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2875 {
2876 if( token != T_LEFT )
2877 Expecting( T_LEFT );
2878
2879 token = NextTok();
2880
2881 switch( token )
2882 {
2883 case T_symbol:
2884 symbol = parseLibSymbol( symbolLibMap );
2885 screen->AddLibSymbol( symbol );
2886 break;
2887
2888 default:
2889 Expecting( "symbol" );
2890 }
2891 }
2892
2893 break;
2894 }
2895
2896 case T_symbol:
2897 screen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
2898 break;
2899
2900 case T_image:
2901 screen->Append( static_cast<SCH_ITEM*>( parseImage() ) );
2902 break;
2903
2904 case T_sheet:
2905 {
2906 SCH_SHEET* sheet = parseSheet();
2907
2908 // Set the parent to aSheet. This effectively creates a method to find
2909 // the root sheet from any sheet so a pointer to the root sheet does not
2910 // need to be stored globally. Note: this is not the same as a hierarchy.
2911 // Complex hierarchies can have multiple copies of a sheet. This only
2912 // provides a simple tree to find the root sheet.
2913 sheet->SetParent( aSheet );
2914 screen->Append( sheet );
2915 break;
2916 }
2917
2918 case T_junction:
2919 screen->Append( parseJunction() );
2920 break;
2921
2922 case T_no_connect:
2923 screen->Append( parseNoConnect() );
2924 break;
2925
2926 case T_bus_entry:
2927 screen->Append( parseBusEntry() );
2928 break;
2929
2930 case T_polyline:
2931 {
2932 // polyline keyword is used in eeschema both for SCH_SHAPE and SCH_LINE items.
2933 // In symbols it describes a polygon, having n corners and can be filled
2934 // In schematic it describes a line (with no fill descr), but could be extended to a
2935 // polygon (for instance when importing files) because the schematic handles all
2936 // SCH_SHAPE.
2937
2938 // parseSchPolyLine() returns always a SCH_SHAPE, io convert it to a simple SCH_LINE
2939 // For compatibility reasons, keep SCH_SHAPE for a polygon and convert to SCH_LINE
2940 // when the item has only 2 corners, similar to a SCH_LINE
2941 SCH_SHAPE* poly = parseSchPolyLine();
2942
2943 if( poly->GetPointCount() < 2 )
2944 {
2945 delete poly;
2946 THROW_PARSE_ERROR( _( "Schematic polyline has too few points" ), CurSource(), CurLine(),
2947 CurLineNumber(), CurOffset() );
2948 }
2949 else if( poly->GetPointCount() > 2 )
2950 {
2951 screen->Append( poly );
2952 }
2953 else
2954 {
2955 // For SCH_SHAPE having only 2 points, this is a "old" SCH_LINE entity.
2956 // So convert the SCH_SHAPE to a simple SCH_LINE
2957 SCH_LINE* line = new SCH_LINE( VECTOR2I(), LAYER_NOTES );
2958 SHAPE_LINE_CHAIN& outline = poly->GetPolyShape().Outline(0);
2959 line->SetStartPoint( outline.CPoint(0) );
2960 line->SetEndPoint( outline.CPoint(1) );
2961 line->SetStroke( poly->GetStroke() );
2962 line->SetLocked( poly->IsLocked() );
2963 const_cast<KIID&>( line->m_Uuid ) = poly->m_Uuid;
2964
2965 screen->Append( line );
2966
2967 delete poly;
2968 }
2969 }
2970 break;
2971
2972 case T_bus:
2973 case T_wire:
2974 screen->Append( parseLine() );
2975 break;
2976
2977 case T_arc:
2978 screen->Append( parseSchArc() );
2979 break;
2980
2981 case T_circle:
2982 screen->Append( parseSchCircle() );
2983 break;
2984
2985 case T_rectangle:
2986 screen->Append( parseSchRectangle() );
2987 break;
2988
2989 case T_bezier:
2990 screen->Append( parseSchBezier() );
2991 break;
2992
2993 case T_rule_area:
2994 screen->Append( parseSchRuleArea() );
2995 break;
2996
2997 case T_netclass_flag: // present only during early development of 7.0
2999
3000 case T_text:
3001 case T_label:
3002 case T_global_label:
3003 case T_hierarchical_label:
3004 case T_directive_label:
3005 screen->Append( parseSchText() );
3006 break;
3007
3008 case T_text_box:
3009 screen->Append( parseSchTextBox() );
3010 break;
3011
3012 case T_table:
3013 screen->Append( parseSchTable() );
3014 break;
3015
3016 case T_sheet_instances:
3017 parseSchSheetInstances( aSheet, screen );
3018 break;
3019
3020 case T_symbol_instances:
3021 parseSchSymbolInstances( screen );
3022 break;
3023
3024 case T_bus_alias:
3025 if( aIsCopyableOnly )
3026 Unexpected( T_bus_alias );
3027
3028 parseBusAlias( screen );
3029 break;
3030
3031 case T_embedded_fonts:
3032 {
3033 SCHEMATIC* schematic = screen->Schematic();
3034
3035 if( !schematic )
3036 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3037 CurLineNumber(), CurOffset() );
3038
3040 NeedRIGHT();
3041 break;
3042 }
3043
3044 case T_embedded_files:
3045 {
3046 SCHEMATIC* schematic = screen->Schematic();
3047
3048 if( !schematic )
3049 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3050 CurLineNumber(), CurOffset() );
3051
3052 EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
3053 embeddedFilesParser.SyncLineReaderWith( *this );
3054
3055 try
3056 {
3057 embeddedFilesParser.ParseEmbedded( schematic->GetEmbeddedFiles() );
3058 }
3059 catch( const PARSE_ERROR& e )
3060 {
3061 m_parseWarnings.push_back( e.What() );
3062
3063 int depth = 0;
3064
3065 for( int tok = embeddedFilesParser.NextTok();
3066 tok != DSN_EOF;
3067 tok = embeddedFilesParser.NextTok() )
3068 {
3069 if( tok == DSN_LEFT )
3070 depth++;
3071 else if( tok == DSN_RIGHT && --depth < 0 )
3072 break;
3073 }
3074 }
3075
3076 SyncLineReaderWith( embeddedFilesParser );
3077 break;
3078 }
3079
3080
3081 default:
3082 Expecting( "bitmap, bus, bus_alias, bus_entry, class_label, embedded_files, global_label, "
3083 "hierarchical_label, junction, label, line, no_connect, page, paper, rule_area, "
3084 "sheet, symbol, symbol_instances, text, title_block" );
3085 }
3086 }
3087
3088 // Older s-expression schematics may not have a UUID so use the one automatically generated
3089 // as the virtual root sheet UUID.
3090 if( ( aSheet == m_rootSheet ) && !fileHasUuid )
3091 {
3092 const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
3093 m_rootUuid = screen->GetUuid();
3094 }
3095
3096 screen->UpdateLocalLibSymbolLinks();
3097 screen->FixupEmbeddedData();
3098
3099 resolveGroups( screen );
3100
3101 SCHEMATIC* schematic = screen->Schematic();
3102
3103 if( !schematic )
3104 THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
3105 CurLineNumber(), CurOffset() );
3106
3107 // When loading the schematic, take a moment to cache the fonts so that the font
3108 // picker can show the embedded fonts immediately.
3109 std::vector<std::string> fontNames;
3110 Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
3111 schematic->GetEmbeddedFiles()->GetFontFiles(), true );
3112
3113 if( m_requiredVersion < 20200828 )
3115}
3116
3117
3119{
3120 wxCHECK_MSG( CurTok() == T_symbol, nullptr,
3121 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
3122
3123 T token;
3124 wxString libName;
3125 SCH_FIELD* field;
3126 std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
3127 TRANSFORM transform;
3128 std::set<int> fieldIDsRead;
3129
3130 // We'll reset this if we find a fields_autoplaced token
3131 symbol->SetFieldsAutoplaced( AUTOPLACE_NONE );
3132
3133 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3134 {
3135 if( token != T_LEFT )
3136 Expecting( T_LEFT );
3137
3138 token = NextTok();
3139
3140 switch( token )
3141 {
3142 case T_lib_name:
3143 {
3144 LIB_ID libId;
3145
3146 token = NextTok();
3147
3148 if( !IsSymbol( token ) )
3149 {
3150 THROW_PARSE_ERROR( _( "Invalid symbol library name" ), CurSource(), CurLine(),
3151 CurLineNumber(), CurOffset() );
3152 }
3153
3154 libName = FromUTF8();
3155 // Some symbol LIB_IDs or lib_name have the '/' character escaped which can break
3156 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3157 // it doesn't need to be escaped.
3158 libName.Replace( "{slash}", "/" );
3159
3160 NeedRIGHT();
3161 break;
3162 }
3163
3164 case T_lib_id:
3165 {
3166 token = NextTok();
3167
3168 if( !IsSymbol( token ) && token != T_NUMBER )
3169 Expecting( "symbol|number" );
3170
3171 LIB_ID libId;
3172 wxString name = FromUTF8();
3173 // Some symbol LIB_IDs have the '/' character escaped which can break
3174 // symbol links. The '/' character is no longer an illegal LIB_ID character so
3175 // it doesn't need to be escaped.
3176 name.Replace( "{slash}", "/" );
3177
3178 int bad_pos = libId.Parse( name );
3179
3180 if( bad_pos >= 0 )
3181 {
3182 if( static_cast<int>( name.size() ) > bad_pos )
3183 {
3184 wxString msg = wxString::Format( _( "Symbol %s contains invalid character '%c'" ),
3185 name,
3186 name[bad_pos] );
3187
3188 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
3189 }
3190
3191 THROW_PARSE_ERROR( _( "Invalid symbol library ID" ), CurSource(), CurLine(),
3192 CurLineNumber(), CurOffset() );
3193 }
3194
3195 symbol->SetLibId( libId );
3196 NeedRIGHT();
3197 break;
3198 }
3199
3200 case T_at:
3201 symbol->SetPosition( parseXY() );
3202
3203 switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
3204 {
3205 case 0: transform = TRANSFORM(); break;
3206 case 90: transform = TRANSFORM( 0, 1, -1, 0 ); break;
3207 case 180: transform = TRANSFORM( -1, 0, 0, -1 ); break;
3208 case 270: transform = TRANSFORM( 0, -1, 1, 0 ); break;
3209 default: Expecting( "0, 90, 180, or 270" );
3210 }
3211
3212 symbol->SetTransform( transform );
3213 NeedRIGHT();
3214 break;
3215
3216 case T_mirror:
3217 token = NextTok();
3218
3219 if( token == T_x )
3220 symbol->SetOrientation( SYM_MIRROR_X );
3221 else if( token == T_y )
3222 symbol->SetOrientation( SYM_MIRROR_Y );
3223 else
3224 Expecting( "x or y" );
3225
3226 NeedRIGHT();
3227 break;
3228
3229 case T_unit:
3230 symbol->SetUnit( parseInt( "symbol unit" ) );
3231 NeedRIGHT();
3232 break;
3233
3234 case T_convert: // Legacy token
3235 case T_body_style:
3236 symbol->SetBodyStyle( parseInt( "symbol body style" ) );
3237 NeedRIGHT();
3238 break;
3239
3240 case T_exclude_from_sim:
3241 symbol->SetExcludedFromSim( parseBool() );
3242 NeedRIGHT();
3243 break;
3244
3245 case T_in_bom:
3246 symbol->SetExcludedFromBOM( !parseBool() );
3247 NeedRIGHT();
3248 break;
3249
3250 case T_on_board:
3251 symbol->SetExcludedFromBoard( !parseBool() );
3252 NeedRIGHT();
3253 break;
3254
3255 case T_in_pos_files:
3256 symbol->SetExcludedFromPosFiles( !parseBool() );
3257 NeedRIGHT();
3258 break;
3259
3260 case T_dnp:
3261 symbol->SetDNP( parseBool() );
3262 NeedRIGHT();
3263 break;
3264
3265 case T_locked:
3266 symbol->SetLocked( parseBool() );
3267 NeedRIGHT();
3268 break;
3269
3270 case T_fields_autoplaced:
3271 if( parseMaybeAbsentBool( true ) )
3272 symbol->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3273
3274 break;
3275
3276 case T_uuid:
3277 NeedSYMBOL();
3278 const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
3279 NeedRIGHT();
3280 break;
3281
3282 case T_default_instance:
3283 {
3284 SCH_SYMBOL_INSTANCE defaultInstance;
3285
3286 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3287 {
3288 if( token != T_LEFT )
3289 Expecting( T_LEFT );
3290
3291 token = NextTok();
3292
3293 switch( token )
3294 {
3295 case T_reference:
3296 NeedSYMBOL();
3297 defaultInstance.m_Reference = FromUTF8();
3298 NeedRIGHT();
3299 break;
3300
3301 case T_unit:
3302 defaultInstance.m_Unit = parseInt( "symbol unit" );
3303 NeedRIGHT();
3304 break;
3305
3306 case T_value:
3307 NeedSYMBOL();
3308
3309 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3310 symbol->SetValueFieldText( wxEmptyString );
3311 else
3312 symbol->SetValueFieldText( FromUTF8() );
3313
3314 NeedRIGHT();
3315 break;
3316
3317 case T_footprint:
3318 NeedSYMBOL();
3319
3320 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3321 symbol->SetFootprintFieldText( wxEmptyString );
3322 else
3323 symbol->SetFootprintFieldText( FromUTF8() );
3324
3325 NeedRIGHT();
3326 break;
3327
3328 default:
3329 Expecting( "reference, unit, value or footprint" );
3330 }
3331 }
3332
3333 break;
3334 }
3335
3336 case T_instances:
3337 {
3338 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3339 {
3340 if( token != T_LEFT )
3341 Expecting( T_LEFT );
3342
3343 token = NextTok();
3344
3345 if( token != T_project )
3346 Expecting( "project" );
3347
3348 NeedSYMBOL();
3349
3350 wxString projectName = FromUTF8();
3351
3352 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3353 {
3354 if( token != T_LEFT )
3355 Expecting( T_LEFT );
3356
3357 token = NextTok();
3358
3359 if( token != T_path )
3360 Expecting( "path" );
3361
3362 SCH_SYMBOL_INSTANCE instance;
3363
3364 instance.m_ProjectName = projectName;
3365
3366 NeedSYMBOL();
3367 instance.m_Path = KIID_PATH( FromUTF8() );
3368
3369 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3370 {
3371 if( token != T_LEFT )
3372 Expecting( T_LEFT );
3373
3374 token = NextTok();
3375
3376 switch( token )
3377 {
3378 case T_reference:
3379 NeedSYMBOL();
3380 instance.m_Reference = FromUTF8();
3381 NeedRIGHT();
3382 break;
3383
3384 case T_unit:
3385 instance.m_Unit = parseInt( "symbol unit" );
3386 NeedRIGHT();
3387 break;
3388
3389 case T_value:
3390 NeedSYMBOL();
3391
3392 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3393 symbol->SetValueFieldText( wxEmptyString );
3394 else
3395 symbol->SetValueFieldText( FromUTF8() );
3396
3397 NeedRIGHT();
3398 break;
3399
3400 case T_footprint:
3401 NeedSYMBOL();
3402
3403 if( m_requiredVersion < 20250318 && CurStr() == "~" )
3404 symbol->SetFootprintFieldText( wxEmptyString );
3405 else
3406 symbol->SetFootprintFieldText( FromUTF8() );
3407
3408 NeedRIGHT();
3409 break;
3410
3411
3412 case T_variant:
3413 {
3414 SCH_SYMBOL_VARIANT variant;
3415 variant.InitializeAttributes( *symbol );
3416
3417 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3418 {
3419 if( token != T_LEFT )
3420 Expecting( T_LEFT );
3421
3422 token = NextTok();
3423
3424 switch( token )
3425 {
3426 case T_name:
3427 NeedSYMBOL();
3428 variant.m_Name = FromUTF8();
3429 NeedRIGHT();
3430 break;
3431
3432 case T_dnp:
3433 variant.m_DNP = parseBool();
3434 NeedRIGHT();
3435 break;
3436
3437 case T_exclude_from_sim:
3438 variant.m_ExcludedFromSim = parseBool();
3439 NeedRIGHT();
3440 break;
3441
3442 case T_in_bom:
3443 variant.m_ExcludedFromBOM = parseBool();
3444
3445 // This fixes the incorrect logic from prior file versions. The "in_bom" token
3446 // used in the file format is the positive logic. However, in the UI the term
3447 // excluded from BOM is used which is the inverted logic.
3448 if( m_requiredVersion >= 20260306 )
3449 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
3450
3451 NeedRIGHT();
3452 break;
3453
3454 case T_on_board:
3455 variant.m_ExcludedFromBoard = !parseBool();
3456 NeedRIGHT();
3457 break;
3458
3459 case T_in_pos_files:
3460 variant.m_ExcludedFromPosFiles = !parseBool();
3461 NeedRIGHT();
3462 break;
3463
3464 case T_field:
3465 {
3466 wxString fieldName;
3467 wxString fieldValue;
3468
3469 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3470 {
3471 if( token != T_LEFT )
3472 Expecting( T_LEFT );
3473
3474 token = NextTok();
3475
3476 switch( token )
3477 {
3478 case T_name:
3479 NeedSYMBOL();
3480 fieldName = FromUTF8();
3481 NeedRIGHT();
3482 break;
3483
3484 case T_value:
3485 NeedSYMBOL();
3486 fieldValue = FromUTF8();
3487 NeedRIGHT();
3488 break;
3489
3490 default:
3491 Expecting( "name or value" );
3492 }
3493 }
3494
3495 variant.m_Fields[fieldName] = fieldValue;
3496 break;
3497 }
3498
3499 default:
3500 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
3501 }
3502
3503 instance.m_Variants[variant.m_Name] = variant;
3504 }
3505
3506 break;
3507 }
3508
3509 default:
3510 Expecting( "reference, unit, value, footprint, or variant" );
3511 }
3512 }
3513
3514 symbol->AddHierarchicalReference( instance );
3515 }
3516 }
3517
3518 break;
3519 }
3520
3521 case T_property:
3522 // The field parent symbol must be set and its orientation must be set before
3523 // the field positions are set.
3524 field = parseSchField( symbol.get() );
3525
3526 // Exclude from simulation used to be managed by a Sim.Enable field set to "0" when
3527 // simulation was disabled.
3529 {
3530 symbol->SetExcludedFromSim( field->GetText() == wxS( "0" ) );
3531 delete field;
3532 break;
3533 }
3534
3535 // Even longer ago, we had a "Spice_Netlist_Enabled" field
3537 {
3538 symbol->SetExcludedFromSim( field->GetText() == wxS( "N" ) );
3539 delete field;
3540 break;
3541 }
3542
3543 SCH_FIELD* existing;
3544
3545 if( field->IsMandatory() )
3546 existing = symbol->GetField( field->GetId() );
3547 else
3548 existing = symbol->GetField( field->GetName() );
3549
3550 if( existing )
3551 *existing = *field;
3552 else
3553 symbol->AddField( *field );
3554
3555 if( field->GetId() == FIELD_T::REFERENCE )
3556 symbol->UpdatePrefix();
3557
3558 delete field;
3559 break;
3560
3561 case T_pin:
3562 {
3563 // Read an alternate pin designation
3564 wxString number;
3565 KIID uuid;
3566 wxString alt;
3567
3568 NeedSYMBOL();
3569 number = FromUTF8();
3570
3571 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3572 {
3573 if( token != T_LEFT )
3574 Expecting( T_LEFT );
3575
3576 token = NextTok();
3577
3578 switch( token )
3579 {
3580 case T_alternate:
3581 NeedSYMBOL();
3582 alt = FromUTF8();
3583 NeedRIGHT();
3584 break;
3585
3586 case T_uuid:
3587 NeedSYMBOL();
3588
3589 // First version to write out pin uuids accidentally wrote out the symbol's
3590 // uuid for each pin, so ignore uuids coming from that version.
3591 if( m_requiredVersion >= 20210126 )
3592 uuid = parseKIID();
3593
3594 NeedRIGHT();
3595 break;
3596
3597 default:
3598 Expecting( "alternate or uuid" );
3599 }
3600 }
3601
3602 symbol->GetRawPins().emplace_back( std::make_unique<SCH_PIN>( symbol.get(), number,
3603 alt, uuid ) );
3604 break;
3605 }
3606
3607 default:
3608 Expecting( "lib_id, lib_name, at, mirror, uuid, exclude_from_sim, on_board, in_bom, dnp, "
3609 "default_instance, property, pin, or instances" );
3610 }
3611 }
3612
3613 if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
3614 symbol->SetSchSymbolLibraryName( libName );
3615
3616 // Ensure edit/status flags are cleared after these initializations:
3617 symbol->ClearFlags();
3618
3619 return symbol.release();
3620}
3621
3622
3624{
3625 wxCHECK_MSG( CurTok() == T_image, nullptr,
3626 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
3627
3628 T token;
3629 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
3630 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3631
3632 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3633 {
3634 if( token != T_LEFT )
3635 Expecting( T_LEFT );
3636
3637 token = NextTok();
3638
3639 switch( token )
3640 {
3641 case T_at:
3642 bitmap->SetPosition( parseXY() );
3643 NeedRIGHT();
3644 break;
3645
3646 case T_scale:
3647 {
3648 const double scale = parseDouble( "image scale factor" );
3649 refImage.SetImageScale( std::isnormal( scale ) ? scale : 1.0 );
3650
3651 NeedRIGHT();
3652 break;
3653 }
3654 case T_uuid:
3655 NeedSYMBOL();
3656 const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
3657 NeedRIGHT();
3658 break;
3659
3660 case T_data:
3661 {
3662 token = NextTok();
3663
3664 wxString data;
3665
3666 // Reserve 128K because most image files are going to be larger than the default
3667 // 1K that wxString reserves.
3668 data.reserve( 1 << 17 );
3669
3670 while( token != T_RIGHT )
3671 {
3672 if( !IsSymbol( token ) )
3673 Expecting( "base64 image data" );
3674
3675 data += FromUTF8();
3676 token = NextTok();
3677 }
3678
3679 wxMemoryBuffer buffer = wxBase64Decode( data );
3680
3681 if( !refImage.ReadImageFile( buffer ) )
3682 THROW_IO_ERROR( _( "Failed to read image data." ) );
3683
3684 break;
3685 }
3686
3687 case T_locked:
3688 bitmap->SetLocked( parseBool() );
3689 NeedRIGHT();
3690 break;
3691
3692 default:
3693 Expecting( "at, scale, uuid, data or locked" );
3694 }
3695 }
3696
3697 // The image will be scaled by PPI in ReadImageFile.
3698
3699 // 20230121 or older file format versions assumed 300 image PPI at load/save.
3700 // Let's keep compatibility by changing image scale.
3701 if( m_requiredVersion <= 20230121 )
3702 {
3703 refImage.SetImageScale( refImage.GetImageScale() * refImage.GetImage().GetPPI() / 300.0 );
3704 }
3705
3706 return bitmap.release();
3707}
3708
3709
3711{
3712 wxCHECK_MSG( CurTok() == T_sheet, nullptr,
3713 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a sheet." ) );
3714
3715 T token;
3717 FILL_PARAMS fill;
3718 SCH_FIELD* field;
3719 std::vector<SCH_FIELD> fields;
3720 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>();
3721 std::set<int> fieldIDsRead;
3722
3723 // We'll reset this if we find a fields_autoplaced token
3724 sheet->SetFieldsAutoplaced( AUTOPLACE_NONE );
3725
3726 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3727 {
3728 if( token != T_LEFT )
3729 Expecting( T_LEFT );
3730
3731 token = NextTok();
3732
3733 switch( token )
3734 {
3735 case T_at:
3736 sheet->SetPosition( parseXY() );
3737 NeedRIGHT();
3738 break;
3739
3740 case T_size:
3741 {
3742 VECTOR2I size;
3743 size.x = parseInternalUnits( "sheet width" );
3744 size.y = parseInternalUnits( "sheet height" );
3745 sheet->SetSize( size );
3746 NeedRIGHT();
3747 break;
3748 }
3749
3750 case T_exclude_from_sim:
3751 sheet->SetExcludedFromSim( parseBool() );
3752 NeedRIGHT();
3753 break;
3754
3755 case T_in_bom:
3756 sheet->SetExcludedFromBOM( !parseBool() );
3757 NeedRIGHT();
3758 break;
3759
3760 case T_on_board:
3761 sheet->SetExcludedFromBoard( !parseBool() );
3762 NeedRIGHT();
3763 break;
3764
3765 case T_dnp:
3766 sheet->SetDNP( parseBool() );
3767 NeedRIGHT();
3768 break;
3769
3770 case T_locked:
3771 sheet->SetLocked( parseBool() );
3772 NeedRIGHT();
3773 break;
3774
3775 case T_fields_autoplaced:
3776 if( parseMaybeAbsentBool( true ) )
3777 sheet->SetFieldsAutoplaced( AUTOPLACE_AUTO );
3778
3779 break;
3780
3781 case T_stroke:
3782 parseStroke( stroke );
3783 sheet->SetBorderWidth( stroke.GetWidth() );
3784 sheet->SetBorderColor( stroke.GetColor() );
3785 break;
3786
3787 case T_fill:
3788 parseFill( fill );
3789 sheet->SetBackgroundColor( fill.m_Color );
3790 break;
3791
3792 case T_uuid:
3793 NeedSYMBOL();
3794 const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
3795 NeedRIGHT();
3796 break;
3797
3798 case T_property:
3799 field = parseSchField( sheet.get() );
3800
3801 if( m_requiredVersion <= 20200310 )
3802 {
3803 // Earlier versions had the wrong ids (and names) saved for sheet fields.
3804 // Fortunately they only saved the sheetname and sheetfilepath (and always
3805 // in that order), so we can hack in a recovery.
3806 if( fields.empty() )
3807 field->setId( FIELD_T::SHEET_NAME );
3808 else
3810 }
3811
3812 fields.emplace_back( *field );
3813
3814 delete field;
3815 break;
3816
3817 case T_pin:
3818 sheet->AddPin( parseSchSheetPin( sheet.get() ) );
3819 break;
3820
3821 case T_instances:
3822 {
3823 std::vector<SCH_SHEET_INSTANCE> instances;
3824
3825 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3826 {
3827 if( token != T_LEFT )
3828 Expecting( T_LEFT );
3829
3830 token = NextTok();
3831
3832 if( token != T_project )
3833 Expecting( "project" );
3834
3835 NeedSYMBOL();
3836
3837 wxString projectName = FromUTF8();
3838
3839 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3840 {
3841 if( token != T_LEFT )
3842 Expecting( T_LEFT );
3843
3844 token = NextTok();
3845
3846 if( token != T_path )
3847 Expecting( "path" );
3848
3849 SCH_SHEET_INSTANCE instance;
3850
3851 instance.m_ProjectName = projectName;
3852
3853 NeedSYMBOL();
3854 instance.m_Path = KIID_PATH( FromUTF8() );
3855
3856 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3857 {
3858 if( token != T_LEFT )
3859 Expecting( T_LEFT );
3860
3861 token = NextTok();
3862
3863 switch( token )
3864 {
3865 case T_page:
3866 {
3867 NeedSYMBOL();
3868 instance.m_PageNumber = FromUTF8();
3869
3870 // Empty page numbers are not permitted
3871 if( instance.m_PageNumber.IsEmpty() )
3872 {
3873 // Use hash character instead
3874 instance.m_PageNumber = wxT( "#" );
3875 }
3876 else
3877 {
3878 // Whitespaces are not permitted
3879 static std::vector<wxString> whitespaces =
3880 { wxT( "\r" ),
3881 wxT( "\n" ),
3882 wxT( "\t" ),
3883 wxT( " " ) };
3884
3885 for( wxString ch : whitespaces )
3886 instance.m_PageNumber.Replace( ch, wxEmptyString );
3887 }
3888
3889 NeedRIGHT();
3890 break;
3891 }
3892
3893 case T_variant:
3894 {
3895 SCH_SHEET_VARIANT variant;
3896 variant.InitializeAttributes( *sheet );
3897
3898 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3899 {
3900 if( token != T_LEFT )
3901 Expecting( T_LEFT );
3902
3903 token = NextTok();
3904
3905 switch( token )
3906 {
3907 case T_name:
3908 NeedSYMBOL();
3909 variant.m_Name = FromUTF8();
3910 NeedRIGHT();
3911 break;
3912
3913 case T_dnp:
3914 variant.m_DNP = parseBool();
3915 NeedRIGHT();
3916 break;
3917
3918 case T_exclude_from_sim:
3919 variant.m_ExcludedFromSim = parseBool();
3920 NeedRIGHT();
3921 break;
3922
3923 case T_in_bom:
3924 variant.m_ExcludedFromBOM = parseBool();
3925
3926 // This fixes the incorrect logic from prior file versions. The "in_bom" token
3927 // used in the file format is the positive logic. However, in the UI the term
3928 // excluded from BOM is used which is the inverted logic.
3929 if( m_requiredVersion >= 20260306 )
3930 variant.m_ExcludedFromBOM = !variant.m_ExcludedFromBOM;
3931
3932 NeedRIGHT();
3933 break;
3934
3935 case T_on_board:
3936 variant.m_ExcludedFromBoard = !parseBool();
3937 NeedRIGHT();
3938 break;
3939
3940 case T_in_pos_files:
3941 variant.m_ExcludedFromPosFiles = !parseBool();
3942 NeedRIGHT();
3943 break;
3944
3945 case T_field:
3946 {
3947 wxString fieldName;
3948 wxString fieldValue;
3949
3950 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3951 {
3952 if( token != T_LEFT )
3953 Expecting( T_LEFT );
3954
3955 token = NextTok();
3956
3957 switch( token )
3958 {
3959 case T_name:
3960 NeedSYMBOL();
3961 fieldName = FromUTF8();
3962 NeedRIGHT();
3963 break;
3964
3965 case T_value:
3966 NeedSYMBOL();
3967 fieldValue = FromUTF8();
3968 NeedRIGHT();
3969 break;
3970
3971 default:
3972 Expecting( "name or value" );
3973 }
3974 }
3975
3976 variant.m_Fields[fieldName] = fieldValue;
3977 break;
3978 }
3979
3980 default:
3981 Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
3982 }
3983
3984 instance.m_Variants[variant.m_Name] = variant;
3985 }
3986
3987 break;
3988 }
3989
3990 default:
3991 Expecting( "page or variant" );
3992 }
3993 }
3994
3995 instances.emplace_back( instance );
3996 }
3997 }
3998
3999 sheet->setInstances( instances );
4000 break;
4001 }
4002
4003 default:
4004 Expecting( "at, size, stroke, background, instances, uuid, property, or pin" );
4005 }
4006 }
4007
4008 sheet->SetFields( fields );
4009
4010 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_NAME ) )
4011 {
4012 THROW_PARSE_ERROR( _( "Missing sheet name property" ), CurSource(), CurLine(),
4013 CurLineNumber(), CurOffset() );
4014 }
4015
4016 if( !FindField( sheet->GetFields(), FIELD_T::SHEET_FILENAME ) )
4017 {
4018 THROW_PARSE_ERROR( _( "Missing sheet file property" ), CurSource(), CurLine(),
4019 CurLineNumber(), CurOffset() );
4020 }
4021
4022 return sheet.release();
4023}
4024
4025
4027{
4028 wxCHECK_MSG( CurTok() == T_junction, nullptr,
4029 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a junction." ) );
4030
4031 T token;
4032 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
4033
4034 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4035 {
4036 if( token != T_LEFT )
4037 Expecting( T_LEFT );
4038
4039 token = NextTok();
4040
4041 switch( token )
4042 {
4043 case T_at:
4044 junction->SetPosition( parseXY() );
4045 NeedRIGHT();
4046 break;
4047
4048 case T_diameter:
4049 junction->SetDiameter( parseInternalUnits( "junction diameter" ) );
4050 NeedRIGHT();
4051 break;
4052
4053 case T_color:
4054 {
4055 COLOR4D color;
4056
4057 color.r = parseInt( "red" ) / 255.0;
4058 color.g = parseInt( "green" ) / 255.0;
4059 color.b = parseInt( "blue" ) / 255.0;
4060 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
4061
4062 junction->SetColor( color );
4063 NeedRIGHT();
4064 break;
4065 }
4066
4067 case T_uuid:
4068 NeedSYMBOL();
4069 const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
4070 NeedRIGHT();
4071 break;
4072
4073 case T_locked:
4074 junction->SetLocked( parseBool() );
4075 NeedRIGHT();
4076 break;
4077
4078 default:
4079 Expecting( "at, diameter, color, uuid or locked" );
4080 }
4081 }
4082
4083 return junction.release();
4084}
4085
4086
4088{
4089 wxCHECK_MSG( CurTok() == T_no_connect, nullptr,
4090 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a no connect." ) );
4091
4092 T token;
4093 std::unique_ptr<SCH_NO_CONNECT> no_connect = std::make_unique<SCH_NO_CONNECT>();
4094
4095 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4096 {
4097 if( token != T_LEFT )
4098 Expecting( T_LEFT );
4099
4100 token = NextTok();
4101
4102 switch( token )
4103 {
4104 case T_at:
4105 no_connect->SetPosition( parseXY() );
4106 NeedRIGHT();
4107 break;
4108
4109 case T_uuid:
4110 NeedSYMBOL();
4111 const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
4112 NeedRIGHT();
4113 break;
4114
4115 case T_locked:
4116 no_connect->SetLocked( parseBool() );
4117 NeedRIGHT();
4118 break;
4119
4120 default:
4121 Expecting( "at, uuid or locked" );
4122 }
4123 }
4124
4125 return no_connect.release();
4126}
4127
4128
4130{
4131 wxCHECK_MSG( CurTok() == T_bus_entry, nullptr,
4132 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus entry." ) );
4133
4134 T token;
4136 std::unique_ptr<SCH_BUS_WIRE_ENTRY> busEntry = std::make_unique<SCH_BUS_WIRE_ENTRY>();
4137
4138 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4139 {
4140 if( token != T_LEFT )
4141 Expecting( T_LEFT );
4142
4143 token = NextTok();
4144
4145 switch( token )
4146 {
4147 case T_at:
4148 busEntry->SetPosition( parseXY() );
4149 NeedRIGHT();
4150 break;
4151
4152 case T_size:
4153 {
4154 VECTOR2I size;
4155
4156 size.x = parseInternalUnits( "bus entry height" );
4157 size.y = parseInternalUnits( "bus entry width" );
4158 busEntry->SetSize( size );
4159 NeedRIGHT();
4160 break;
4161 }
4162
4163 case T_stroke:
4164 parseStroke( stroke );
4165 busEntry->SetStroke( stroke );
4166 break;
4167
4168 case T_uuid:
4169 NeedSYMBOL();
4170 const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
4171 NeedRIGHT();
4172 break;
4173
4174 case T_locked:
4175 busEntry->SetLocked( parseBool() );
4176 NeedRIGHT();
4177 break;
4178
4179 default:
4180 Expecting( "at, size, uuid, stroke or locked" );
4181 }
4182 }
4183
4184 return busEntry.release();
4185}
4186
4187
4189{
4190 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
4191 {
4192 aShape->SetFillColor( aShape->GetStroke().GetColor() );
4194 }
4195}
4196
4197
4199{
4200 T token;
4202 FILL_PARAMS fill;
4203
4204 std::unique_ptr<SCH_SHAPE> polyline = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_NOTES );
4205
4206 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4207 {
4208 if( token != T_LEFT )
4209 Expecting( T_LEFT );
4210
4211 token = NextTok();
4212
4213 switch( token )
4214 {
4215 case T_pts:
4216 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4217 {
4218 if( token != T_LEFT )
4219 Expecting( T_LEFT );
4220
4221 token = NextTok();
4222
4223 if( token != T_xy )
4224 Expecting( "xy" );
4225
4226 polyline->AddPoint( parseXY() );
4227
4228 NeedRIGHT();
4229 }
4230 break;
4231
4232 case T_stroke:
4233 parseStroke( stroke );
4234
4235 // In 6.0, the default schematic line style was Dashed.
4236 if( m_requiredVersion <= 20211123 && stroke.GetLineStyle() == LINE_STYLE::DEFAULT )
4238
4239 polyline->SetStroke( stroke );
4240 break;
4241
4242 case T_fill:
4243 parseFill( fill );
4244 polyline->SetFillMode( fill.m_FillType );
4245 polyline->SetFillColor( fill.m_Color );
4246 fixupSchFillMode( polyline.get() );
4247 break;
4248
4249 case T_uuid:
4250 NeedSYMBOL();
4251 const_cast<KIID&>( polyline->m_Uuid ) = parseKIID();
4252 NeedRIGHT();
4253 break;
4254
4255 case T_locked:
4256 polyline->SetLocked( parseBool() );
4257 NeedRIGHT();
4258 break;
4259
4260 default:
4261 Expecting( "pts, uuid, stroke, fill or locked" );
4262 }
4263 }
4264
4265 return polyline.release();
4266}
4267
4268
4270{
4271 // Note: T_polyline is deprecated in this code: it is now handled by
4272 // parseSchPolyLine() that can handle true polygons, and not only one segment.
4273
4274 T token;
4276 int layer;
4277
4278 switch( CurTok() )
4279 {
4280 case T_polyline: layer = LAYER_NOTES; break;
4281 case T_wire: layer = LAYER_WIRE; break;
4282 case T_bus: layer = LAYER_BUS; break;
4283 default:
4284 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as a line." );
4285 }
4286
4287 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( VECTOR2I(), layer );
4288
4289 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4290 {
4291 if( token != T_LEFT )
4292 Expecting( T_LEFT );
4293
4294 token = NextTok();
4295
4296 switch( token )
4297 {
4298 case T_pts:
4299 NeedLEFT();
4300 token = NextTok();
4301
4302 if( token != T_xy )
4303 Expecting( "xy" );
4304
4305 line->SetStartPoint( parseXY() );
4306 NeedRIGHT();
4307 NeedLEFT();
4308 token = NextTok();
4309
4310 if( token != T_xy )
4311 Expecting( "xy" );
4312
4313 line->SetEndPoint( parseXY() );
4314 NeedRIGHT();
4315 NeedRIGHT();
4316 break;
4317
4318 case T_stroke:
4319 parseStroke( stroke );
4320 line->SetStroke( stroke );
4321 break;
4322
4323 case T_uuid:
4324 NeedSYMBOL();
4325 const_cast<KIID&>( line->m_Uuid ) = parseKIID();
4326 NeedRIGHT();
4327 break;
4328
4329 case T_locked:
4330 line->SetLocked( parseBool() );
4331 NeedRIGHT();
4332 break;
4333
4334 default:
4335 Expecting( "pts, uuid, stroke or locked" );
4336 }
4337 }
4338
4339 return line.release();
4340}
4341
4342
4344{
4345 wxCHECK_MSG( CurTok() == T_arc, nullptr,
4346 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an arc." ) );
4347
4348 T token;
4349 VECTOR2I startPoint;
4350 VECTOR2I midPoint;
4351 VECTOR2I endPoint;
4353 FILL_PARAMS fill;
4354 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
4355
4356 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4357 {
4358 if( token != T_LEFT )
4359 Expecting( T_LEFT );
4360
4361 token = NextTok();
4362
4363 switch( token )
4364 {
4365 case T_start:
4366 startPoint = parseXY();
4367 NeedRIGHT();
4368 break;
4369
4370 case T_mid:
4371 midPoint = parseXY();
4372 NeedRIGHT();
4373 break;
4374
4375 case T_end:
4376 endPoint = parseXY();
4377 NeedRIGHT();
4378 break;
4379
4380 case T_stroke:
4381 parseStroke( stroke );
4382 arc->SetStroke( stroke );
4383 break;
4384
4385 case T_fill:
4386 parseFill( fill );
4387 arc->SetFillMode( fill.m_FillType );
4388 arc->SetFillColor( fill.m_Color );
4389 fixupSchFillMode( arc.get() );
4390 break;
4391
4392 case T_uuid:
4393 NeedSYMBOL();
4394 const_cast<KIID&>( arc->m_Uuid ) = KIID( FromUTF8() );
4395 NeedRIGHT();
4396 break;
4397
4398 case T_locked:
4399 arc->SetLocked( parseBool() );
4400 NeedRIGHT();
4401 break;
4402
4403 default:
4404 Expecting( "start, mid, end, stroke, fill, uuid or locked" );
4405 }
4406 }
4407
4408 arc->SetArcGeometry( startPoint, midPoint, endPoint );
4409
4410 return arc.release();
4411}
4412
4413
4415{
4416 wxCHECK_MSG( CurTok() == T_circle, nullptr,
4417 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a circle." ) );
4418
4419 T token;
4421 int radius = 0;
4423 FILL_PARAMS fill;
4424 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
4425
4426 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4427 {
4428 if( token != T_LEFT )
4429 Expecting( T_LEFT );
4430
4431 token = NextTok();
4432
4433 switch( token )
4434 {
4435 case T_center:
4436 center = parseXY();
4437 NeedRIGHT();
4438 break;
4439
4440 case T_radius:
4441 radius = parseInternalUnits( "radius length" );
4442 NeedRIGHT();
4443 break;
4444
4445 case T_stroke:
4446 parseStroke( stroke );
4447 circle->SetStroke( stroke );
4448 break;
4449
4450 case T_fill:
4451 parseFill( fill );
4452 circle->SetFillMode( fill.m_FillType );
4453 circle->SetFillColor( fill.m_Color );
4454 fixupSchFillMode( circle.get() );
4455 break;
4456
4457 case T_uuid:
4458 NeedSYMBOL();
4459 const_cast<KIID&>( circle->m_Uuid ) = KIID( FromUTF8() );
4460 NeedRIGHT();
4461 break;
4462
4463 case T_locked:
4464 circle->SetLocked( parseBool() );
4465 NeedRIGHT();
4466 break;
4467
4468 default:
4469 Expecting( "center, radius, stroke, fill, uuid or locked" );
4470 }
4471 }
4472
4473 circle->SetCenter( center );
4474 circle->SetEnd( VECTOR2I( center.x + radius, center.y ) );
4475
4476 return circle.release();
4477}
4478
4479
4481{
4482 wxCHECK_MSG( CurTok() == T_rectangle, nullptr,
4483 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rectangle." ) );
4484
4485 T token;
4487 FILL_PARAMS fill;
4488 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
4489
4490 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4491 {
4492 if( token != T_LEFT )
4493 Expecting( T_LEFT );
4494
4495 token = NextTok();
4496
4497 switch( token )
4498 {
4499 case T_start:
4500 rectangle->SetPosition( parseXY() );
4501 NeedRIGHT();
4502 break;
4503
4504 case T_end:
4505 rectangle->SetEnd( parseXY() );
4506 NeedRIGHT();
4507 break;
4508
4509 case T_radius:
4510 rectangle->SetCornerRadius( parseDouble( "corner radius" ) * schIUScale.IU_PER_MM );
4511 NeedRIGHT();
4512 break;
4513
4514 case T_stroke:
4515 parseStroke( stroke );
4516 rectangle->SetStroke( stroke );
4517 break;
4518
4519 case T_fill:
4520 parseFill( fill );
4521 rectangle->SetFillMode( fill.m_FillType );
4522 rectangle->SetFillColor( fill.m_Color );
4523 fixupSchFillMode( rectangle.get() );
4524 break;
4525
4526 case T_uuid:
4527 NeedSYMBOL();
4528 const_cast<KIID&>( rectangle->m_Uuid ) = KIID( FromUTF8() );
4529 NeedRIGHT();
4530 break;
4531
4532 case T_locked:
4533 rectangle->SetLocked( parseBool() );
4534 NeedRIGHT();
4535 break;
4536
4537 default:
4538 Expecting( "start, end, stroke, fill, uuid or locked" );
4539 }
4540 }
4541
4542 return rectangle.release();
4543}
4544
4545
4547{
4548 wxCHECK_MSG( CurTok() == T_rule_area, nullptr,
4549 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a rule area." ) );
4550
4551 T token;
4553 FILL_PARAMS fill;
4554 std::unique_ptr<SCH_RULE_AREA> ruleArea = std::make_unique<SCH_RULE_AREA>();
4555
4556 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4557 {
4558 if( token != T_LEFT )
4559 Expecting( T_LEFT );
4560
4561 token = NextTok();
4562
4563 switch( token )
4564 {
4565 case T_polyline:
4566 {
4567 std::unique_ptr<SCH_SHAPE> poly( parseSchPolyLine() );
4568 SHAPE_POLY_SET& sch_rule_poly = poly->GetPolyShape();
4569
4570 // The polygon must be closed, it is a schematic closed polyline:
4571 sch_rule_poly.Outline(0).SetClosed( true );
4572
4573 ruleArea->SetPolyShape( sch_rule_poly );
4574
4575 ruleArea->SetStroke( poly->GetStroke() );
4576 ruleArea->SetFillMode( poly->GetFillMode() );
4577 ruleArea->SetFillColor( poly->GetFillColor() );
4578
4579 // the uuid is saved to the shape but stored and saved out of the rulearea
4580 const_cast<KIID&>( ruleArea->m_Uuid ) = poly->m_Uuid;
4581 break;
4582 }
4583
4584 case T_exclude_from_sim:
4585 ruleArea->SetExcludedFromSim( parseBool() );
4586 NeedRIGHT();
4587 break;
4588
4589 case T_in_bom:
4590 ruleArea->SetExcludedFromBOM( !parseBool() );
4591 NeedRIGHT();
4592 break;
4593
4594 case T_on_board:
4595 ruleArea->SetExcludedFromBoard( !parseBool() );
4596 NeedRIGHT();
4597 break;
4598
4599 case T_dnp:
4600 ruleArea->SetDNP( parseBool() );
4601 NeedRIGHT();
4602 break;
4603
4604 case T_locked:
4605 ruleArea->SetLocked( parseBool() );
4606 NeedRIGHT();
4607 break;
4608
4609 default:
4610 Expecting( "exclude_from_sim, on_board, in_bom, dnp, locked, or polyline" );
4611 }
4612 }
4613
4614 return ruleArea.release();
4615}
4616
4617
4619{
4620 wxCHECK_MSG( CurTok() == T_bezier, nullptr,
4621 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bezier." ) );
4622
4623 T token;
4625 FILL_PARAMS fill;
4626 std::unique_ptr<SCH_SHAPE> bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER );
4627
4628 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4629 {
4630 if( token != T_LEFT )
4631 Expecting( T_LEFT );
4632
4633 token = NextTok();
4634
4635 switch( token )
4636 {
4637 case T_pts:
4638 {
4639 int ii = 0;
4640
4641 for( token = NextTok(); token != T_RIGHT; token = NextTok(), ++ii )
4642 {
4643 if( token != T_LEFT )
4644 Expecting( T_LEFT );
4645
4646 token = NextTok();
4647
4648 if( token != T_xy )
4649 Expecting( "xy" );
4650
4651 switch( ii )
4652 {
4653 case 0: bezier->SetStart( parseXY() ); break;
4654 case 1: bezier->SetBezierC1( parseXY() ); break;
4655 case 2: bezier->SetBezierC2( parseXY() ); break;
4656 case 3: bezier->SetEnd( parseXY() ); break;
4657 default: Unexpected( "control point" ); break;
4658 }
4659
4660 NeedRIGHT();
4661 }
4662 }
4663 break;
4664
4665 case T_stroke:
4666 parseStroke( stroke );
4667 bezier->SetStroke( stroke );
4668 break;
4669
4670 case T_fill:
4671 parseFill( fill );
4672 bezier->SetFillMode( fill.m_FillType );
4673 bezier->SetFillColor( fill.m_Color );
4674 fixupSchFillMode( bezier.get() );
4675 break;
4676
4677 case T_uuid:
4678 NeedSYMBOL();
4679 const_cast<KIID&>( bezier->m_Uuid ) = KIID( FromUTF8() );
4680 NeedRIGHT();
4681 break;
4682
4683 case T_locked:
4684 bezier->SetLocked( parseBool() );
4685 NeedRIGHT();
4686 break;
4687
4688 default:
4689 Expecting( "pts, stroke, fill, uuid or locked" );
4690 }
4691 }
4692
4693 bezier->RebuildBezierToSegmentsPointsList( m_maxError );
4694
4695 return bezier.release();
4696}
4697
4698
4700{
4701 T token;
4702 std::unique_ptr<SCH_TEXT> text;
4703
4704 switch( CurTok() )
4705 {
4706 case T_text: text = std::make_unique<SCH_TEXT>(); break;
4707 case T_label: text = std::make_unique<SCH_LABEL>(); break;
4708 case T_global_label: text = std::make_unique<SCH_GLOBALLABEL>(); break;
4709 case T_hierarchical_label: text = std::make_unique<SCH_HIERLABEL>(); break;
4710 case T_netclass_flag: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4711 case T_directive_label: text = std::make_unique<SCH_DIRECTIVE_LABEL>(); break;
4712 default:
4713 wxCHECK_MSG( false, nullptr, "Cannot parse " + GetTokenString( CurTok() ) + " as text." );
4714 }
4715
4716 // We'll reset this if we find a fields_autoplaced token
4717 text->SetFieldsAutoplaced( AUTOPLACE_NONE );
4718
4719 NeedSYMBOL();
4720
4721 text->SetText( FromUTF8() );
4722
4723 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4724 {
4725 if( token != T_LEFT )
4726 Expecting( T_LEFT );
4727
4728 token = NextTok();
4729
4730 switch( token )
4731 {
4732 case T_exclude_from_sim:
4733 text->SetExcludedFromSim( parseBool() );
4734 NeedRIGHT();
4735 break;
4736
4737 case T_at:
4738 text->SetPosition( parseXY() );
4739 text->SetTextAngle( EDA_ANGLE( parseDouble( "text angle" ), DEGREES_T ).KeepUpright() );
4740
4741 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() ) )
4742 {
4743 switch( static_cast<int>( label->GetTextAngle().AsDegrees() ) )
4744 {
4745 case 0: label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4746 case 90: label->SetSpinStyle( SPIN_STYLE::UP ); break;
4747 case 180: label->SetSpinStyle( SPIN_STYLE::LEFT ); break;
4748 case 270: label->SetSpinStyle( SPIN_STYLE::BOTTOM ); break;
4749 default: wxFAIL; label->SetSpinStyle( SPIN_STYLE::RIGHT ); break;
4750 }
4751 }
4752
4753 NeedRIGHT();
4754 break;
4755
4756 case T_shape:
4757 {
4758 if( text->Type() == SCH_TEXT_T || text->Type() == SCH_LABEL_T )
4759 Unexpected( T_shape );
4760
4761 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( text.get() );
4762
4763 token = NextTok();
4764
4765 switch( token )
4766 {
4767 case T_input: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
4768 case T_output: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
4769 case T_bidirectional: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
4770 case T_tri_state: label->SetShape( LABEL_FLAG_SHAPE::L_TRISTATE ); break;
4771 case T_passive: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
4772 case T_dot: label->SetShape( LABEL_FLAG_SHAPE::F_DOT ); break;
4773 case T_round: label->SetShape( LABEL_FLAG_SHAPE::F_ROUND ); break;
4774 case T_diamond: label->SetShape( LABEL_FLAG_SHAPE::F_DIAMOND ); break;
4775 case T_rectangle: label->SetShape( LABEL_FLAG_SHAPE::F_RECTANGLE ); break;
4776 default:
4777 Expecting( "input, output, bidirectional, tri_state, passive, dot, round, diamond"
4778 "or rectangle" );
4779 }
4780
4781 NeedRIGHT();
4782 break;
4783 }
4784
4785 case T_length:
4786 {
4787 if( text->Type() != SCH_DIRECTIVE_LABEL_T )
4788 Unexpected( T_length );
4789
4790 SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( text.get() );
4791
4792 label->SetPinLength( parseInternalUnits( "pin length" ) );
4793 NeedRIGHT();
4794 }
4795 break;
4796
4797 case T_fields_autoplaced:
4798 if( parseMaybeAbsentBool( true ) )
4799 text->SetFieldsAutoplaced( AUTOPLACE_AUTO );
4800
4801 break;
4802
4803 case T_effects:
4804 parseEDA_TEXT( static_cast<EDA_TEXT*>( text.get() ), true );
4805
4806 // Hidden schematic text is no longer supported
4807 text->SetVisible( true );
4808 break;
4809
4810 case T_iref: // legacy format; current is a T_property (aka SCH_FIELD)
4811 if( text->Type() == SCH_GLOBAL_LABEL_T )
4812 {
4813 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4814 SCH_FIELD* field = label->GetField( FIELD_T::INTERSHEET_REFS );
4815
4816 field->SetTextPos( parseXY() );
4817 NeedRIGHT();
4818
4819 field->SetVisible( true );
4820 }
4821 break;
4822
4823 case T_uuid:
4824 NeedSYMBOL();
4825 const_cast<KIID&>( text->m_Uuid ) = parseKIID();
4826 NeedRIGHT();
4827 break;
4828
4829 case T_property:
4830 {
4831 if( text->Type() == SCH_TEXT_T )
4832 Unexpected( T_property );
4833
4834 SCH_FIELD* field = parseSchField( text.get() );
4835
4836 // Intersheetrefs is a mandatory field and so will already exist
4837 if( text->Type() == SCH_GLOBAL_LABEL_T && field->IsMandatory() )
4838 {
4839 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( text.get() );
4840 *label->GetField( field->GetId() ) = *field;
4841 }
4842 else
4843 {
4844 static_cast<SCH_LABEL_BASE*>( text.get() )->GetFields().emplace_back( *field );
4845 }
4846
4847 delete field;
4848 break;
4849 }
4850
4851 case T_locked:
4852 text->SetLocked( parseBool() );
4853 NeedRIGHT();
4854 break;
4855
4856 default:
4857 Expecting( "at, shape, iref, uuid, effects or locked" );
4858 }
4859 }
4860
4861 SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( text.get() );
4862
4863 if( label && label->GetFields().empty() )
4865
4866 return text.release();
4867}
4868
4869
4871{
4872 wxCHECK_MSG( CurTok() == T_text_box, nullptr,
4873 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a text box." ) );
4874
4875 std::unique_ptr<SCH_TEXTBOX> textBox = std::make_unique<SCH_TEXTBOX>();
4876
4877 parseSchTextBoxContent( textBox.get() );
4878
4879 return textBox.release();
4880}
4881
4882
4884{
4885 wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
4886 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
4887
4888 std::unique_ptr<SCH_TABLECELL> cell = std::make_unique<SCH_TABLECELL>();
4889
4890 parseSchTextBoxContent( cell.get() );
4891
4892 return cell.release();
4893}
4894
4895
4897{
4898 T token;
4899 VECTOR2I pos;
4900 VECTOR2I end;
4901 VECTOR2I size;
4902 int left;
4903 int top;
4904 int right;
4905 int bottom;
4907 FILL_PARAMS fill;
4908 bool foundEnd = false;
4909 bool foundSize = false;
4910 bool foundMargins = false;
4911
4912 NeedSYMBOL();
4913
4914 aTextBox->SetText( FromUTF8() );
4915
4916 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4917 {
4918 if( token != T_LEFT )
4919 Expecting( T_LEFT );
4920
4921 token = NextTok();
4922
4923 switch( token )
4924 {
4925 case T_exclude_from_sim:
4926 aTextBox->SetExcludedFromSim( parseBool() );
4927 NeedRIGHT();
4928 break;
4929
4930 case T_start: // Legacy token during 6.99 development; fails to handle angle
4931 pos = parseXY();
4932 NeedRIGHT();
4933 break;
4934
4935 case T_end: // Legacy token during 6.99 development; fails to handle angle
4936 end = parseXY();
4937 foundEnd = true;
4938 NeedRIGHT();
4939 break;
4940
4941 case T_at:
4942 pos = parseXY();
4943 aTextBox->SetTextAngle( EDA_ANGLE( parseDouble( "textbox angle" ), DEGREES_T ) );
4944 NeedRIGHT();
4945 break;
4946
4947 case T_size:
4948 size = parseXY();
4949 foundSize = true;
4950 NeedRIGHT();
4951 break;
4952
4953 case T_span:
4954 if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
4955 {
4956 cell->SetColSpan( parseInt( "column span" ) );
4957 cell->SetRowSpan( parseInt( "row span" ) );
4958 }
4959 else
4960 {
4961 Expecting( "at, size, stroke, fill, effects or uuid" );
4962 }
4963
4964 NeedRIGHT();
4965 break;
4966
4967 case T_stroke:
4968 parseStroke( stroke );
4969 aTextBox->SetStroke( stroke );
4970 break;
4971
4972 case T_fill:
4973 parseFill( fill );
4974 aTextBox->SetFillMode( fill.m_FillType );
4975 aTextBox->SetFillColor( fill.m_Color );
4976 fixupSchFillMode( aTextBox );
4977 break;
4978
4979 case T_margins:
4980 parseMargins( left, top, right, bottom );
4981 aTextBox->SetMarginLeft( left );
4982 aTextBox->SetMarginTop( top );
4983 aTextBox->SetMarginRight( right );
4984 aTextBox->SetMarginBottom( bottom );
4985 foundMargins = true;
4986 NeedRIGHT();
4987 break;
4988
4989 case T_effects:
4990 parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ), false );
4991 break;
4992
4993 case T_uuid:
4994 NeedSYMBOL();
4995 const_cast<KIID&>( aTextBox->m_Uuid ) = KIID( FromUTF8() );
4996 NeedRIGHT();
4997 break;
4998
4999 case T_locked:
5000 aTextBox->SetLocked( parseBool() );
5001 NeedRIGHT();
5002 break;
5003
5004 default:
5005 if( dynamic_cast<SCH_TABLECELL*>( aTextBox ) != nullptr )
5006 Expecting( "at, size, stroke, fill, effects, span, uuid or locked" );
5007 else
5008 Expecting( "at, size, stroke, fill, effects, uuid or locked" );
5009 }
5010 }
5011
5012 aTextBox->SetPosition( pos );
5013
5014 if( foundEnd )
5015 aTextBox->SetEnd( end );
5016 else if( foundSize )
5017 aTextBox->SetEnd( pos + size );
5018 else
5019 Expecting( "size" );
5020
5021 if( !foundMargins )
5022 {
5023 int margin = aTextBox->GetLegacyTextMargin();
5024 aTextBox->SetMarginLeft( margin );
5025 aTextBox->SetMarginTop( margin );
5026 aTextBox->SetMarginRight( margin );
5027 aTextBox->SetMarginBottom( margin );
5028 }
5029}
5030
5031
5033{
5034 wxCHECK_MSG( CurTok() == T_table, nullptr,
5035 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
5036
5037 T token;
5038 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
5039 STROKE_PARAMS borderStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5040 STROKE_PARAMS separatorsStroke( defaultLineWidth, LINE_STYLE::DEFAULT );
5041 std::unique_ptr<SCH_TABLE> table = std::make_unique<SCH_TABLE>( defaultLineWidth );
5042
5043 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5044 {
5045 if( token != T_LEFT )
5046 Expecting( T_LEFT );
5047
5048 token = NextTok();
5049
5050 switch( token )
5051 {
5052 case T_column_count:
5053 table->SetColCount( parseInt( "column count" ) );
5054 NeedRIGHT();
5055 break;
5056
5057 case T_column_widths:
5058 {
5059 int col = 0;
5060
5061 while( ( token = NextTok() ) != T_RIGHT )
5062 table->SetColWidth( col++, parseInternalUnits() );
5063
5064 break;
5065 }
5066
5067 case T_row_heights:
5068 {
5069 int row = 0;
5070
5071 while( ( token = NextTok() ) != T_RIGHT )
5072 table->SetRowHeight( row++, parseInternalUnits() );
5073
5074 break;
5075 }
5076
5077 case T_cells:
5078 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5079 {
5080 if( token != T_LEFT )
5081 Expecting( T_LEFT );
5082
5083 token = NextTok();
5084
5085 if( token != T_table_cell )
5086 Expecting( "table_cell" );
5087
5088 table->AddCell( parseSchTableCell() );
5089 }
5090
5091 break;
5092
5093 case T_border:
5094 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5095 {
5096 if( token != T_LEFT )
5097 Expecting( T_LEFT );
5098
5099 token = NextTok();
5100
5101 switch( token )
5102 {
5103 case T_external:
5104 table->SetStrokeExternal( parseBool() );
5105 NeedRIGHT();
5106 break;
5107
5108 case T_header:
5109 table->SetStrokeHeaderSeparator( parseBool() );
5110 NeedRIGHT();
5111 break;
5112
5113 case T_stroke:
5114 parseStroke( borderStroke );
5115 table->SetBorderStroke( borderStroke );
5116 break;
5117
5118 default:
5119 Expecting( "external, header or stroke" );
5120 break;
5121 }
5122 }
5123
5124 break;
5125
5126 case T_separators:
5127 for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5128 {
5129 if( token != T_LEFT )
5130 Expecting( T_LEFT );
5131
5132 token = NextTok();
5133
5134 switch( token )
5135 {
5136 case T_rows:
5137 table->SetStrokeRows( parseBool() );
5138 NeedRIGHT();
5139 break;
5140
5141 case T_cols:
5142 table->SetStrokeColumns( parseBool() );
5143 NeedRIGHT();
5144 break;
5145
5146 case T_stroke:
5147 parseStroke( separatorsStroke );
5148 table->SetSeparatorsStroke( separatorsStroke );
5149 break;
5150
5151 default:
5152 Expecting( "rows, cols, or stroke" );
5153 break;
5154 }
5155 }
5156
5157 break;
5158
5159 case T_uuid:
5160 NeedSYMBOL();
5161 const_cast<KIID&>( table->m_Uuid ) = parseKIID();
5162 NeedRIGHT();
5163 break;
5164
5165 case T_locked:
5166 table->SetLocked( parseBool() );
5167 NeedRIGHT();
5168 break;
5169
5170 default:
5171 Expecting( "columns, col_widths, row_heights, border, separators, uuid, locked, header or cells" );
5172 }
5173 }
5174
5175 if( !table->GetCell( 0, 0 ) )
5176 {
5177 THROW_PARSE_ERROR( _( "Invalid table: no cells defined" ), CurSource(), CurLine(), CurLineNumber(),
5178 CurOffset() );
5179 }
5180
5181 return table.release();
5182}
5183
5184
5186{
5187 wxCHECK_RET( CurTok() == T_bus_alias,
5188 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a bus alias." ) );
5189 wxCHECK( aScreen, /* void */ );
5190
5191 T token;
5192 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
5193 wxString alias;
5194 wxString member;
5195
5196 NeedSYMBOL();
5197
5198 alias = FromUTF8();
5199
5200 if( m_requiredVersion < 20210621 )
5201 alias = ConvertToNewOverbarNotation( alias );
5202
5203 busAlias->SetName( alias );
5204
5205 NeedLEFT();
5206 token = NextTok();
5207
5208 if( token != T_members )
5209 Expecting( "members" );
5210
5211 token = NextTok();
5212
5213 while( token != T_RIGHT )
5214 {
5215 if( !IsSymbol( token ) )
5216 Expecting( "quoted string" );
5217
5218 member = FromUTF8();
5219
5220 if( m_requiredVersion < 20210621 )
5221 member = ConvertToNewOverbarNotation( member );
5222
5223 busAlias->AddMember( member );
5224
5225 token = NextTok();
5226 }
5227
5228 NeedRIGHT();
5229
5230 aScreen->AddBusAlias( busAlias );
5231}
5232
5233
5235{
5236 T token;
5237
5238 while( ( token = NextTok() ) != T_RIGHT )
5239 {
5240 // This token is the Uuid of the item in the group.
5241 // Since groups are serialized at the end of the file/footprint, the Uuid should already
5242 // have been seen and exist in the board.
5243 KIID uuid( CurStr() );
5244 aGroupInfo.memberUuids.push_back( uuid );
5245 }
5246}
5247
5248
5250{
5251 wxCHECK_RET( CurTok() == T_group,
5252 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
5253
5254 T token;
5255
5256 m_groupInfos.push_back( GROUP_INFO() );
5257 GROUP_INFO& groupInfo = m_groupInfos.back();
5258
5259 while( ( token = NextTok() ) != T_LEFT )
5260 {
5261 if( token == T_STRING )
5262 groupInfo.name = FromUTF8();
5263 else
5264 Expecting( "group name or locked" );
5265 }
5266
5267 for( ; token != T_RIGHT; token = NextTok() )
5268 {
5269 if( token != T_LEFT )
5270 Expecting( T_LEFT );
5271
5272 token = NextTok();
5273
5274 switch( token )
5275 {
5276 case T_uuid:
5277 NextTok();
5278 groupInfo.uuid = parseKIID();
5279 NeedRIGHT();
5280 break;
5281
5282 case T_lib_id:
5283 {
5284 token = NextTok();
5285
5286 if( !IsSymbol( token ) && token != T_NUMBER )
5287 Expecting( "symbol|number" );
5288
5289 LIB_ID libId;
5290 wxString name = FromUTF8();
5291 // Some symbol LIB_IDs have the '/' character escaped which can break
5292 // symbol links. The '/' character is no longer an illegal LIB_ID character so
5293 // it doesn't need to be escaped.
5294 name.Replace( "{slash}", "/" );
5295
5296 int bad_pos = groupInfo.libId.Parse( name );
5297
5298 if( bad_pos >= 0 )
5299 {
5300 if( static_cast<int>( name.size() ) > bad_pos )
5301 {
5302 wxString msg = wxString::Format( _( "Group library link %s contains invalid character '%c'" ), name,
5303 name[bad_pos] );
5304
5305 THROW_PARSE_ERROR( msg, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5306 }
5307
5308 THROW_PARSE_ERROR( _( "Invalid library ID" ), CurSource(), CurLine(), CurLineNumber(), CurOffset() );
5309 }
5310
5311 NeedRIGHT();
5312 break;
5313 }
5314
5315 case T_members:
5316 {
5317 parseGroupMembers( groupInfo );
5318 break;
5319 }
5320
5321 case T_locked:
5322 groupInfo.locked = parseBool();
5323 NeedRIGHT();
5324 break;
5325
5326 default:
5327 Expecting( "uuid, lib_id, members, locked" );
5328 }
5329 }
5330}
5331
5332
5334{
5335 if( !aParent )
5336 return;
5337
5338 auto getItem =
5339 [&]( const KIID& aId )
5340 {
5341 SCH_ITEM* aItem = nullptr;
5342
5343 for( SCH_ITEM* item : aParent->Items() )
5344 {
5345 if( item->m_Uuid == aId )
5346 {
5347 aItem = item;
5348 break;
5349 }
5350 }
5351
5352 return aItem;
5353 };
5354
5355 // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
5356 // to in the group declarations we saw.
5357 //
5358 // First add all group objects so subsequent GetItem() calls for nested groups work.
5359 for( const GROUP_INFO& groupInfo : m_groupInfos )
5360 {
5361 SCH_GROUP* group = nullptr;
5362
5363 group = new SCH_GROUP( aParent );
5364 group->SetName( groupInfo.name );
5365
5366 const_cast<KIID&>( group->m_Uuid ) = groupInfo.uuid;
5367
5368 if( groupInfo.libId.IsValid() )
5369 group->SetDesignBlockLibId( groupInfo.libId );
5370
5371 group->SetLocked( groupInfo.locked );
5372
5373 aParent->Append( group );
5374 }
5375
5376 for( const GROUP_INFO& groupInfo : m_groupInfos )
5377 {
5378 SCH_GROUP* group = static_cast<SCH_GROUP*>( getItem( groupInfo.uuid ) );
5379
5380 if( group && group->Type() == SCH_GROUP_T )
5381 {
5382 for( const KIID& aUuid : groupInfo.memberUuids )
5383 {
5384 if( SCH_ITEM* gItem = getItem( aUuid ) )
5385 group->AddItem( gItem );
5386 }
5387 }
5388 }
5389
5390 aParent->GroupsSanityCheck( true );
5391}
5392
5393
5395{
5396 // Skip tokens until we exit the current S-expression block.
5397 // This is used for error recovery when parsing fails mid-symbol.
5398 while( aDepth > 0 )
5399 {
5400 T token = NextTok();
5401
5402 if( token == T_EOF )
5403 break;
5404 else if( token == T_LEFT )
5405 aDepth++;
5406 else if( token == T_RIGHT )
5407 aDepth--;
5408 }
5409}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:118
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
void SetContentModified(bool aModified=true)
Definition base_screen.h:59
int GetPPI() const
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
EDA_ANGLE Normalize()
Definition eda_angle.h:229
const KIID m_Uuid
Definition eda_item.h:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
EDA_ITEM * GetParent() const
Definition eda_item.h:114
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
FILL_T GetFillMode() const
Definition eda_shape.h:154
SHAPE_POLY_SET & GetPolyShape()
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:166
int GetPointCount() const
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:232
void SetFillMode(FILL_T aFill)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:91
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:280
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:516
void SetUnresolvedFontName(const wxString &aFontName)
Definition eda_text.h:260
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:109
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:560
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:401
void SetBoldFlag(bool aBold)
Set only the bold flag, without changing the font.
Definition eda_text.cpp:362
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:370
void SetLineSpacing(double aLineSpacing)
Definition eda_text.cpp:508
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:268
static bool ValidateHyperlink(const wxString &aURL)
Check if aURL is a valid hyperlink.
void SetItalicFlag(bool aItalic)
Set only the italic flag, without changing the font.
Definition eda_text.cpp:311
void SetHyperlink(wxString aLink)
Definition eda_text.h:415
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:254
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:283
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:393
void ParseEmbedded(EMBEDDED_FILES *aFiles)
const std::vector< wxString > * UpdateFontFiles()
Helper function to get a list of fonts for fontconfig to add to the library.
const std::vector< wxString > * GetFontFiles() const
If we just need the cached version of the font files, we can use this function which is const and wil...
void SetAreFontsEmbedded(bool aEmbedFonts)
Simple container to manage fill parameters.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
double r
Red component.
Definition color4d.h:393
double g
Green component.
Definition color4d.h:394
double a
Alpha component.
Definition color4d.h:396
double b
Blue component.
Definition color4d.h:395
Definition kiid.h:48
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:52
Define a library symbol object.
Definition lib_symbol.h:83
wxString GetName() const override
Definition lib_symbol.h:145
void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode) override
EMBEDDED_FILES * GetEmbeddedFiles() override
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:66
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
void SetWidthMM(double aWidthInMM)
Definition page_info.h:140
void SetHeightMM(double aHeightInMM)
Definition page_info.h:145
const PAGE_SIZE_TYPE & GetType() const
Definition page_info.h:102
A progress reporter interface for use in multi-threaded environments.
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Holds all the data relating to one schematic.
Definition schematic.h:89
EMBEDDED_FILES * GetEmbeddedFiles() override
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:40
Class for a wire to bus entry.
void SetPinLength(int aLength)
Definition sch_label.h:484
bool IsMandatory() const
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:123
FIELD_T GetId() const
Definition sch_field.h:127
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
void setId(FIELD_T aId)
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:52
bool m_appending
Appending load status.
SCH_FIELD * parseSchField(SCH_ITEM *aParent)
void parseSchSymbolInstances(SCH_SCREEN *aScreen)
void parsePinNumbers(std::unique_ptr< LIB_SYMBOL > &aSymbol)
LIB_SYMBOL * parseLibSymbol(LIB_SYMBOL_MAP &aSymbolLibMap)
void parseBusAlias(SCH_SCREEN *aScreen)
void parseTITLE_BLOCK(TITLE_BLOCK &aTitleBlock)
void parseGroupMembers(GROUP_INFO &aGroupInfo)
void parseStroke(STROKE_PARAMS &aStroke)
Parse stroke definition aStroke.
int m_unit
The current unit being parsed.
void parseEDA_TEXT(EDA_TEXT *aText, bool aConvertOverbarSyntax, bool aEnforceMinTextSize=true)
void resolveGroups(SCH_SCREEN *aParent)
SCH_SHEET * m_rootSheet
The rootsheet for full project loads or null for importing a schematic.
void ParseLib(LIB_SYMBOL_MAP &aSymbolLibMap)
std::vector< GROUP_INFO > m_groupInfos
void parseHeader(TSCHEMATIC_T::T aHeaderType, int aFileVersion)
void parseBodyStyles(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parseMargins(int &aLeft, int &aTop, int &aRight, int &aBottom)
wxString m_symbolName
The current symbol name.
VECTOR2I parseXY(bool aInvertY=false)
void parsePinNames(std::unique_ptr< LIB_SYMBOL > &aSymbol)
std::vector< wxString > m_parseWarnings
Non-fatal warnings collected during parsing.
void ParseSchematic(SCH_SHEET *aSheet, bool aIsCopyablyOnly=false, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Parse the internal LINE_READER object into aSheet.
SCH_SHEET_PIN * parseSchSheetPin(SCH_SHEET *aSheet)
int m_bodyStyle
The current body style being parsed.
SCH_FIELD * parseProperty(std::unique_ptr< LIB_SYMBOL > &aSymbol)
void parseSchTextBoxContent(SCH_TEXTBOX *aTextBox)
bool parseMaybeAbsentBool(bool aDefaultValue)
Parses a boolean flag inside a list that existed before boolean normalization.
void parsePAGE_INFO(PAGE_INFO &aPageInfo)
int m_requiredVersion
Set to the symbol library file version required.
int m_maxError
Max deviation allowed when approximating bezier curves.
LIB_SYMBOL * ParseSymbol(LIB_SYMBOL_MAP &aSymbolLibMap, int aFileVersion=SEXPR_SYMBOL_LIB_FILE_VERSION)
Parse internal LINE_READER object into symbols and return all found.
void skipToBlockEnd(int aDepth=1)
Skip tokens until we reach the end of the current S-expression block.
void parseSchSheetInstances(SCH_SHEET *aRootSheet, SCH_SCREEN *aScreen)
SCH_IO_KICAD_SEXPR_PARSER(LINE_READER *aLineReader=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, unsigned aLineCount=0, SCH_SHEET *aRootSheet=nullptr, bool aIsAppending=false)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
void SetLocked(bool aLocked) override
Definition sch_item.h:257
bool IsLocked() const override
Definition sch_item.cpp:149
void SetFieldsAutoplaced(AUTOPLACE_ALGO aAlgo)
Definition sch_item.h:630
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:181
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:212
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:140
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:202
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:149
void SetFileFormatVersionAtLoad(int aVersion)
Definition sch_screen.h:138
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:727
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:167
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void AddLibSymbol(LIB_SYMBOL *aLibSymbol)
Add aLibSymbol to the library symbol map.
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition.
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:142
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
const KIID & GetUuid() const
Definition sch_screen.h:532
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
void SetLegacySymbolInstanceData()
Update the symbol value and footprint instance data for legacy designs.
SCHEMATIC * Schematic() const
void FixupEmbeddedData()
After loading a file from disk, the library symbols do not yet contain the full data for their embedd...
int GetFileFormatVersionAtLoad() const
Definition sch_screen.h:139
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
KIID m_uuid
A unique identifier for each schematic file.
Definition sch_screen.h:735
std::vector< SCH_SYMBOL_INSTANCE > m_symbolInstances
The list of symbol instances loaded from the schematic file.
Definition sch_screen.h:726
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:86
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:63
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:58
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Variant information for a schematic sheet.
void InitializeAttributes(const SCH_SHEET &aSheet)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:140
Variant information for a schematic symbol.
void InitializeAttributes(const SCH_SYMBOL &aSymbol)
Schematic symbol object.
Definition sch_symbol.h:76
void SetMarginBottom(int aBottom)
Definition sch_textbox.h:68
int GetLegacyTextMargin() const
void SetMarginLeft(int aLeft)
Definition sch_textbox.h:65
void SetMarginRight(int aRight)
Definition sch_textbox.h:67
void SetExcludedFromSim(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Definition sch_textbox.h:99
void SetMarginTop(int aTop)
Definition sch_textbox.h:66
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void ParseStroke(STROKE_PARAMS &aStroke)
Simple container to manage line stroke parameters.
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
LINE_STYLE GetLineStyle() const
KIGFX::COLOR4D GetColor() const
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:41
void SetRevision(const wxString &aRevision)
Definition title_block.h:81
void SetComment(int aIdx, const wxString &aComment)
void SetTitle(const wxString &aTitle)
Definition title_block.h:58
void SetCompany(const wxString &aCompany)
Definition title_block.h:91
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:71
for transforming drawing coordinates for a wxDC device context.
Definition transform.h:46
wxString m_Name
bool m_ExcludedFromBOM
std::map< wxString, wxString > m_Fields
bool m_ExcludedFromPosFiles
bool m_ExcludedFromSim
bool m_ExcludedFromBoard
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
@ DSN_LEFT
Definition dsnlexer.h:67
@ DSN_RIGHT
Definition dsnlexer.h:66
@ DSN_STRING
Definition dsnlexer.h:68
@ DSN_EOF
Definition dsnlexer.h:69
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ NO_RECURSE
Definition eda_item.h:54
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:48
@ FILLED_WITH_COLOR
Definition eda_shape.h:62
@ NO_FILL
Definition eda_shape.h:59
@ REVERSE_HATCH
Definition eda_shape.h:64
@ HATCH
Definition eda_shape.h:63
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:61
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:60
@ CROSS_HATCH
Definition eda_shape.h:65
FONTCONFIG * Fontconfig()
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
KIID & NilUuid()
Definition kiid.cpp:68
@ LAYER_DEVICE
Definition layer_ids.h:468
@ LAYER_WIRE
Definition layer_ids.h:454
@ LAYER_NOTES
Definition layer_ids.h:469
@ LAYER_BUS
Definition layer_ids.h:455
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
#define MAX_PAGE_SIZE_EESCHEMA_MM
Definition page_info.h:41
#define MIN_PAGE_SIZE_MM
Min and max page sizes for clamping, in mm.
Definition page_info.h:39
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
@ PT_NC
not connected (must be left open)
Definition pin_type.h:50
@ PT_OUTPUT
usual output
Definition pin_type.h:38
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:40
@ PT_NIC
not internally connected (may be connected to anything)
Definition pin_type.h:44
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:39
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:49
@ PT_POWER_OUT
output of a regulator: intended to be connected to power input pins
Definition pin_type.h:47
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:48
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:46
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:45
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:43
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:127
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:135
GRAPHIC_PINSHAPE
Definition pin_type.h:84
const SCH_FIELD * FindField(const std::vector< SCH_FIELD > &aFields, FIELD_T aFieldId)
Definition sch_field.h:372
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
Class to handle a set of SCH_ITEMs.
double parseDouble(LINE_READER &aReader, const char *aLine, const char **aOutput)
Parses an ASCII point string with possible leading whitespace into a double precision floating point ...
void fixupSchFillMode(SCH_SHAPE *aShape)
@ AUTOPLACE_NONE
Definition sch_item.h:70
@ AUTOPLACE_AUTO
Definition sch_item.h:71
@ L_BIDI
Definition sch_label.h:102
@ L_TRISTATE
Definition sch_label.h:103
@ L_UNSPECIFIED
Definition sch_label.h:104
@ F_DOT
Definition sch_label.h:107
@ F_ROUND
Definition sch_label.h:108
@ L_OUTPUT
Definition sch_label.h:101
@ F_DIAMOND
Definition sch_label.h:109
@ L_INPUT
Definition sch_label.h:100
@ F_RECTANGLE
Definition sch_label.h:110
#define SIM_LEGACY_ENABLE_FIELD
Definition sim_model.h:64
#define SIM_LEGACY_ENABLE_FIELD_V7
Definition sim_model.h:60
const int scale
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
wxString UnescapeString(const wxString &aSource)
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
A filename or source description, a problem input line, a line number, a byte offset,...
wxString m_Name
Definition sch_pin.h:45
GRAPHIC_PINSHAPE m_Shape
Definition sch_pin.h:46
ELECTRICAL_PINTYPE m_Type
Definition sch_pin.h:47
A simple container for sheet instance information.
std::map< wxString, SCH_SHEET_VARIANT > m_Variants
A list of sheet variants.
A simple container for schematic symbol instance information.
std::map< wxString, SCH_SYMBOL_VARIANT > m_Variants
A list of symbol variants.
@ SYM_MIRROR_Y
Definition symbol.h:44
@ SYM_MIRROR_X
Definition symbol.h:43
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ USER
The field ID hasn't been set yet; field is invalid.
@ INTERSHEET_REFS
Global label cross-reference page numbers.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
#define SHEET_MANDATORY_FIELDS
#define GLOBALLABEL_MANDATORY_FIELDS
wxString GetCanonicalFieldName(FIELD_T aFieldType)
KIBIS top(path, &reporter)
KIBIS_PIN * pin
std::vector< std::vector< std::string > > table
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
static constexpr double IU_PER_MM
Mock up a conversion function.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
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:521
@ SCH_GROUP_T
Definition typeinfo.h:174
@ SCH_SYMBOL_T
Definition typeinfo.h:173
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:172
@ SCH_LABEL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:176
@ SCH_TEXT_T
Definition typeinfo.h:152
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:169
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687