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