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