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