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