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 along
19 * with this program. If not, see <http://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 {
67 THROW_IO_ERROR( wxString::Format( _( "Library '%s' not found." ), m_libFileName.GetFullPath() ) );
68 }
69
70 wxCHECK_RET( m_libFileName.IsAbsolute(),
71 wxString::Format( "Cannot use relative file paths in sexpr plugin to "
72 "open library '%s'.", m_libFileName.GetFullPath() ) );
73
74 if( !m_libFileName.IsDir() )
75 {
76 wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library file '%s'",
77 m_libFileName.GetFullPath() );
78
79 FILE_LINE_READER reader( m_libFileName.GetFullPath() );
80
81 SCH_IO_KICAD_SEXPR_PARSER parser( &reader );
82
83 parser.ParseLib( m_symbols );
84
88
89 // Check if there were any parse warnings (symbols that failed to parse).
90 // If so, mark the library as having parse errors and throw to notify the user.
91 // The library has loaded all valid symbols, but saving would lose the bad ones.
92 const std::vector<wxString>& warnings = parser.GetParseWarnings();
93
94 if( !warnings.empty() )
95 {
96 SetParseError( true );
97
98 wxString errorMsg = wxString::Format(
99 _( "Library '%s' loaded with errors:\n\n" ), m_libFileName.GetFullPath() );
100
101 for( const wxString& warning : warnings )
102 errorMsg += warning + wxT( "\n\n" );
103
104 errorMsg += _( "The library cannot be saved until these errors are fixed manually." );
105
106 THROW_IO_ERROR( errorMsg );
107 }
108 }
109 else
110 {
111 wxString libFileName;
112
113 wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library folder '%s'", m_libFileName.GetPath() );
114
115 // Clear source file tracking for fresh load
116 m_symbolSourceFiles.clear();
117
118 wxFileName tmp( m_libFileName.GetPath(), wxS( "dummy" ), wxString( FILEEXT::KiCadSymbolLibFileExtension ) );
119 wxDir dir( m_libFileName.GetPath() );
120 wxString fileSpec = wxS( "*." ) + wxString( FILEEXT::KiCadSymbolLibFileExtension );
121
122 if( dir.GetFirst( &libFileName, fileSpec ) )
123 {
124 wxString errorCache;
125
126 do
127 {
128 tmp.SetFullName( libFileName );
129 wxString sourceFilePath = tmp.GetFullPath();
130
131 // Track symbol pointers before parsing so we can detect which were replaced.
132 // When the parser encounters a duplicate name, it overwrites the existing
133 // symbol, so we need to update source tracking for those symbols too.
134 std::map<wxString, LIB_SYMBOL*> existingPtrs;
135
136 for( const auto& [ name, symbol ] : m_symbols )
137 existingPtrs[ name ] = symbol;
138
139 try
140 {
141 FILE_LINE_READER reader( sourceFilePath );
142 SCH_IO_KICAD_SEXPR_PARSER parser( &reader );
143
144 parser.ParseLib( m_symbols );
146
147 // Update source tracking for all symbols that came from this file.
148 // This includes both new symbols and symbols that were overwritten
149 // (when a duplicate name existed in a previously loaded file).
150 for( const auto& [ name, symbol ] : m_symbols )
151 {
152 auto it = existingPtrs.find( name );
153
154 if( it == existingPtrs.end() )
155 {
156 // New symbol from this file
157 m_symbolSourceFiles[ name ] = sourceFilePath;
158 }
159 else if( it->second != symbol )
160 {
161 // Symbol pointer changed - this file overwrote the previous version.
162 // Update tracking so we save to this file (the one whose version
163 // is actually in memory).
164 m_symbolSourceFiles[ name ] = sourceFilePath;
165 }
166 }
167
168 // Collect any parse warnings from this file
169 for( const wxString& warning : parser.GetParseWarnings() )
170 {
171 SetParseError( true );
172
173 if( !errorCache.IsEmpty() )
174 errorCache += wxT( "\n\n" );
175
176 errorCache += warning;
177 }
178 }
179 catch( const IO_ERROR& ioe )
180 {
181 // Mark that we had a parse error - saving would lose symbols
182 SetParseError( true );
183
184 if( !errorCache.IsEmpty() )
185 errorCache += wxT( "\n\n" );
186
187 errorCache += wxString::Format( _( "Unable to read file '%s'" ) + '\n', sourceFilePath );
188 errorCache += ioe.What();
189 }
190 } while( dir.GetNext( &libFileName ) );
191
192 if( !errorCache.IsEmpty() )
193 {
194 errorCache += _( "\n\nThe library cannot be saved until these errors are fixed manually." );
195 THROW_IO_ERROR( errorCache );
196 }
197 }
198
201 }
202
203 // Remember the file modification time of library file when the cache snapshot was made,
204 // so that in a networked environment we will reload the cache as needed.
206}
207
208
209void SCH_IO_KICAD_SEXPR_LIB_CACHE::Save( const std::optional<bool>& aOpt )
210{
211 if( !m_isModified )
212 return;
213
214 // If the library had a parse error during loading, we cannot safely save it.
215 // Only symbols before the parse error were loaded, so saving would permanently
216 // lose all symbols after the error point. See issue #22241.
217 if( HasParseError() )
218 {
219 THROW_IO_ERROR( wxString::Format(
220 _( "Cannot save library '%s' because it had a parse error during loading.\n\n"
221 "Saving would permanently lose symbols that could not be loaded.\n"
222 "Please fix the library file manually before saving." ),
223 m_libFileName.GetFullPath() ) );
224 }
225
226 // Write through symlinks, don't replace them.
227 wxFileName fn = GetRealFile();
228
229 // Normalize the path: if it's a directory on the filesystem, ensure fn is marked as a
230 // directory so that IsDir() checks work correctly.
231 if( !fn.IsDir() && wxFileName::DirExists( fn.GetFullPath() ) )
232 fn.AssignDir( fn.GetFullPath() );
233
234 if( !fn.IsDir() )
235 {
236 auto formatter = std::make_unique<PRETTIFIED_FILE_OUTPUTFORMATTER>( fn.GetFullPath() );
237
238 formatLibraryHeader( *formatter.get() );
239
240 std::vector<LIB_SYMBOL*> orderedSymbols;
241
242 for( const auto& [ name, symbol ] : m_symbols )
243 {
244 if( symbol )
245 orderedSymbols.push_back( symbol );
246 }
247
248 // Library must be ordered by inheritance depth.
249 std::sort( orderedSymbols.begin(), orderedSymbols.end(),
250 []( const LIB_SYMBOL* aLhs, const LIB_SYMBOL* aRhs )
251 {
252 unsigned int lhDepth = aLhs->GetInheritanceDepth();
253 unsigned int rhDepth = aRhs->GetInheritanceDepth();
254
255 if( lhDepth == rhDepth )
256 return aLhs->GetName() < aRhs->GetName();
257
258 return lhDepth < rhDepth;
259 } );
260
261 for( LIB_SYMBOL* symbol : orderedSymbols )
262 SaveSymbol( symbol, *formatter.get() );
263
264 formatter->Print( ")" );
265 formatter->Finish();
266 formatter.reset();
267 }
268 else
269 {
270 if( !fn.DirExists() )
271 {
272 if( !fn.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
273 THROW_IO_ERROR( wxString::Format( _( "Cannot create symbol library path '%s'." ), fn.GetPath() ) );
274 }
275
276 // Detect renamed symbols whose old source file entries are now orphaned.
277 // Schedule the old files for deletion so they don't linger on disk.
278 for( auto it = m_symbolSourceFiles.begin(); it != m_symbolSourceFiles.end(); )
279 {
280 if( m_symbols.find( it->first ) == m_symbols.end() )
281 {
282 m_pendingFileDeletes.insert( it->second );
283 it = m_symbolSourceFiles.erase( it );
284 }
285 else
286 {
287 ++it;
288 }
289 }
290
291 // Group symbols by their source file to preserve multi-symbol files
292 std::map<wxString, std::vector<LIB_SYMBOL*>> symbolsByFile;
293
294 for( const auto& [ name, symbol ] : m_symbols )
295 {
296 auto it = m_symbolSourceFiles.find( name );
297
298 if( it != m_symbolSourceFiles.end() )
299 {
300 // Symbol has a known source file - group it with others from that file
301 symbolsByFile[ it->second ].push_back( symbol );
302 }
303 else
304 {
305 // New symbol without source file - create individual file
306 wxFileName saveFn( fn );
307 saveFn.SetName( EscapeString( name, CTX_FILENAME ) );
309
310 symbolsByFile[ saveFn.GetFullPath() ].push_back( symbol );
311 }
312 }
313
314 // Sort each file's symbols by inheritance depth
315 auto sortByInheritance = []( LIB_SYMBOL* aLhs, LIB_SYMBOL* aRhs )
316 {
317 unsigned int lhDepth = aLhs->GetInheritanceDepth();
318 unsigned int rhDepth = aRhs->GetInheritanceDepth();
319
320 if( lhDepth == rhDepth )
321 return aLhs->GetName() < aRhs->GetName();
322
323 return lhDepth < rhDepth;
324 };
325
326 // Write each file
327 for( auto& [ filePath, symbols ] : symbolsByFile )
328 {
329 wxFileName oldFn = filePath;
330
331 if( oldFn.GetPath() != m_libFileName.GetPath() )
332 oldFn.SetPath( m_libFileName.GetPath() );
333
334 std::sort( symbols.begin(), symbols.end(), sortByInheritance );
335
336 auto formatter = std::make_unique<PRETTIFIED_FILE_OUTPUTFORMATTER>( oldFn.GetFullPath() );
337
338 formatLibraryHeader( *formatter.get() );
339
340 for( LIB_SYMBOL* symbol : symbols )
341 SaveSymbol( symbol, *formatter.get() );
342
343 formatter->Print( ")" );
344 formatter->Finish();
345 formatter.reset();
346
347 // Update source file tracking for new symbols
348 for( LIB_SYMBOL* symbol : symbols )
349 m_symbolSourceFiles[ symbol->GetName() ] = filePath;
350 }
351
352 // Remove files for deleted symbols that are no longer needed
353 for( const wxString& deadFile : m_pendingFileDeletes )
354 {
355 if( symbolsByFile.find( deadFile ) == symbolsByFile.end() && wxFileExists( deadFile ) )
356 {
357 wxRemoveFile( deadFile );
358 }
359 }
360
361 m_pendingFileDeletes.clear();
362 }
363
365 m_isModified = false;
366}
367
368
370 const wxString& aLibName, bool aIncludeData )
371{
372 wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
373
374 // If we've requested to embed the fonts in the symbol, do so.
375 // Otherwise, clear the embedded fonts from the symbol. Embedded
376 // fonts will be used if available
377 if( aSymbol->GetAreFontsEmbedded() )
378 aSymbol->EmbedFonts();
379 else
381
382 std::vector<SCH_FIELD*> orderedFields;
383 std::string name = aFormatter.Quotew( aSymbol->GetLibId().GetLibItemName().wx_str() );
384 std::string unitName = aSymbol->GetLibId().GetLibItemName();
385
386 if( !aLibName.IsEmpty() )
387 {
388 name = aFormatter.Quotew( aLibName );
389
390 LIB_ID unitId;
391
392 wxCHECK2( unitId.Parse( aLibName ) < 0, /* do nothing */ );
393
394 unitName = unitId.GetLibItemName();
395 }
396
397 if( aSymbol->IsRoot() )
398 {
399 aFormatter.Print( "(symbol %s", name.c_str() );
400
401 if( aSymbol->IsGlobalPower() )
402 aFormatter.Print( "(power global)" );
403 else if( aSymbol->IsLocalPower() )
404 aFormatter.Print( "(power local)" );
405
406 // TODO: add uuid token here.
407
408 // TODO: add anchor position token here.
409
410 if( aSymbol->IsMultiBodyStyle() )
411 {
412 aFormatter.Print( "(body_styles " );
413
414 if( aSymbol->HasDeMorganBodyStyles() )
415 {
416 aFormatter.Print( "demorgan" );
417 }
418 else
419 {
420 for( const wxString& bodyStyle : aSymbol->GetBodyStyleNames() )
421 aFormatter.Print( "%s ", aFormatter.Quotew( bodyStyle ).c_str() );
422 }
423
424 aFormatter.Print( ")" );
425 }
426
427 if( !aSymbol->GetShowPinNumbers() )
428 aFormatter.Print( "(pin_numbers (hide yes))" );
429
430 if( aSymbol->GetPinNameOffset() != schIUScale.MilsToIU( DEFAULT_PIN_NAME_OFFSET )
431 || !aSymbol->GetShowPinNames() )
432 {
433 aFormatter.Print( "(pin_names" );
434
435 if( aSymbol->GetPinNameOffset() != schIUScale.MilsToIU( DEFAULT_PIN_NAME_OFFSET ) )
436 {
437 aFormatter.Print( "(offset %s)",
439 aSymbol->GetPinNameOffset() ).c_str() );
440 }
441
442 if( !aSymbol->GetShowPinNames() )
443 KICAD_FORMAT::FormatBool( &aFormatter, "hide", true );
444
445 aFormatter.Print( ")" );
446 }
447
448 KICAD_FORMAT::FormatBool( &aFormatter, "exclude_from_sim", aSymbol->GetExcludedFromSim() );
449 KICAD_FORMAT::FormatBool( &aFormatter, "in_bom", !aSymbol->GetExcludedFromBOM() );
450 KICAD_FORMAT::FormatBool( &aFormatter, "on_board", !aSymbol->GetExcludedFromBoard() );
451 KICAD_FORMAT::FormatBool( &aFormatter, "in_pos_files", !aSymbol->GetExcludedFromPosFiles() );
452
453 KICAD_FORMAT::FormatBool( &aFormatter, "duplicate_pin_numbers_are_jumpers",
455
456 const std::vector<std::set<wxString>>& jumperGroups = aSymbol->JumperPinGroups();
457
458 if( !jumperGroups.empty() )
459 {
460 aFormatter.Print( "(jumper_pin_groups" );
461
462 for( const std::set<wxString>& group : jumperGroups )
463 {
464 aFormatter.Print( "(" );
465
466 for( const wxString& padName : group )
467 aFormatter.Print( "%s ", aFormatter.Quotew( padName ).c_str() );
468
469 aFormatter.Print( ")" );
470 }
471
472 aFormatter.Print( ")" );
473 }
474
475 // TODO: add atomic token here.
476
477 // TODO: add required token here."
478
479 aSymbol->GetFields( orderedFields );
480
481 for( SCH_FIELD* field : orderedFields )
482 saveField( field, aFormatter );
483
484 // @todo At some point in the future the lock status (all units interchangeable) should
485 // be set deterministically. For now a custom lock property is used to preserve the
486 // locked flag state.
487 if( aSymbol->UnitsLocked() )
488 {
489 SCH_FIELD locked( nullptr, FIELD_T::USER, "ki_locked" );
490 saveField( &locked, aFormatter );
491 }
492
493 saveDcmInfoAsFields( aSymbol, aFormatter );
494
495 // Save the draw items grouped by units.
496 std::vector<LIB_SYMBOL_UNIT> units = aSymbol->GetUnitDrawItems();
497 std::sort( units.begin(), units.end(),
498 []( const LIB_SYMBOL_UNIT& a, const LIB_SYMBOL_UNIT& b )
499 {
500 if( a.m_unit == b.m_unit )
501 return a.m_bodyStyle < b.m_bodyStyle;
502
503 return a.m_unit < b.m_unit;
504 } );
505
506 for( const LIB_SYMBOL_UNIT& unit : units )
507 {
508 // Add quotes and escape chars like ") to the UTF8 unitName string
509 name = aFormatter.Quotes( unitName );
510 name.pop_back(); // Remove last char: the quote ending the string.
511
512 aFormatter.Print( "(symbol %s_%d_%d\"",
513 name.c_str(),
514 unit.m_unit,
515 unit.m_bodyStyle );
516
517 // if the unit has a display name, write that
518 if( aSymbol->GetUnitDisplayNames().contains( unit.m_unit ) )
519 {
520 name = aSymbol->GetUnitDisplayNames().at( unit.m_unit );
521 aFormatter.Print( "(unit_name %s)", aFormatter.Quotes( name ).c_str() );
522 }
523
524 // Enforce item ordering
525 auto cmp =
526 []( const SCH_ITEM* a, const SCH_ITEM* b )
527 {
528 return *a < *b;
529 };
530
531 std::multiset<SCH_ITEM*, decltype( cmp )> save_map( cmp );
532
533 for( SCH_ITEM* item : unit.m_items )
534 save_map.insert( item );
535
536 for( SCH_ITEM* item : save_map )
537 saveSymbolDrawItem( item, aFormatter );
538
539 aFormatter.Print( ")" );
540 }
541
542 KICAD_FORMAT::FormatBool( &aFormatter, "embedded_fonts", aSymbol->GetAreFontsEmbedded() );
543
544 if( !aSymbol->EmbeddedFileMap().empty() )
545 aSymbol->WriteEmbeddedFiles( aFormatter, aIncludeData );
546 }
547 else
548 {
549 std::shared_ptr<LIB_SYMBOL> parent = aSymbol->GetParent().lock();
550
551 wxASSERT( parent );
552
553 aFormatter.Print( "(symbol %s (extends %s)",
554 name.c_str(),
555 aFormatter.Quotew( parent->GetName() ).c_str() );
556
557 aSymbol->GetFields( orderedFields );
558
559 for( SCH_FIELD* field : orderedFields )
560 saveField( field, aFormatter );
561
562 saveDcmInfoAsFields( aSymbol, aFormatter );
563
564 KICAD_FORMAT::FormatBool( &aFormatter, "embedded_fonts", aSymbol->GetAreFontsEmbedded() );
565
566 if( !aSymbol->EmbeddedFileMap().empty() )
567 aSymbol->WriteEmbeddedFiles( aFormatter, aIncludeData );
568 }
569
570 aFormatter.Print( ")" );
571}
572
573
575 OUTPUTFORMATTER& aFormatter )
576{
577 wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
578
579 if( !aSymbol->GetKeyWords().IsEmpty() )
580 {
581 SCH_FIELD keywords( nullptr, FIELD_T::USER, wxString( "ki_keywords" ) );
582 keywords.SetVisible( false );
583 keywords.SetText( aSymbol->GetKeyWords() );
584 saveField( &keywords, aFormatter );
585 }
586
587 wxArrayString fpFilters = aSymbol->GetFPFilters();
588
589 if( !fpFilters.IsEmpty() )
590 {
591 wxString tmp;
592
593 for( const wxString& filter : fpFilters )
594 {
595 // Spaces are not handled in fp filter names so escape spaces if any
596 wxString curr_filter = EscapeString( filter, ESCAPE_CONTEXT::CTX_NO_SPACE );
597
598 if( tmp.IsEmpty() )
599 tmp = curr_filter;
600 else
601 tmp += " " + curr_filter;
602 }
603
604 SCH_FIELD description( nullptr, FIELD_T::USER, wxString( "ki_fp_filters" ) );
605 description.SetVisible( false );
606 description.SetText( tmp );
607 saveField( &description, aFormatter );
608 }
609}
610
611
613{
614 wxCHECK_RET( aItem, "Invalid SCH_ITEM pointer." );
615
616 switch( aItem->Type() )
617 {
618 case SCH_SHAPE_T:
619 {
620 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( aItem );
621 STROKE_PARAMS stroke = shape->GetStroke();
622 FILL_T fillMode = shape->GetFillMode();
623 COLOR4D fillColor = shape->GetFillColor();
624 bool isPrivate = shape->IsPrivate();
625
626 switch( shape->GetShape() )
627 {
628 case SHAPE_T::ARC:
629 formatArc( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
630 break;
631
632 case SHAPE_T::CIRCLE:
633 formatCircle( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
634 break;
635
637 formatRect( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
638 break;
639
640 case SHAPE_T::BEZIER:
641 formatBezier(&aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
642 break;
643
644 case SHAPE_T::POLY:
645 formatPoly( &aFormatter, shape, isPrivate, stroke, fillMode, fillColor, true );
646 break;
647
648 default:
650 }
651
652 break;
653 }
654
655 case SCH_PIN_T:
656 savePin( static_cast<SCH_PIN*>( aItem ), aFormatter );
657 break;
658
659 case SCH_TEXT_T:
660 saveText( static_cast<SCH_TEXT*>( aItem ), aFormatter );
661 break;
662
663 case SCH_TEXTBOX_T:
664 saveTextBox( static_cast<SCH_TEXTBOX*>( aItem ), aFormatter );
665 break;
666
667 default:
668 UNIMPLEMENTED_FOR( aItem->GetClass() );
669 }
670}
671
672
674{
675 wxCHECK_RET( aField && aField->Type() == SCH_FIELD_T, "Invalid SCH_FIELD object." );
676
677 wxString fieldName = aField->GetName();
678
679 if( aField->IsMandatory() )
680 fieldName = GetCanonicalFieldName( aField->GetId() );
681
682 aFormatter.Print( "(property %s %s %s (at %s %s %s)",
683 aField->IsPrivate() ? "private" : "",
684 aFormatter.Quotew( fieldName ).c_str(),
685 aFormatter.Quotew( aField->GetText() ).c_str(),
687 aField->GetPosition().x ).c_str(),
689 -aField->GetPosition().y ).c_str(),
690 fmt::format( "{:g}", aField->GetTextAngle().AsDegrees() ).c_str() );
691
692 KICAD_FORMAT::FormatBool( &aFormatter, "show_name", aField->IsNameShown() );
693
694 KICAD_FORMAT::FormatBool( &aFormatter, "do_not_autoplace", !aField->CanAutoplace() );
695
696 if( !aField->IsVisible() )
697 KICAD_FORMAT::FormatBool( &aFormatter, "hide", true );
698
699 aField->Format( &aFormatter, 0 );
700 aFormatter.Print( ")" );
701}
702
703
705{
706 wxCHECK_RET( aPin && aPin->Type() == SCH_PIN_T, "Invalid SCH_PIN object." );
707
708 aPin->ClearFlags( IS_CHANGED );
709
710 aFormatter.Print( "(pin %s %s (at %s %s %s) (length %s)",
712 getPinShapeToken( aPin->GetShape() ),
714 aPin->GetPosition().x ).c_str(),
716 -aPin->GetPosition().y ).c_str(),
719 aPin->GetLength() ).c_str() );
720
721 if( !aPin->IsVisible() )
722 KICAD_FORMAT::FormatBool( &aFormatter, "hide", true );
723
724 // This follows the EDA_TEXT effects formatting for future expansion.
725 aFormatter.Print( "(name %s (effects (font (size %s %s))))",
726 aFormatter.Quotew( aPin->GetName() ).c_str(),
728 aPin->GetNameTextSize() ).c_str(),
730 aPin->GetNameTextSize() ).c_str() );
731
732 aFormatter.Print( "(number %s (effects (font (size %s %s))))",
733 aFormatter.Quotew( aPin->GetNumber() ).c_str(),
735 aPin->GetNumberTextSize() ).c_str(),
737 aPin->GetNumberTextSize() ).c_str() );
738
739
740 for( const std::pair<const wxString, SCH_PIN::ALT>& alt : aPin->GetAlternates() )
741 {
742 // There was a bug somewhere in the alternate pin code that allowed pin alternates with no
743 // name to be saved in library symbols. This strips any invalid alternates just in case
744 // that code resurfaces.
745 if( alt.second.m_Name.IsEmpty() )
746 continue;
747
748 aFormatter.Print( "(alternate %s %s %s)",
749 aFormatter.Quotew( alt.second.m_Name ).c_str(),
750 getPinElectricalTypeToken( alt.second.m_Type ),
751 getPinShapeToken( alt.second.m_Shape ) );
752 }
753
754 aFormatter.Print( ")" );
755}
756
757
759{
760 wxCHECK_RET( aText && aText->Type() == SCH_TEXT_T, "Invalid SCH_TEXT object." );
761
762 aFormatter.Print( "(text %s %s (at %s %s %d)",
763 aText->IsPrivate() ? "private" : "",
764 aFormatter.Quotew( aText->GetText() ).c_str(),
766 aText->GetPosition().x ).c_str(),
768 -aText->GetPosition().y ).c_str(),
769 aText->GetTextAngle().AsTenthsOfADegree() );
770
771 aText->EDA_TEXT::Format( &aFormatter, 0 );
772 aFormatter.Print( ")" );
773}
774
775
777{
778 wxCHECK_RET( aTextBox && aTextBox->Type() == SCH_TEXTBOX_T, "Invalid SCH_TEXTBOX object." );
779
780 aFormatter.Print( "(text_box %s %s",
781 aTextBox->IsPrivate() ? "private" : "",
782 aFormatter.Quotew( aTextBox->GetText() ).c_str() );
783
784 VECTOR2I pos = aTextBox->GetStart();
785 VECTOR2I size = aTextBox->GetEnd() - pos;
786
787 aFormatter.Print( "(at %s %s %s) (size %s %s) (margins %s %s %s %s)",
790 EDA_UNIT_UTILS::FormatAngle( aTextBox->GetTextAngle() ).c_str(),
797
798 aTextBox->GetStroke().Format( &aFormatter, schIUScale );
799 formatFill( &aFormatter, aTextBox->GetFillMode(), aTextBox->GetFillColor() );
800 aTextBox->EDA_TEXT::Format( &aFormatter, 0 );
801 aFormatter.Print( ")" );
802}
803
804
805void SCH_IO_KICAD_SEXPR_LIB_CACHE::DeleteSymbol( const wxString& aSymbolName )
806{
807 LIB_SYMBOL_MAP::iterator it = m_symbols.find( aSymbolName );
808
809 if( it == m_symbols.end() )
810 THROW_IO_ERROR( wxString::Format( _( "library %s does not contain a symbol named %s" ),
811 m_libFileName.GetFullName(), aSymbolName ) );
812
813 LIB_SYMBOL* symbol = it->second;
814
815 auto recordSourceFileForDeletion = [this]( const wxString& aName )
816 {
817 auto srcIt = m_symbolSourceFiles.find( aName );
818
819 if( srcIt != m_symbolSourceFiles.end() )
820 {
821 m_pendingFileDeletes.insert( srcIt->second );
822 m_symbolSourceFiles.erase( srcIt );
823 }
824 };
825
826 if( symbol->IsRoot() )
827 {
828 LIB_SYMBOL* rootSymbol = symbol;
829
830 recordSourceFileForDeletion( aSymbolName );
831
832 // Remove the root symbol and all its children.
833 m_symbols.erase( it );
834
835 LIB_SYMBOL_MAP::iterator it1 = m_symbols.begin();
836
837 while( it1 != m_symbols.end() )
838 {
839 if( it1->second->IsDerived()
840 && it1->second->GetParent().lock() == rootSymbol->SharedPtr() )
841 {
842 recordSourceFileForDeletion( it1->first );
843 delete it1->second;
844 it1 = m_symbols.erase( it1 );
845 }
846 else
847 {
848 it1++;
849 }
850 }
851
852 delete rootSymbol;
853 }
854 else
855 {
856 recordSourceFileForDeletion( aSymbolName );
857 // Just remove the alias.
858 m_symbols.erase( it );
859 delete symbol;
860 }
861
863 m_isModified = true;
864}
865
866
868{
869 for( auto& [name, symbol] : m_symbols )
870 {
871 if( symbol->GetParentName().IsEmpty() )
872 continue;
873
874 auto it = m_symbols.find( symbol->GetParentName() );
875
876 if( it == m_symbols.end() )
877 {
878 wxString error;
879
880 error.Printf( _( "No parent for extended symbol %s found in library '%s'" ),
881 name.c_str(), m_libFileName.GetFullPath() );
882 THROW_IO_ERROR( error );
883 }
884
885 symbol->SetParent( it->second );
886 }
887}
888
889
891{
892 aFormatter.Print( "(kicad_symbol_lib (version %d) (generator \"kicad_symbol_editor\") "
893 "(generator_version \"%s\")",
895 GetMajorMinorVersion().c_str().AsChar() );
896}
897
898
900{
901 if( !m_libFileName.IsDir() )
902 return m_libFileName.FileExists();
903 else
904 return m_libFileName.DirExists();
905}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:127
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
int AsTenthsOfADegree() const
Definition eda_angle.h:118
double AsDegrees() const
Definition eda_angle.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:151
FILL_T GetFillMode() const
Definition eda_shape.h:158
SHAPE_T GetShape() const
Definition eda_shape.h:185
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:232
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
COLOR4D GetFillColor() const
Definition eda_shape.h:169
wxString SHAPE_T_asString() const
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:172
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:114
virtual bool IsVisible() const
Definition eda_text.h:212
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:385
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, EMBEDDED_FILE * > & EmbeddedFileMap() const
bool GetAreFontsEmbedded() const
A LINE_READER that reads from an open file.
Definition richio.h:158
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()
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
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:52
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
Define a library symbol object.
Definition lib_symbol.h:83
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:152
wxString GetKeyWords() const override
Definition lib_symbol.h:179
std::weak_ptr< LIB_SYMBOL > & GetParent()
Definition lib_symbol.h:114
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.
Definition lib_symbol.h:287
bool IsRoot() const override
For symbols derived from other symbols, IsRoot() indicates no derivation.
Definition lib_symbol.h:199
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:750
bool IsMultiBodyStyle() const override
Definition lib_symbol.h:774
wxString GetName() const override
Definition lib_symbol.h:145
bool IsLocalPower() const override
wxArrayString GetFPFilters() const
Definition lib_symbol.h:211
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:92
const std::vector< wxString > & GetBodyStyleNames() const
Definition lib_symbol.h:787
bool HasDeMorganBodyStyles() const override
Definition lib_symbol.h:784
EMBEDDED_FILES * GetEmbeddedFiles() override
bool IsGlobalPower() const override
unsigned GetInheritanceDepth() const
Get the number of parents for this symbol.
bool GetDuplicatePinNumbersAreJumpers() const
Definition lib_symbol.h:753
std::vector< std::set< wxString > > & JumperPinGroups()
Each jumper pin group is a set of pin numbers that should be treated as internally connected.
Definition lib_symbol.h:760
void EmbedFonts() override
An interface used to output 8 bit text in a convenient way.
Definition richio.h:295
std::string Quotew(const wxString &aWrapee) const
Definition richio.cpp:511
int PRINTF_FUNC_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition richio.cpp:426
virtual std::string Quotes(const std::string &aWrapee) const
Check aWrapee input string for a need to be quoted (e.g.
Definition richio.cpp:472
bool IsMandatory() const
VECTOR2I GetPosition() const override
bool IsNameShown() const
Definition sch_field.h:216
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:126
FIELD_T GetId() const
Definition sch_field.h:130
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
bool CanAutoplace() const
Definition sch_field.h:227
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 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:168
bool IsPrivate() const
Definition sch_item.h:254
wxString GetClass() const override
Return the class name.
Definition sch_item.h:178
int GetNumberTextSize() const
Definition sch_pin.cpp:765
int GetLength() const
Definition sch_pin.cpp:379
const std::map< wxString, ALT > & GetAlternates() const
Definition sch_pin.h:163
bool IsVisible() const
Definition sch_pin.cpp:471
const wxString & GetName() const
Definition sch_pin.cpp:485
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:344
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:336
int GetNameTextSize() const
Definition sch_pin.cpp:739
const wxString & GetNumber() const
Definition sch_pin.h:127
GRAPHIC_PINSHAPE GetShape() const
Definition sch_pin.cpp:358
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:393
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:61
int GetMarginBottom() const
Definition sch_textbox.h:76
int GetMarginLeft() const
Definition sch_textbox.h:73
int GetMarginRight() const
Definition sch_textbox.h:75
int GetMarginTop() const
Definition sch_textbox.h:74
VECTOR2I GetPosition() const override
Definition sch_text.h:150
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:45
The common library.
#define DEFAULT_PIN_NAME_OFFSET
The intersheets references prefix string.
#define _(s)
#define IS_CHANGED
Item was edited, and modified.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:49
FILL_T
Definition eda_shape.h:59
static const std::string KiCadSymbolLibFileExtension
const wxChar *const traceSchLegacyPlugin
Flag to enable legacy schematic plugin debug output.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
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 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 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 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
@ USER
The field ID hasn't been set yet; field is invalid.
wxString GetCanonicalFieldName(FIELD_T aFieldType)
wxLogTrace helper definitions.
@ SCH_FIELD_T
Definition typeinfo.h:151
@ SCH_SHAPE_T
Definition typeinfo.h:150
@ SCH_TEXT_T
Definition typeinfo.h:152
@ SCH_TEXTBOX_T
Definition typeinfo.h:153
@ SCH_PIN_T
Definition typeinfo.h:154
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687