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