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