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