KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_kicad_sexpr_lib_cache.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * @author Wayne Stambaugh <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <fmt/format.h>
23#include <set>
24
25#include <wx/log.h>
26#include <wx/dir.h>
27
28#include <base_units.h>
29#include <build_version.h>
30#include <common.h>
31#include <sch_shape.h>
32#include <lib_symbol.h>
33#include <sch_textbox.h>
34#include <macros.h>
35#include <richio.h>
39#include <string_utils.h>
40#include <trace_helpers.h>
42
43
44SCH_IO_KICAD_SEXPR_LIB_CACHE::SCH_IO_KICAD_SEXPR_LIB_CACHE( const wxString& aFullPathAndFileName ) :
45 SCH_IO_LIB_CACHE( aFullPathAndFileName )
46{
48}
49
50
54
55
57{
58 // Normalize the path: if it's a directory on the filesystem, ensure m_libFileName
59 // is marked as a directory so that IsDir() checks work correctly throughout the code.
60 // wxFileName::IsDir() only checks if the path string ends with a separator, not if
61 // the path is actually a directory on the filesystem.
62 if( !m_libFileName.IsDir() && wxFileName::DirExists( m_libFileName.GetFullPath() ) )
63 m_libFileName.AssignDir( m_libFileName.GetFullPath() );
64
65 if( !isLibraryPathValid() )
66 THROW_IO_ERRORF( _( "Library '%s' not found." ), m_libFileName.GetFullPath() );
67
68 wxCHECK_RET( m_libFileName.IsAbsolute(),
69 wxString::Format( wxT( "Cannot use relative file paths in sexpr plugin to open library '%s'." ),
70 m_libFileName.GetFullPath() ) );
71
72 if( !m_libFileName.IsDir() )
73 {
74 wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library file '%s'",
75 m_libFileName.GetFullPath() );
76
77 FILE_LINE_READER reader( m_libFileName.GetFullPath() );
78
79 SCH_IO_KICAD_SEXPR_PARSER parser( &reader );
80
81 parser.ParseLib( m_symbols );
82
86
87 // Check if there were any parse warnings (symbols that failed to parse).
88 // If so, mark the library as having parse errors and throw to notify the user.
89 // The library has loaded all valid symbols, but saving would lose the bad ones.
90 const std::vector<wxString>& warnings = parser.GetParseWarnings();
91
92 if( !warnings.empty() )
93 {
94 SetParseError( true );
95
96 wxString errorMsg = wxString::Format(
97 _( "Library '%s' loaded with errors:\n\n" ), m_libFileName.GetFullPath() );
98
99 for( const wxString& warning : warnings )
100 errorMsg += warning + wxT( "\n\n" );
101
102 errorMsg += _( "The library cannot be saved until these errors are fixed manually." );
103
104 THROW_IO_ERROR( errorMsg );
105 }
106 }
107 else
108 {
109 wxString libFileName;
110
111 wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library folder '%s'", m_libFileName.GetPath() );
112
113 // Clear source file tracking for fresh load
114 m_symbolSourceFiles.clear();
115
116 wxFileName tmp( m_libFileName.GetPath(), wxS( "dummy" ), wxString( FILEEXT::KiCadSymbolLibFileExtension ) );
117 wxDir dir( m_libFileName.GetPath() );
118 wxString fileSpec = wxS( "*." ) + wxString( FILEEXT::KiCadSymbolLibFileExtension );
119
120 if( dir.GetFirst( &libFileName, fileSpec ) )
121 {
122 wxString errorCache;
123
124 do
125 {
126 tmp.SetFullName( libFileName );
127 wxString sourceFilePath = tmp.GetFullPath();
128
129 // Track symbol pointers before parsing so we can detect which were replaced.
130 // When the parser encounters a duplicate name, it overwrites the existing
131 // symbol, so we need to update source tracking for those symbols too.
132 std::map<wxString, LIB_SYMBOL*> existingPtrs;
133
134 for( const auto& [ name, symbol ] : m_symbols )
135 existingPtrs[ name ] = symbol;
136
137 try
138 {
139 FILE_LINE_READER reader( sourceFilePath );
140 SCH_IO_KICAD_SEXPR_PARSER parser( &reader );
141
142 parser.ParseLib( m_symbols );
144
145 // Update source tracking for all symbols that came from this file.
146 // This includes both new symbols and symbols that were overwritten
147 // (when a duplicate name existed in a previously loaded file).
148 for( const auto& [ name, symbol ] : m_symbols )
149 {
150 auto it = existingPtrs.find( name );
151
152 if( it == existingPtrs.end() )
153 {
154 // New symbol from this file
155 m_symbolSourceFiles[ name ] = sourceFilePath;
156 }
157 else if( it->second != symbol )
158 {
159 // Symbol pointer changed - this file overwrote the previous version.
160 // Update tracking so we save to this file (the one whose version
161 // is actually in memory).
162 m_symbolSourceFiles[ name ] = sourceFilePath;
163 }
164 }
165
166 // Collect any parse warnings from this file
167 for( const wxString& warning : parser.GetParseWarnings() )
168 {
169 SetParseError( true );
170
171 if( !errorCache.IsEmpty() )
172 errorCache += wxT( "\n\n" );
173
174 errorCache += warning;
175 }
176 }
177 catch( const IO_ERROR& ioe )
178 {
179 // Mark that we had a parse error - saving would lose symbols
180 SetParseError( true );
181
182 if( !errorCache.IsEmpty() )
183 errorCache += wxT( "\n\n" );
184
185 errorCache += wxString::Format( _( "Unable to read file '%s'" ) + '\n', sourceFilePath );
186 errorCache += ioe.What();
187 }
188 } while( dir.GetNext( &libFileName ) );
189
190 if( !errorCache.IsEmpty() )
191 {
192 errorCache += _( "\n\nThe library cannot be saved until these errors are fixed manually." );
193 THROW_IO_ERROR( errorCache );
194 }
195 }
196
199 }
200
201 // Remember the file modification time of library file when the cache snapshot was made,
202 // so that in a networked environment we will reload the cache as needed.
204}
205
206
207void SCH_IO_KICAD_SEXPR_LIB_CACHE::Save( const std::optional<bool>& aOpt )
208{
209 if( !m_isModified )
210 return;
211
212 // If the library had a parse error during loading, we cannot safely save it.
213 // Only symbols before the parse error were loaded, so saving would permanently
214 // lose all symbols after the error point. See issue #22241.
215 if( HasParseError() )
216 {
217 THROW_IO_ERRORF( _( "Cannot save library '%s' because it had a parse error during loading.\n\n"
218 "Saving would permanently lose symbols that could not be loaded.\n"
219 "Please fix the library file manually before saving." ),
220 m_libFileName.GetFullPath() );
221 }
222
223 // Write through symlinks, don't replace them.
224 wxFileName fn = GetRealFile();
225
226 // Normalize the path: if it's a directory on the filesystem, ensure fn is marked as a
227 // directory so that IsDir() checks work correctly.
228 if( !fn.IsDir() && wxFileName::DirExists( fn.GetFullPath() ) )
229 fn.AssignDir( fn.GetFullPath() );
230
231 if( !fn.IsDir() )
232 {
233 auto formatter = std::make_unique<PRETTIFIED_FILE_OUTPUTFORMATTER>( fn.GetFullPath() );
234
235 formatLibraryHeader( *formatter.get() );
236
237 std::vector<LIB_SYMBOL*> orderedSymbols;
238
239 for( const auto& [ name, symbol ] : m_symbols )
240 {
241 if( symbol )
242 orderedSymbols.push_back( symbol );
243 }
244
245 // Library must be ordered by inheritance depth.
246 std::sort( orderedSymbols.begin(), orderedSymbols.end(),
247 []( const LIB_SYMBOL* aLhs, const LIB_SYMBOL* aRhs )
248 {
249 unsigned int lhDepth = aLhs->GetInheritanceDepth();
250 unsigned int rhDepth = aRhs->GetInheritanceDepth();
251
252 if( lhDepth == rhDepth )
253 return aLhs->GetName() < aRhs->GetName();
254
255 return lhDepth < rhDepth;
256 } );
257
258 for( LIB_SYMBOL* symbol : orderedSymbols )
259 SaveSymbol( symbol, *formatter.get() );
260
261 formatter->Print( ")" );
262 formatter->Finish();
263 formatter.reset();
264 }
265 else
266 {
267 if( !fn.DirExists() )
268 {
269 if( !fn.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
270 THROW_IO_ERRORF( _( "Cannot create symbol library path '%s'." ), fn.GetPath() );
271 }
272
273 // Detect renamed symbols whose old source file entries are now orphaned.
274 // Schedule the old files for deletion so they don't linger on disk.
275 for( auto it = m_symbolSourceFiles.begin(); it != m_symbolSourceFiles.end(); )
276 {
277 if( m_symbols.find( it->first ) == m_symbols.end() )
278 {
279 m_pendingFileDeletes.insert( it->second );
280 it = m_symbolSourceFiles.erase( it );
281 }
282 else
283 {
284 ++it;
285 }
286 }
287
288 // Group symbols by their source file to preserve multi-symbol files
289 std::map<wxString, std::vector<LIB_SYMBOL*>> symbolsByFile;
290
291 for( const auto& [ name, symbol ] : m_symbols )
292 {
293 auto it = m_symbolSourceFiles.find( name );
294
295 if( it != m_symbolSourceFiles.end() )
296 {
297 // Symbol has a known source file - group it with others from that file
298 symbolsByFile[ it->second ].push_back( symbol );
299 }
300 else
301 {
302 // New symbol without source file - create individual file
303 wxFileName saveFn( fn );
304 saveFn.SetName( EscapeString( name, CTX_FILENAME ) );
306
307 symbolsByFile[ saveFn.GetFullPath() ].push_back( symbol );
308 }
309 }
310
311 // Sort each file's symbols by inheritance depth
312 auto sortByInheritance =
313 []( LIB_SYMBOL* aLhs, LIB_SYMBOL* aRhs )
314 {
315 unsigned int lhDepth = aLhs->GetInheritanceDepth();
316 unsigned int rhDepth = aRhs->GetInheritanceDepth();
317
318 if( lhDepth == rhDepth )
319 return aLhs->GetName() < aRhs->GetName();
320
321 return lhDepth < rhDepth;
322 };
323
324 // Write each file
325 for( auto& [ filePath, symbols ] : symbolsByFile )
326 {
327 wxFileName oldFn = filePath;
328
329 if( oldFn.GetPath() != m_libFileName.GetPath() )
330 oldFn.SetPath( m_libFileName.GetPath() );
331
332 std::sort( symbols.begin(), symbols.end(), sortByInheritance );
333
334 auto formatter = std::make_unique<PRETTIFIED_FILE_OUTPUTFORMATTER>( oldFn.GetFullPath() );
335
336 formatLibraryHeader( *formatter.get() );
337
338 for( LIB_SYMBOL* symbol : symbols )
339 SaveSymbol( symbol, *formatter.get() );
340
341 formatter->Print( ")" );
342 formatter->Finish();
343 formatter.reset();
344
345 // Update source file tracking for new symbols
346 for( LIB_SYMBOL* symbol : symbols )
347 m_symbolSourceFiles[ symbol->GetName() ] = filePath;
348 }
349
350 // Remove files for deleted symbols that are no longer needed
351 for( const wxString& deadFile : m_pendingFileDeletes )
352 {
353 if( symbolsByFile.find( deadFile ) == symbolsByFile.end() && wxFileExists( deadFile ) )
354 {
355 wxRemoveFile( deadFile );
356 }
357 }
358
359 m_pendingFileDeletes.clear();
360 }
361
363 m_isModified = false;
364}
365
366
368 const wxString& aLibName, bool aIncludeData )
369{
370 wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
371
372 // If we've requested to embed the fonts in the symbol, do so.
373 // Otherwise, clear the embedded fonts from the symbol. Embedded
374 // fonts will be used if available
375 if( aSymbol->GetAreFontsEmbedded() )
376 aSymbol->EmbedFonts();
377 else
379
380 std::vector<SCH_FIELD*> orderedFields;
381 std::string name = aFormatter.Quotew( aSymbol->GetLibId().GetLibItemName().wx_str() );
382 std::string unitName = aSymbol->GetLibId().GetLibItemName();
383
384 if( !aLibName.IsEmpty() )
385 {
386 name = aFormatter.Quotew( aLibName );
387
388 LIB_ID unitId;
389
390 wxCHECK2( unitId.Parse( aLibName ) < 0, /* do nothing */ );
391
392 unitName = unitId.GetLibItemName();
393 }
394
395 if( aSymbol->IsRoot() )
396 {
397 aFormatter.Print( "(symbol %s", name.c_str() );
398
399 if( aSymbol->IsGlobalPower() )
400 aFormatter.Print( "(power global)" );
401 else if( aSymbol->IsLocalPower() )
402 aFormatter.Print( "(power local)" );
403
404 // TODO: add uuid token here.
405
406 // TODO: add anchor position token here.
407
408 if( aSymbol->IsMultiBodyStyle() )
409 {
410 aFormatter.Print( "(body_styles " );
411
412 if( aSymbol->HasDeMorganBodyStyles() )
413 {
414 aFormatter.Print( "demorgan" );
415 }
416 else
417 {
418 for( const wxString& bodyStyle : aSymbol->GetBodyStyleNames() )
419 aFormatter.Print( "%s ", aFormatter.Quotew( bodyStyle ).c_str() );
420 }
421
422 aFormatter.Print( ")" );
423 }
424
425 if( !aSymbol->GetShowPinNumbers() )
426 aFormatter.Print( "(pin_numbers (hide yes))" );
427
428 if( aSymbol->GetPinNameOffset() != schIUScale.MilsToIU( DEFAULT_PIN_NAME_OFFSET )
429 || !aSymbol->GetShowPinNames() )
430 {
431 aFormatter.Print( "(pin_names" );
432
433 if( aSymbol->GetPinNameOffset() != schIUScale.MilsToIU( DEFAULT_PIN_NAME_OFFSET ) )
434 {
435 aFormatter.Print( "(offset %s)",
437 aSymbol->GetPinNameOffset() ).c_str() );
438 }
439
440 if( !aSymbol->GetShowPinNames() )
441 KICAD_FORMAT::FormatBool( &aFormatter, "hide", true );
442
443 aFormatter.Print( ")" );
444 }
445
446 KICAD_FORMAT::FormatBool( &aFormatter, "exclude_from_sim", aSymbol->GetExcludedFromSim() );
447 KICAD_FORMAT::FormatBool( &aFormatter, "in_bom", !aSymbol->GetExcludedFromBOM() );
448 KICAD_FORMAT::FormatBool( &aFormatter, "on_board", !aSymbol->GetExcludedFromBoard() );
449 KICAD_FORMAT::FormatBool( &aFormatter, "in_pos_files", !aSymbol->GetExcludedFromPosFiles() );
450
451 KICAD_FORMAT::FormatBool( &aFormatter, "duplicate_pin_numbers_are_jumpers",
453
454 const JUMPER_GROUP_SET& jumperGroups = aSymbol->JumperPinGroups();
455
456 if( !jumperGroups.IsEmpty() )
457 {
458 aFormatter.Print( "(jumper_pin_groups" );
459
460 for( const JUMPER_GROUP& group : jumperGroups.GetAll() )
461 {
462 aFormatter.Print( "(" );
463
464 for( const wxString& padName : group.GetNames() )
465 aFormatter.Print( "%s ", aFormatter.Quotew( padName ).c_str() );
466
467 aFormatter.Print( ")" );
468 }
469
470 aFormatter.Print( ")" );
471 }
472
473 // TODO: add atomic token here.
474
475 // TODO: add required token here."
476
477 aSymbol->GetFields( orderedFields );
478
479 for( SCH_FIELD* field : orderedFields )
480 saveField( field, aFormatter );
481
482 // @todo At some point in the future the lock status (all units interchangeable) should
483 // be set deterministically. For now a custom lock property is used to preserve the
484 // locked flag state.
485 if( aSymbol->UnitsLocked() )
486 {
487 SCH_FIELD locked( nullptr, FIELD_T::USER, "ki_locked" );
488 saveField( &locked, aFormatter );
489 }
490
491 saveDcmInfoAsFields( aSymbol, aFormatter );
492
493 savePinMapData( aSymbol, aFormatter );
494
495 for( const LIB_SYMBOL_UNIT& unit : aSymbol->GetUnitDrawItems() )
496 {
497 // Add quotes and escape chars like ") to the UTF8 unitName string
498 name = aFormatter.Quotes( unitName );
499 name.pop_back(); // Remove last char: the quote ending the string.
500
501 aFormatter.Print( "(symbol %s_%d_%d\"",
502 name.c_str(),
503 unit.m_unit,
504 unit.m_bodyStyle );
505
506 // if the unit has a display name, write that
507 if( aSymbol->GetUnitDisplayNames().contains( unit.m_unit ) )
508 {
509 name = aSymbol->GetUnitDisplayNames().at( unit.m_unit );
510 aFormatter.Print( "(unit_name %s)", aFormatter.Quotes( name ).c_str() );
511 }
512
513 // Enforce item ordering
514 auto cmp =
515 []( const SCH_ITEM* a, const SCH_ITEM* b )
516 {
517 return *a < *b;
518 };
519
520 std::multiset<SCH_ITEM*, decltype( cmp )> save_map( cmp );
521
522 for( SCH_ITEM* item : unit.m_items )
523 save_map.insert( item );
524
525 for( SCH_ITEM* item : save_map )
526 saveSymbolDrawItem( item, aFormatter );
527
528 aFormatter.Print( ")" );
529 }
530
531 KICAD_FORMAT::FormatBool( &aFormatter, "embedded_fonts", aSymbol->GetAreFontsEmbedded() );
532
533 if( !aSymbol->EmbeddedFileMap().empty() )
534 aSymbol->WriteEmbeddedFiles( aFormatter, aIncludeData );
535 }
536 else
537 {
538 std::shared_ptr<LIB_SYMBOL> parent = aSymbol->GetParent().lock();
539
540 wxASSERT( parent );
541
542 // Prefer the recorded parent name over dereferencing the live parent pointer. The parent
543 // LIB_SYMBOL uses a null_deleter shared_ptr, so the weak_ptr's control block can outlive the
544 // parent object (for example when a derived symbol is copied to another library and the
545 // buffered parent is freed before this symbol is serialized). In that state GetParent().lock()
546 // can return a non-null but dangling pointer, and reading parent->GetName() is a use-after-
547 // free that crashes release builds while only tripping the assertion above in debug builds.
548 // The recorded parent name is a value member and is always safe to read.
549 wxString parentName = aSymbol->GetParentName();
550
551 if( parentName.IsEmpty() && parent )
552 parentName = parent->GetName();
553
554 aFormatter.Print( "(symbol %s (extends %s)",
555 name.c_str(),
556 aFormatter.Quotew( parentName ).c_str() );
557
558 aSymbol->GetFields( orderedFields );
559
560 for( SCH_FIELD* field : orderedFields )
561 saveField( field, aFormatter );
562
563 saveDcmInfoAsFields( aSymbol, aFormatter );
564
565 savePinMapData( aSymbol, aFormatter );
566
567 KICAD_FORMAT::FormatBool( &aFormatter, "embedded_fonts", aSymbol->GetAreFontsEmbedded() );
568
569 if( !aSymbol->EmbeddedFileMap().empty() )
570 aSymbol->WriteEmbeddedFiles( aFormatter, aIncludeData );
571 }
572
573 aFormatter.Print( ")" );
574}
575
576
578 OUTPUTFORMATTER& aFormatter )
579{
580 wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
581
582 if( !aSymbol->GetRawKeyWords().IsEmpty() )
583 {
584 SCH_FIELD keywords( nullptr, FIELD_T::USER, wxString( "ki_keywords" ) );
585 keywords.SetVisible( false );
586 keywords.SetText( aSymbol->GetRawKeyWords() );
587 saveField( &keywords, aFormatter );
588 }
589
590 wxArrayString fpFilters = aSymbol->GetFPFilters();
591
592 if( !fpFilters.IsEmpty() )
593 {
594 wxString tmp;
595
596 for( const wxString& filter : fpFilters )
597 {
598 // Spaces are not handled in fp filter names so escape spaces if any
599 wxString curr_filter = EscapeString( filter, ESCAPE_CONTEXT::CTX_NO_SPACE );
600
601 if( tmp.IsEmpty() )
602 tmp = curr_filter;
603 else
604 tmp += " " + curr_filter;
605 }
606
607 SCH_FIELD description( nullptr, FIELD_T::USER, wxString( "ki_fp_filters" ) );
608 description.SetVisible( false );
609 description.SetText( tmp );
610 saveField( &description, aFormatter );
611 }
612}
613
614
616{
617 wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
618
619 // Emit only the symbol's own bundle; derived symbols inheriting the parent's maps write
620 // nothing here so the inheritance is preserved on round-trip.
621 const std::vector<ASSOCIATED_FOOTPRINT>& associations = aSymbol->GetAssociatedFootprints();
622
623 if( !associations.empty() )
624 {
625 aFormatter.Print( "(associated_footprints" );
626
627 for( const ASSOCIATED_FOOTPRINT& assoc : associations )
628 {
629 aFormatter.Print( "(footprint %s",
630 aFormatter.Quotew( assoc.m_FootprintLibId.GetUniStringLibId() ).c_str() );
631
632 if( !assoc.m_MapName.IsEmpty() )
633 aFormatter.Print( "(map %s)", aFormatter.Quotew( assoc.m_MapName ).c_str() );
634
635 aFormatter.Print( ")" );
636 }
637
638 aFormatter.Print( ")" );
639 }
640
641 const std::vector<PIN_MAP>& maps = aSymbol->GetPinMaps().GetAll();
642
643 if( !maps.empty() )
644 {
645 aFormatter.Print( "(pin_maps" );
646
647 for( const PIN_MAP& map : maps )
648 {
649 aFormatter.Print( "(pin_map %s", aFormatter.Quotew( map.GetName() ).c_str() );
650
651 for( const PIN_MAP_ENTRY& entry : map.GetEntries() )
652 {
653 aFormatter.Print( "(entry %s %s)",
654 aFormatter.Quotew( entry.m_PinNumber ).c_str(),
655 aFormatter.Quotew( entry.m_PadNumber ).c_str() );
656 }
657
658 aFormatter.Print( ")" );
659 }
660
661 aFormatter.Print( ")" );
662 }
663}
664
665
667{
668 wxCHECK_RET( aItem, "Invalid SCH_ITEM pointer." );
669
670 switch( aItem->Type() )
671 {
672 case SCH_SHAPE_T:
673 {
674 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
675 STROKE_PARAMS stroke = shape->GetStroke();
676 FILL_T fillMode = shape->GetFillMode();
677 COLOR4D fillColor = shape->GetFillColor();
678 bool isPrivate = shape->IsPrivate();
679
680 switch( shape->GetShape() )
681 {
682 case SHAPE_T::ARC:
683 formatArc( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
684 break;
685
686 case SHAPE_T::CIRCLE:
687 formatCircle( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
688 break;
689
691 formatRect( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
692 break;
693
694 case SHAPE_T::BEZIER:
695 formatBezier(&aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
696 break;
697
698 case SHAPE_T::POLY:
699 formatPoly( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
700 break;
701
702 case SHAPE_T::ELLIPSE:
703 formatEllipse( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
704 break;
705
707 formatEllipseArc( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
708 break;
709
710 default:
712 }
713
714 break;
715 }
716
717 case SCH_PIN_T:
718 savePin( static_cast<SCH_PIN*>( aItem ), aFormatter );
719 break;
720
721 case SCH_TEXT_T:
722 saveText( static_cast<SCH_TEXT*>( aItem ), aFormatter );
723 break;
724
725 case SCH_TEXTBOX_T:
726 saveTextBox( static_cast<SCH_TEXTBOX*>( aItem ), aFormatter );
727 break;
728
729 default:
730 UNIMPLEMENTED_FOR( aItem->GetClass() );
731 }
732}
733
734
736{
737 wxCHECK_RET( aField && aField->Type() == SCH_FIELD_T, "Invalid SCH_FIELD object." );
738
739 wxString fieldName = aField->GetName();
740
741 if( aField->IsMandatory() )
742 fieldName = GetDefaultFieldName( aField->GetId(), UNTRANSLATED );
743
744 aFormatter.Print( "(property %s %s %s (at %s %s %s)",
745 aField->IsPrivate() ? "private" : "",
746 aFormatter.Quotew( fieldName ).c_str(),
747 aFormatter.Quotew( aField->GetText() ).c_str(),
750 fmt::format( "{:g}", aField->GetTextAngle().AsDegrees() ).c_str() );
751
752 KICAD_FORMAT::FormatBool( &aFormatter, "show_name", aField->IsNameShown() );
753
754 KICAD_FORMAT::FormatBool( &aFormatter, "do_not_autoplace", !aField->CanAutoplace() );
755
756 if( !aField->IsVisible() )
757 KICAD_FORMAT::FormatBool( &aFormatter, "hide", true );
758
759 aField->Format( &aFormatter, 0 );
760 KICAD_FORMAT::FormatCustomProperties( &aFormatter, *aField );
761 aFormatter.Print( ")" );
762}
763
764
766{
767 wxCHECK_RET( aPin && aPin->Type() == SCH_PIN_T, "Invalid SCH_PIN object." );
768
769 aPin->ClearFlags( IS_CHANGED );
770
771 aFormatter.Print( "(pin %s %s (at %s %s %s) (length %s)",
773 getPinShapeToken( aPin->GetShape() ),
778
779 if( !aPin->IsVisible() )
780 KICAD_FORMAT::FormatBool( &aFormatter, "hide", true );
781
782 // This follows the EDA_TEXT effects formatting for future expansion.
783 aFormatter.Print( "(name %s (effects (font (size %s %s))))",
784 aFormatter.Quotew( aPin->GetName() ).c_str(),
787
788 aFormatter.Print( "(number %s (effects (font (size %s %s))))",
789 aFormatter.Quotew( aPin->GetNumber() ).c_str(),
792
793
794 for( const std::pair<const wxString, SCH_PIN::ALT>& alt : aPin->GetAlternates() )
795 {
796 // There was a bug somewhere in the alternate pin code that allowed pin alternates with no
797 // name to be saved in library symbols. This strips any invalid alternates just in case
798 // that code resurfaces.
799 if( alt.second.m_Name.IsEmpty() )
800 continue;
801
802 aFormatter.Print( "(alternate %s %s %s)",
803 aFormatter.Quotew( alt.second.m_Name ).c_str(),
804 getPinElectricalTypeToken( alt.second.m_Type ),
805 getPinShapeToken( alt.second.m_Shape ) );
806 }
807
808 KICAD_FORMAT::FormatCustomProperties( &aFormatter, *aPin );
809 aFormatter.Print( ")" );
810}
811
812
814{
815 wxCHECK_RET( aText && aText->Type() == SCH_TEXT_T, "Invalid SCH_TEXT object." );
816
817 aFormatter.Print( "(text %s %s (at %s %s %d)",
818 aText->IsPrivate() ? "private" : "",
819 aFormatter.Quotew( aText->GetText() ).c_str(),
822 aText->GetTextAngle().AsTenthsOfADegree() );
823
824 aText->EDA_TEXT::Format( &aFormatter, 0 );
825 KICAD_FORMAT::FormatCustomProperties( &aFormatter, *aText );
826 aFormatter.Print( ")" );
827}
828
829
831{
832 wxCHECK_RET( aTextBox && aTextBox->Type() == SCH_TEXTBOX_T, "Invalid SCH_TEXTBOX object." );
833
834 aFormatter.Print( "(text_box %s %s",
835 aTextBox->IsPrivate() ? "private" : "",
836 aFormatter.Quotew( aTextBox->GetText() ).c_str() );
837
838 VECTOR2I pos = aTextBox->GetStart();
839 VECTOR2I size = aTextBox->GetEnd() - pos;
840
841 aFormatter.Print( "(at %s %s %s) (size %s %s) (margins %s %s %s %s)",
844 EDA_UNIT_UTILS::FormatAngle( aTextBox->GetTextAngle() ).c_str(),
851
852 aTextBox->GetStroke().Format( &aFormatter, schIUScale );
853 formatFill( &aFormatter, aTextBox->GetFillMode(), aTextBox->GetFillColor() );
854 aTextBox->EDA_TEXT::Format( &aFormatter, 0 );
855 KICAD_FORMAT::FormatCustomProperties( &aFormatter, *aTextBox );
856 aFormatter.Print( ")" );
857}
858
859
860void SCH_IO_KICAD_SEXPR_LIB_CACHE::DeleteSymbol( const wxString& aSymbolName )
861{
862 LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbolName );
863
864 if( it == m_symbols.end() )
865 THROW_IO_ERRORF( _( "library %s does not contain a symbol named %s" ), m_libFileName.GetFullName(), aSymbolName );
866
867 LIB_SYMBOL* symbol = it->second;
868
869 auto recordSourceFileForDeletion = [this]( const wxString& aName )
870 {
871 auto srcIt = m_symbolSourceFiles.find( aName );
872
873 if( srcIt != m_symbolSourceFiles.end() )
874 {
875 m_pendingFileDeletes.insert( srcIt->second );
876 m_symbolSourceFiles.erase( srcIt );
877 }
878 };
879
880 if( symbol->IsRoot() )
881 {
882 LIB_SYMBOL* rootSymbol = symbol;
883
884 recordSourceFileForDeletion( aSymbolName );
885
886 // Remove the root symbol and all its children.
887 m_symbols.erase( it );
888
889 LIB_SYMBOL_MAP::iterator it1 = m_symbols.begin();
890
891 while( it1 != m_symbols.end() )
892 {
893 if( it1->second->IsDerived()
894 && it1->second->GetParent().lock() == rootSymbol->SharedPtr() )
895 {
896 recordSourceFileForDeletion( it1->first );
897 delete it1->second;
898 it1 = m_symbols.erase( it1 );
899 }
900 else
901 {
902 it1++;
903 }
904 }
905
906 delete rootSymbol;
907 }
908 else
909 {
910 recordSourceFileForDeletion( aSymbolName );
911 // Just remove the alias.
912 m_symbols.erase( it );
913 delete symbol;
914 }
915
917 m_isModified = true;
918}
919
920
922{
923 for( auto& [name, symbol] : m_symbols )
924 {
925 if( symbol->GetParentName().IsEmpty() )
926 continue;
927
928 auto it = m_symbols.find( symbol->GetParentName() );
929
930 if( it == m_symbols.end() )
931 {
932 wxString error;
933
934 error.Printf( _( "No parent for extended symbol %s found in library '%s'" ),
935 name.c_str(), m_libFileName.GetFullPath() );
936 THROW_IO_ERROR( error );
937 }
938
939 symbol->SetParent( it->second );
940 }
941}
942
943
945{
946 aFormatter.Print( "(kicad_symbol_lib (version %d) (generator \"kicad_symbol_editor\") "
947 "(generator_version \"%s\")",
949 GetMajorMinorVersion().c_str().AsChar() );
950}
951
952
954{
955 if( !m_libFileName.IsDir() )
956 return m_libFileName.FileExists();
957 else
958 return m_libFileName.DirExists();
959}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
int AsTenthsOfADegree() const
Definition eda_angle.h:117
double AsDegrees() const
Definition eda_angle.h:115
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
FILL_T GetFillMode() const
Definition eda_shape.h:150
SHAPE_T GetShape() const
Definition eda_shape.h:177
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:333
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:283
COLOR4D GetFillColor() const
Definition eda_shape.h:161
wxString SHAPE_T_asString() const
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual bool IsVisible() const
Definition eda_text.h:226
virtual void Format(OUTPUTFORMATTER *aFormatter, int aControlBits) const
Output the object to aFormatter in s-expression form.
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:178
void WriteEmbeddedFiles(OUTPUTFORMATTER &aOut, bool aWriteData) const
Output formatter for the embedded files.
void ClearEmbeddedFonts()
Remove all embedded fonts from the collection.
const std::map< wxString, std::shared_ptr< EMBEDDED_FILE > > & EmbeddedFileMap() const
Provide an iterable view of the file collection.
bool GetAreFontsEmbedded() const
A LINE_READER that reads from an open file.
Definition richio.h:157
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Represents an (ordered) list of JUMPER_GROUP objects.
const std::vector< JUMPER_GROUP > & GetAll() const
bool IsEmpty() const
Represents a group of jumper pins or pads, keyed by name.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
Define a library symbol object.
Definition lib_symbol.h:114
const wxString & GetRawKeyWords() const
Return only this symbol's keywords, without inheriting from its parent.
Definition lib_symbol.h:218
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
std::weak_ptr< LIB_SYMBOL > & GetParent()
Definition lib_symbol.h:152
void GetFields(std::vector< SCH_FIELD * > &aList, bool aVisibleOnly=false) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
bool UnitsLocked() const
Check whether symbol units are interchangeable.
bool IsRoot() const override
For symbols derived from other symbols, IsRoot() indicates no derivation.
Definition lib_symbol.h:240
std::vector< struct LIB_SYMBOL_UNIT > GetUnitDrawItems()
Return a list of SCH_ITEM objects separated by unit and convert number.
std::map< int, wxString > & GetUnitDisplayNames()
Definition lib_symbol.h:881
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:902
JUMPER_GROUP_SET & JumperPinGroups()
Each jumper pin group is a set of pin numbers that should be treated as internally connected.
Definition lib_symbol.h:891
wxString GetName() const override
Definition lib_symbol.h:183
bool IsLocalPower() const override
wxArrayString GetFPFilters() const
Definition lib_symbol.h:252
std::shared_ptr< LIB_SYMBOL > SharedPtr() const
http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/sp_techniques.html#weak_without_shared.
Definition lib_symbol.h:125
const std::vector< wxString > & GetBodyStyleNames() const
bool HasDeMorganBodyStyles() const override
const PIN_MAP_SET & GetPinMaps() const
Pin-to-pad mapping (issue #2282).
Definition lib_symbol.h:266
EMBEDDED_FILES * GetEmbeddedFiles() override
bool IsGlobalPower() const override
const wxString & GetParentName() const
Definition lib_symbol.h:995
unsigned GetInheritanceDepth() const
Get the number of parents for this symbol.
bool GetDuplicatePinNumbersAreJumpers() const
Definition lib_symbol.h:884
void EmbedFonts() override
const std::vector< ASSOCIATED_FOOTPRINT > & GetAssociatedFootprints() const
Definition lib_symbol.h:270
An interface used to output 8 bit text in a convenient way.
Definition richio.h:294
std::string Quotew(const wxString &aWrapee) const
Definition richio.cpp:505
int PRINTF_FUNC_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition richio.cpp:432
virtual std::string Quotes(const std::string &aWrapee) const
Check aWrapee input string for a need to be quoted (e.g.
Definition richio.cpp:466
const std::vector< PIN_MAP > & GetAll() const
Definition pin_map.h:139
A named pin map.
Definition pin_map.h:65
bool IsMandatory() const
VECTOR2I GetPosition() const override
bool IsNameShown() const
Definition sch_field.h:229
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
FIELD_T GetId() const
Definition sch_field.h:143
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
bool CanAutoplace() const
Definition sch_field.h:240
void SetText(const wxString &aText) override
static void saveSymbolDrawItem(SCH_ITEM *aItem, OUTPUTFORMATTER &aFormatter)
SCH_IO_KICAD_SEXPR_LIB_CACHE(const wxString &aLibraryPath)
static void saveDcmInfoAsFields(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter)
static void savePinMapData(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter)
Write the symbol's own (non-inherited) pin maps and associated footprints (issue #2282).
static void SaveSymbol(LIB_SYMBOL *aSymbol, OUTPUTFORMATTER &aFormatter, const wxString &aLibName=wxEmptyString, bool aIncludeData=true)
static void saveTextBox(SCH_TEXTBOX *aTextBox, OUTPUTFORMATTER &aFormatter)
static void saveField(SCH_FIELD *aField, OUTPUTFORMATTER &aFormatter)
void formatLibraryHeader(OUTPUTFORMATTER &aFormatter)
void DeleteSymbol(const wxString &aName) override
static void savePin(SCH_PIN *aPin, OUTPUTFORMATTER &aFormatter)
static void saveText(SCH_TEXT *aText, OUTPUTFORMATTER &aFormatter)
void Save(const std::optional< bool > &aOpt=std::nullopt) override
Save the entire library to file m_libFileName;.
void updateParentSymbolLinks()
Update the parent symbol links for derived symbols.
Object to parser s-expression symbol library and schematic file formats.
const std::vector< wxString > & GetParseWarnings() const
Return any non-fatal parse warnings that occurred during parsing.
void ParseLib(LIB_SYMBOL_MAP &aSymbolLibMap)
void SetParseError(bool aHasError=true)
Set the parse error state.
bool HasParseError() const
LIB_SYMBOL_MAP m_symbols
wxFileName GetRealFile() const
long long GetLibModificationTime()
std::map< wxString, wxString > m_symbolSourceFiles
For folder-based libraries, track which source file each symbol was loaded from.
SCH_IO_LIB_CACHE(const wxString &aLibraryPath)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
bool IsPrivate() const
Definition sch_item.h:259
wxString GetClass() const override
Return the class name.
Definition sch_item.h:180
int GetNumberTextSize() const
Definition sch_pin.cpp:882
int GetLength() const
Definition sch_pin.cpp:421
const std::map< wxString, ALT > & GetAlternates() const
Definition sch_pin.h:219
bool IsVisible() const
Definition sch_pin.cpp:513
const wxString & GetName() const
Definition sch_pin.cpp:527
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:386
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:378
int GetNameTextSize() const
Definition sch_pin.cpp:856
const wxString & GetNumber() const
Definition sch_pin.h:144
GRAPHIC_PINSHAPE GetShape() const
Definition sch_pin.cpp:400
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:435
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:59
int GetMarginBottom() const
Definition sch_textbox.h:82
int GetMarginLeft() const
Definition sch_textbox.h:79
int GetMarginRight() const
Definition sch_textbox.h:81
int GetMarginTop() const
Definition sch_textbox.h:80
VECTOR2I GetPosition() const override
Definition sch_text.h:143
Simple container to manage line stroke parameters.
void Format(OUTPUTFORMATTER *out, const EDA_IU_SCALE &aIuScale) const
bool GetExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:231
int GetPinNameOffset() const
Definition symbol.h:163
virtual bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:201
virtual bool GetShowPinNames() const
Definition symbol.h:169
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:216
virtual bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:186
virtual bool GetShowPinNumbers() const
Definition symbol.h:175
wxString wx_str() const
Definition utf8.cpp:41
#define DEFAULT_PIN_NAME_OFFSET
The offset of the pin name string from the end of the pin in mils.
#define _(s)
FILL_T
Definition eda_fill.h:29
#define IS_CHANGED
Item was edited, and modified.
@ ELLIPSE
Definition eda_shape.h:62
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
static const std::string KiCadSymbolLibFileExtension
const wxChar *const traceSchLegacyPlugin
Flag to enable legacy schematic plugin debug output.
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
KICOMMON_API std::string FormatAngle(const EDA_ANGLE &aAngle)
Convert aAngle from board units to a string appropriate for writing to file.
KICOMMON_API std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
Converts aValue from internal units to a string appropriate for writing to file.
void FormatCustomProperties(OUTPUTFORMATTER *aOut, const EDA_ITEM &aItem)
Writes the item's custom properties as a series of (custom_property "key" "value")
void FormatBool(OUTPUTFORMATTER *aOut, const wxString &aKey, bool aValue)
Writes a boolean to the formatter, in the style (aKey [yes|no])
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
EDA_ANGLE getPinAngle(PIN_ORIENTATION aOrientation)
void formatArc(OUTPUTFORMATTER *aFormatter, EDA_SHAPE *aArc, bool aIsPrivate, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, bool aInvertY, const KIID &aUuid, bool aLocked)
void formatEllipseArc(OUTPUTFORMATTER *aFormatter, EDA_SHAPE *aEllipseArc, bool aIsPrivate, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, bool aInvertY, const KIID &aUuid, bool aLocked)
void formatCircle(OUTPUTFORMATTER *aFormatter, EDA_SHAPE *aCircle, bool aIsPrivate, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, bool aInvertY, const KIID &aUuid, bool aLocked)
void formatRect(OUTPUTFORMATTER *aFormatter, EDA_SHAPE *aRect, bool aIsPrivate, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, bool aInvertY, const KIID &aUuid, bool aLocked)
const char * getPinElectricalTypeToken(ELECTRICAL_PINTYPE aType)
void formatBezier(OUTPUTFORMATTER *aFormatter, EDA_SHAPE *aBezier, bool aIsPrivate, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, bool aInvertY, const KIID &aUuid, bool aLocked)
void formatEllipse(OUTPUTFORMATTER *aFormatter, EDA_SHAPE *aEllipse, bool aIsPrivate, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, bool aInvertY, const KIID &aUuid, bool aLocked)
void formatPoly(OUTPUTFORMATTER *aFormatter, EDA_SHAPE *aPolyLine, bool aIsPrivate, const STROKE_PARAMS &aStroke, FILL_T aFillMode, const COLOR4D &aFillColor, bool aInvertY, const KIID &aUuid, bool aLocked)
void formatFill(OUTPUTFORMATTER *aFormatter, FILL_T aFillMode, const COLOR4D &aFillColor)
Fill token formatting helper.
const char * getPinShapeToken(GRAPHIC_PINSHAPE aShape)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_FILENAME
@ CTX_NO_SPACE
A first-class footprint choice on a LIB_SYMBOL, tied to a named pin map.
Definition pin_map.h:159
One symbol-pin to footprint-pad mapping inside a PIN_MAP.
Definition pin_map.h:42
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ USER
The field ID hasn't been set yet; field is invalid.
@ UNTRANSLATED
wxLogTrace helper definitions.
@ SCH_FIELD_T
Definition typeinfo.h:146
@ SCH_SHAPE_T
Definition typeinfo.h:145
@ SCH_TEXT_T
Definition typeinfo.h:147
@ SCH_TEXTBOX_T
Definition typeinfo.h:148
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708