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