KiCad PCB EDA Suite
Loading...
Searching...
No Matches
schematic.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 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21#include <advanced_config.h>
22#include <api/api_enums.h>
23#include <algorithm>
24#include <common.h>
25#include <inspectable_impl.h>
26#include <set>
27#include <bus_alias.h>
28#include <commit.h>
29#include <connection_graph.h>
30#include <core/ignore.h>
31#include <core/kicad_algo.h>
32#include <core/profile.h>
33#include <sch_collectors.h>
34#include <erc/erc_settings.h>
35#include <font/outline_font.h>
37#include <pgm_base.h>
38#include <progress_reporter.h>
39#include <project.h>
43#include <refdes_tracker.h>
44#include <schematic.h>
46#include <sch_bus_entry.h>
47#include <sch_commit.h>
48#include <sch_junction.h>
49#include <sch_label.h>
50#include <sch_line.h>
51#include <sch_marker.h>
52#include <api/schematic/schematic_rules.pb.h>
53#include <sch_no_connect.h>
54#include <sch_rule_area.h>
55#include <sch_symbol.h>
56#include <sch_pin.h>
57#include <sch_sheet.h>
58#include <sch_screen.h>
59#include <sch_sheet_pin.h>
60#include <sch_selection_tool.h>
61#include <sim/spice_settings.h>
62#include <sim/spice_value.h>
63#include <trace_helpers.h>
64#include <string_utils.h>
66#include <tool/tool_manager.h>
67#include <undo_redo_container.h>
68#include <local_history.h>
69#include <richio.h>
70#include <sch_io/sch_io_mgr.h>
72#include <sch_io/sch_io.h>
73
74#include <wx/log.h>
75
77
79 EDA_ITEM( nullptr, SCHEMATIC_T ),
80 m_project( nullptr ),
81 m_rootSheet( nullptr ),
82 m_schematicHolder( nullptr )
83{
85 m_netChains = std::make_unique<SCH_CONNECTIVITY::NETCHAIN_MANAGER>( this );
88
89 SetProject( aPrj );
90
91 // Install the text-variable dependency adapter before any sheets load so
92 // add-notifications reach the tracker.
93 m_textVarAdapter = std::make_unique<SCHEMATIC_TEXT_VAR_ADAPTER>( *this );
95
98 [&]( INSPECTABLE* aItem, PROPERTY_BASE* aProperty, COMMIT* aCommit )
99 {
100 // Special case: propagate value, footprint, and datasheet fields to other units
101 // of a given symbol if they aren't in the selection
102
103 SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( aItem );
104
105 if( !field || !IsValid() )
106 return;
107
108 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( field->GetParent() );
109
110 if( !symbol || aProperty->Name() != _HKI( "Text" ) )
111 return;
112
113 // TODO(JE) This will need to get smarter to enable API access
114 SCH_SHEET_PATH sheetPath = CurrentSheet();
115
116 wxString newValue = aItem->Get<wxString>( aProperty );
117
118 if( field->GetId() == FIELD_T::REFERENCE )
119 {
120 symbol->SetRef( &sheetPath, newValue );
121
122 // The user might want to change all the units to the new ref. Or they
123 // might not. Since we have no way of knowing, we default to the most
124 // concrete action (change only the selected reference).
125 return;
126 }
127
128 wxString ref = symbol->GetRef( &sheetPath );
129 int unit = symbol->GetUnit();
130 LIB_ID libId = symbol->GetLibId();
131
132 for( SCH_SHEET_PATH& sheet : Hierarchy() )
133 {
134 std::vector<SCH_SYMBOL*> otherUnits;
135
136 CollectOtherUnits( ref, unit, libId, sheet, &otherUnits );
137
138 for( SCH_SYMBOL* otherUnit : otherUnits )
139 {
140 switch( field->GetId() )
141 {
142 case FIELD_T::VALUE:
145 {
146 if( aCommit )
147 aCommit->Modify( otherUnit, sheet.LastScreen() );
148
149 otherUnit->GetField( field->GetId() )->SetText( newValue );
150 break;
151 }
152
153 default: break;
154 }
155 }
156 }
157 } );
158
159 Reset();
160}
161
162
164{
166
167 delete m_currentSheet;
168 delete m_connectionGraph;
169 delete m_rootSheet;
170 m_IsSchematicExists = false;
171}
172
173
175{
176 m_importNetMap.reset();
178 delete m_rootSheet;
179
180 m_rootSheet = nullptr;
181 m_topLevelSheets.clear();
182 m_hierarchy.clear();
183
184 m_connectionGraph->Reset();
185 m_currentSheet->clear();
186
187 m_busAliases.clear();
188
192}
193
194
196{
197 if( m_project )
198 {
199 PROJECT_FILE& project = m_project->GetProjectFile();
200
201 // ERC exclusions migrations can't be resolved until the schematic is loaded.
202 // Make sure to process them here if they exist so that they get persisted by the save below.
203 if( project.m_ErcSettings && !project.m_ErcSettings->m_ErcExclusionsLegacy.empty() )
205
206 // d'tor will save settings to file
207 delete project.m_ErcSettings;
208 project.m_ErcSettings = nullptr;
209
210 // d'tor will save settings to file
211 delete project.m_SchematicSettings;
212 project.m_SchematicSettings = nullptr;
213 }
214
216 m_project = aPrj;
217
218 if( m_project )
219 {
220 PROJECT_FILE& project = m_project->GetProjectFile();
221 project.m_ErcSettings = new ERC_SETTINGS( &project, "erc" );
222 project.m_SchematicSettings = new SCHEMATIC_SETTINGS( &project, "schematic" );
223
224 project.m_SchematicSettings->LoadFromFile();
225 project.m_SchematicSettings->m_NgspiceSettings->LoadFromFile();
226 project.m_ErcSettings->LoadFromFile();
227
229 }
230}
231
232
234{
235 wxASSERT( m_project );
236
237 // Cache all existing annotations in the REFDES_TRACKER
238 std::shared_ptr<REFDES_TRACKER> refdesTracker = m_project->GetProjectFile().m_SchematicSettings->m_refDesTracker;
239
240 SCH_SHEET_LIST sheets = Hierarchy();
241 SCH_REFERENCE_LIST references;
242
243 sheets.GetSymbols( references, SYMBOL_FILTER_ALL );
244
245 for( const SCH_REFERENCE& ref : references )
246 {
247 refdesTracker->Insert( ref.GetFullRef( false ).ToStdString() );
248 }
249}
250
251
252bool SCHEMATIC::Contains( const SCH_REFERENCE& aRef ) const
253{
254 SCH_SHEET_LIST sheets = Hierarchy();
255 SCH_REFERENCE_LIST references;
256
261 sheets.GetSymbols( references, SYMBOL_FILTER_ALL );
262
263 return std::any_of( references.begin(), references.end(),
264 [&]( const SCH_REFERENCE& ref )
265 {
266 return ref.GetFullRef( true ) == aRef.GetFullRef( true );
267 } );
268}
269
270
272{
273 if( m_rootSheet && m_rootSheet->m_Uuid == niluuid )
274 {
275 if( !m_rootSheet->GetScreen() )
276 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
277
278 return;
279 }
280
281 SCH_SHEET* previousRoot = m_rootSheet;
282
283 m_rootSheet = new SCH_SHEET( this );
284 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
285 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
286
287 if( previousRoot )
288 {
289 previousRoot->SetParent( m_rootSheet );
290
291 if( m_rootSheet->GetScreen() )
292 m_rootSheet->GetScreen()->Append( previousRoot );
293
294 m_topLevelSheets.clear();
295 m_topLevelSheets.push_back( previousRoot );
296 }
297}
298
299
301{
302 // Early exit if we're already in the process of setting top-level sheets to avoid recursion
304 return;
305
307
308 if( !m_topLevelSheets.empty() )
309 return;
310
311 SCH_SHEET* rootSheet = new SCH_SHEET( this );
312 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
313
314 rootSheet->SetScreen( rootScreen );
315 rootSheet->SyncUuidToScreen();
316
317 SetTopLevelSheets( { rootSheet } );
318
319 SCH_SHEET_PATH rootSheetPath;
320 rootSheetPath.push_back( m_rootSheet );
321 rootSheetPath.push_back( rootSheet );
322 rootSheetPath.SetPageNumber( wxT( "1" ) );
323}
324
325
327{
328 if( m_topLevelSheets.empty() )
329 return;
330
331 if( m_currentSheet->empty() || !IsTopLevelSheet( m_currentSheet->at( 0 ) ) )
332 {
333 m_currentSheet->clear();
334 m_currentSheet->push_back( m_topLevelSheets[0] );
335 }
336}
337
338
339void SCHEMATIC::rebuildHierarchyState( bool aResetConnectionGraph )
340{
342
343 if( aResetConnectionGraph && m_project )
344 m_connectionGraph->Reset();
345
346 m_variantNames.clear();
347
348 if( m_rootSheet && m_rootSheet->GetScreen() )
349 {
350 SCH_SCREENS screens( m_rootSheet );
351 std::set<wxString> variantNames = screens.GetVariantNames();
352 m_variantNames.insert( variantNames.begin(), variantNames.end() );
353 }
354
355 // Also include variants from the project file that may not have any diffs yet.
356 // This ensures newly created variants with no symbol changes are preserved.
357 if( m_project )
358 {
359 for( const auto& [name, description] : Settings().m_VariantDescriptions )
360 m_variantNames.insert( name );
361 }
362}
363
364
365void SCHEMATIC::SetTopLevelSheets( const std::vector<SCH_SHEET*>& aSheets )
366{
367 wxCHECK_RET( !aSheets.empty(), wxS( "Cannot set empty top-level sheets!" ) );
368
369 // Set the recursion guard early before any calls to ensureDefaultTopLevelSheet()
370 bool wasAlreadySetting = m_settingTopLevelSheets;
372
373 std::vector<SCH_SHEET*> validSheets;
374 validSheets.reserve( aSheets.size() );
375
376 for( SCH_SHEET* sheet : aSheets )
377 {
378 // Skip null sheets and virtual roots (which have niluuid)
379 if( sheet && sheet->m_Uuid != niluuid )
380 validSheets.push_back( sheet );
381 }
382
383 if( validSheets.empty() )
384 {
385 // Guard against re-entry to prevent infinite recursion
386 if( !wasAlreadySetting )
388
389 m_settingTopLevelSheets = wasAlreadySetting;
390 return;
391 }
392
394
395 std::set<SCH_SHEET*> desiredSheets( validSheets.begin(), validSheets.end() );
396
397 if( m_rootSheet->GetScreen() )
398 {
399 for( SCH_ITEM* item : m_rootSheet->GetScreen()->Items() )
400 {
401 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
402
403 if( sheet && !desiredSheets.contains( sheet ) )
404 delete sheet;
405 }
406
407 m_rootSheet->GetScreen()->Clear( false );
408 }
409
410 m_currentSheet->clear();
411 m_topLevelSheets.clear();
412
413 for( SCH_SHEET* sheet : validSheets )
414 {
415 // Parent to the root sheet, not the root screen. SCH_SCREEN::Append() reparents to
416 // itself, so this has to follow it. A headless import reaches the schematic through
417 // the sheet, and SCHEMATIC::AdoptContent() parents the same way.
418 if( m_rootSheet->GetScreen() )
419 m_rootSheet->GetScreen()->Append( sheet );
420
421 sheet->SetParent( m_rootSheet );
422 m_topLevelSheets.push_back( sheet );
423 }
424
426 rebuildHierarchyState( true );
427
428 m_settingTopLevelSheets = wasAlreadySetting;
429}
430
431
433{
434 wxCHECK_RET( aContent.connectionGraph && aContent.connectionGraph->m_ownedNetChains,
435 wxS( "AdoptContent requires a staged graph with its own netchain manager" ) );
436
437 SCH_SHEET* target = aContent.targetSheet ? aContent.targetSheet : m_rootSheet;
438 SCH_SCREEN* outgoingScreen = nullptr;
439
440 // Replacing the screen while also replacing the top level sheets frees the outgoing sheets
441 // twice, once through the screen's R-tree and once through the sheet list
442 wxCHECK_RET( !aContent.screen || aContent.topLevelSheets.empty(),
443 wxS( "AdoptContent cannot replace both the screen and the top level sheets" ) );
444
445 // The virtual root's screen owns the top level sheets through its R-tree, so replacing it
446 // outright frees sheets that m_topLevelSheets still names
447 wxCHECK_RET( !aContent.screen || target != m_rootSheet,
448 wxS( "AdoptContent cannot replace the virtual root screen" ) );
449
450 // Replacing the top level sheets deletes the outgoing ones and everything below them, so the
451 // only coherent target is the virtual root whose container index is being replaced with it
452 wxCHECK_RET( aContent.topLevelSheets.empty() || target == m_rootSheet,
453 wxS( "AdoptContent can only replace the top level sheets through the virtual root" ) );
454
455 if( aContent.screen )
456 {
457 // A sheet and its screen are one identity to the rest of the schematic, so the
458 // incoming screen inherits the identity of the sheet it is hung on.
459 aContent.screen->m_uuid = target->m_Uuid;
460 aContent.screen->IncRefCount();
461 outgoingScreen = std::exchange( target->m_screen, aContent.screen.release() );
462 }
463 else if( SCH_SCREEN* screen = target->GetScreen() )
464 {
465 screen->m_rtree = std::move( aContent.screenItems );
466
467 if( aContent.screenLibSymbols )
468 screen->m_libSymbols.swap( aContent.screenLibSymbols->m_libSymbols );
469
470 --screen->m_modification_sync;
471
472 // The index now owns what it names, so the staged items lose their owners.
473 for( std::unique_ptr<SCH_ITEM>& item : aContent.itemOwners )
474 item.release();
475 }
476
477 std::vector<SCH_SHEET*> outgoingTopLevelSheets;
478
479 if( !aContent.topLevelSheets.empty() )
480 {
481 outgoingTopLevelSheets.swap( m_topLevelSheets );
482 m_topLevelSheets = std::move( aContent.topLevelSheets );
483
484 for( SCH_SHEET* sheet : m_topLevelSheets )
485 sheet->SetParent( m_rootSheet );
486 }
487
488 m_hierarchy.swap( aContent.hierarchy );
489
490 if( aContent.currentSheet )
491 m_currentSheet->Swap( *aContent.currentSheet );
492
493 m_labelToPageRefsMap.clear();
494
495 CONNECTION_GRAPH* outgoingGraph = std::exchange( m_connectionGraph, aContent.connectionGraph.release() );
496
497 // The outgoing graph, deleted below, still points at a replaced manager
498 std::unique_ptr<SCH_CONNECTIVITY::NETCHAIN_MANAGER> outgoingNetChains;
499
500 if( aContent.preserveNetChains )
501 m_connectionGraph->BorrowNetChains( *m_netChains );
502 else
503 outgoingNetChains = std::exchange( m_netChains, m_connectionGraph->ReleaseNetChains() );
504
505 m_connectionGraph->SetSchematic( this );
506
507
508 // The hierarchy and the current sheet named sheets that the outgoing screen and the
509 // outgoing top level sheets own, so neither could be destroyed before now.
510 for( SCH_SHEET* sheet : outgoingTopLevelSheets )
511 delete sheet;
512
513 if( outgoingScreen )
514 {
515 outgoingScreen->DecRefCount();
516
517 if( outgoingScreen->GetRefCount() == 0 )
518 delete outgoingScreen;
519 }
520
521 if( aContent.embeddedFiles )
522 {
523 *GetEmbeddedFiles() = std::move( *aContent.embeddedFiles );
524 Settings().m_SchDrawingSheetFileName.swap( aContent.drawingSheetFileName );
525 }
526
527 // Anything still reachable once the outgoing sheets and screen are gone survived the adoption
528 // while holding SCH_CONNECTIONs that name the outgoing graph. Symbol and sheet pins carry
529 // their own connection maps, so the indexed items alone are not enough, and Recalculate only
530 // re-points what it considers dirty.
531 SCH_SCREENS retained( Root() );
532
533 for( SCH_SCREEN* screen = retained.GetFirst(); screen; screen = retained.GetNext() )
534 {
535 for( SCH_ITEM* item : screen->Items() )
536 {
537 item->SetConnectionGraph( m_connectionGraph );
538
539 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
540 {
541 for( std::unique_ptr<SCH_PIN>& pin : symbol->GetRawPins() )
542 pin->SetConnectionGraph( m_connectionGraph );
543 }
544 else if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
545 {
546 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
547 pin->SetConnectionGraph( m_connectionGraph );
548 }
549 }
550 }
551
552 delete outgoingGraph;
553}
554
555
557{
558 // Virtual root's screen is just a container - return the first top-level sheet's screen
559 // which is what callers actually want
560 if( !m_topLevelSheets.empty() && m_topLevelSheets[0] )
561 return m_topLevelSheets[0]->GetScreen();
562
563 return nullptr;
564}
565
566
568{
569 wxCHECK( !m_hierarchy.empty(), m_hierarchy );
570
571 return m_hierarchy;
572}
573
574
580
581
582void SCHEMATIC::GetContextualTextVars( wxArrayString* aVars ) const
583{
584 auto add = [&]( const wxString& aVar )
585 {
586 if( !alg::contains( *aVars, aVar ) )
587 aVars->push_back( aVar );
588 };
589
590 add( wxT( "#" ) );
591 add( wxT( "##" ) );
592 add( wxT( "SHEETPATH" ) );
593 add( wxT( "SHEETNAME" ) );
594 add( wxT( "FILENAME" ) );
595 add( wxT( "FILEPATH" ) );
596 add( wxT( "PROJECTNAME" ) );
597 add( wxT( "VARIANT" ) );
598 add( wxT( "VARIANT_DESC" ) );
599
600 if( !CurrentSheet().empty() )
602
603 for( std::pair<wxString, wxString> entry : m_project->GetTextVars() )
604 add( entry.first );
605}
606
607
608bool SCHEMATIC::ResolveTextVar( const SCH_SHEET_PATH* aSheetPath, wxString* token, int aDepth ) const
609{
610 wxCHECK( aSheetPath, false );
611
612 if( token->IsSameAs( wxT( "#" ) ) )
613 {
614 *token = aSheetPath->GetPageNumber();
615 return true;
616 }
617 else if( token->IsSameAs( wxT( "##" ) ) )
618 {
619 *token = wxString::Format( "%i", Root().CountSheets() );
620 return true;
621 }
622 else if( token->IsSameAs( wxT( "SHEETPATH" ) ) )
623 {
624 *token = aSheetPath->PathHumanReadable();
625 return true;
626 }
627 else if( token->IsSameAs( wxT( "SHEETNAME" ) ) )
628 {
629 *token = aSheetPath->Last()->GetName();
630 return true;
631 }
632 else if( token->IsSameAs( wxT( "FILENAME" ) ) )
633 {
634 wxFileName fn( GetFileName() );
635 *token = fn.GetFullName();
636 return true;
637 }
638 else if( token->IsSameAs( wxT( "FILEPATH" ) ) )
639 {
640 wxFileName fn( GetFileName() );
641 *token = fn.GetFullPath();
642 return true;
643 }
644 else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) )
645 {
646 *token = m_project->GetProjectName();
647 return true;
648 }
649 else if( token->IsSameAs( wxT( "VARIANTNAME" ) ) || token->IsSameAs( wxT( "VARIANT" ) ) )
650 {
651 *token = m_currentVariant;
652 return true;
653 }
654 else if( token->IsSameAs( wxT( "VARIANT_DESC" ) ) )
655 {
657 return true;
658 }
659
660 // aSheetPath->LastScreen() can be null during schematic loading
661 if( aSheetPath->LastScreen()
662 && aSheetPath->LastScreen()->GetTitleBlock().TextVarResolver( token, m_project, INTERNAL ) )
663 {
664 return true;
665 }
666
667 if( m_project->TextVarResolver( token ) )
668 return true;
669
670 return false;
671}
672
673
675{
676 // With virtual root pattern, m_rootSheet is the virtual root with no file
677 // Return filename from first top-level sheet if available
678 if( !IsValid() )
679 return wxString( wxEmptyString );
680
681 if( !m_topLevelSheets.empty() && m_topLevelSheets[0]->GetScreen() )
682 return m_topLevelSheets[0]->GetScreen()->GetFileName();
683
684 return wxString( wxEmptyString );
685}
686
687
689{
690 if( !m_project )
691 {
692 static SCHEMATIC_SETTINGS defaultSettings( nullptr, "schematic" );
693 return defaultSettings;
694 }
695 wxASSERT( m_project );
696 return *m_project->GetProjectFile().m_SchematicSettings;
697}
698
699
701{
702 wxASSERT( m_project );
703 return *m_project->GetProjectFile().m_ErcSettings;
704}
705
706
707std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
708{
709 SCH_SHEET_LIST sheetList = Hierarchy();
710 ERC_SETTINGS& settings = ErcSettings();
711
714
715 // Child exclusions need the loaded hierarchy to recover nonpersistent item IDs.
716 for( const auto& [markerData, comment] : settings.m_ErcExclusionsLegacy )
717 {
718 ERC_EXCLUSION exclusion = ERC_EXCLUSION::FromLegacyStrings( sheetList, markerData, comment );
719
720 if( !exclusion.GetSortKey().empty() )
721 {
722 // Legacy format can sometimes have the same exclusion multiple times,
723 // without and with a comment. If this happens, replace the existing one
724 // if we can go from no comment to comment
725 auto [it, inserted] = settings.m_ErcExclusions.insert( exclusion );
726
727 if( !inserted && !comment.empty() && it->GetComment().empty() )
728 {
729 ERC_EXCLUSION updated = *it;
730 updated.SetComment( comment );
731 settings.m_ErcExclusions.erase( it );
732 settings.m_ErcExclusions.insert( updated );
733 }
734 }
735 }
736
737 settings.m_ErcExclusionsLegacy.clear();
738
739 for( const SCH_SHEET_PATH& sheet : sheetList )
740 {
741 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
742 {
743 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
744 ERC_EXCLUSION lookup = ERC_EXCLUSION::FromMarker( *marker );
745 auto it = settings.m_ErcExclusions.find( lookup );
746
747 if( it != settings.m_ErcExclusions.end() )
748 {
749 marker->SetExcluded( true, it->GetComment() );
750 settings.m_ErcExclusions.erase( it );
751 }
752 }
753 }
754
755 std::vector<SCH_MARKER*> newMarkers;
756
757 for( const ERC_EXCLUSION& exclusion : settings.m_ErcExclusions )
758 {
759 SCH_MARKER* marker = SCH_MARKER::FromProto( exclusion.ToProto().marker(), sheetList );
760
761 if( marker )
762 {
763 marker->SetExcluded( true, exclusion.GetComment() );
764 newMarkers.push_back( marker );
765 }
766 else
767 {
768 m_unresolvedErcExclusions.push_back( exclusion );
769 }
770 }
771
772 settings.m_ErcExclusions.clear();
773
774 return newMarkers;
775}
776
777
778std::shared_ptr<BUS_ALIAS> SCHEMATIC::GetBusAlias( const wxString& aLabel ) const
779{
780 for( auto it = m_busAliases.rbegin(); it != m_busAliases.rend(); ++it )
781 {
782 const auto& alias = *it;
783
784 if( alias && alias->GetName() == aLabel )
785 return alias;
786 }
787
788 return nullptr;
789}
790
791
792void SCHEMATIC::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
793{
794 if( !aAlias )
795 return;
796
797 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
798 {
799 return candidate && candidate->GetName() == aAlias->GetName() && candidate->Members() == aAlias->Members();
800 };
801
802 auto it = std::find_if( m_busAliases.begin(), m_busAliases.end(), sameDefinition );
803
804 if( it != m_busAliases.end() )
805 std::rotate( it, std::next( it ), m_busAliases.end() );
806 else
807 m_busAliases.push_back( aAlias->Clone() );
808
810}
811
812
813void SCHEMATIC::SetBusAliases( const std::vector<std::shared_ptr<BUS_ALIAS>>& aAliases )
814{
815 std::vector<std::shared_ptr<BUS_ALIAS>> aliases;
816
817 for( const std::shared_ptr<BUS_ALIAS>& alias : aAliases )
818 {
819 if( !alias )
820 continue;
821
822 std::shared_ptr<BUS_ALIAS> clone = alias->Clone();
823
824 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
825 {
826 return candidate && candidate->GetName() == clone->GetName() && candidate->Members() == clone->Members();
827 };
828
829 auto it = std::find_if( aliases.begin(), aliases.end(), sameDefinition );
830
831 if( it != aliases.end() )
832 std::rotate( it, std::next( it ), aliases.end() );
833 else
834 aliases.push_back( clone );
835 }
836
837 m_busAliases.swap( aliases );
839}
840
841
843{
844 m_busAliases.clear();
845
846 if( !m_project )
847 return;
848
849 const auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
850
851 for( const auto& alias : projectAliases )
852 {
853 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
854
855 busAlias->SetName( alias.first );
856 busAlias->SetMembers( alias.second );
857
858 m_busAliases.push_back( busAlias );
859 }
860}
861
862
864{
865 if( !m_project )
866 return;
867
868 auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
869
870 projectAliases.clear();
871
872 for( const std::shared_ptr<BUS_ALIAS>& alias : m_busAliases )
873 {
874 if( !alias )
875 continue;
876
877 projectAliases.insert_or_assign( alias->GetName(), alias->Members() );
878 }
879}
880
881
883{
884 std::set<wxString> names;
885
886 for( const auto& [key, subgraphList] : m_connectionGraph->GetNetMap() )
887 {
888 CONNECTION_SUBGRAPH* firstSubgraph = subgraphList[0];
889
890 if( !firstSubgraph->GetDriverConnection()->IsBus()
892 {
893 names.insert( key.Name );
894 }
895 }
896
897 return names;
898}
899
900
901bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
902{
903 auto* environment = TEXT_EVAL::ENVIRONMENT::Current();
904
905 if( !environment || !environment->IsCollectingSources() )
906 return resolveCrossReference( token, aDepth );
907
908 const TEXT_EVAL::ENVIRONMENT::CROSS_REFERENCE_KEY key{ *token, aDepth };
909 const bool resolved = resolveCrossReference( token, aDepth );
910 environment->RecordCrossReference( key, { *token, resolved } );
911 return resolved;
912}
913
914
915bool SCHEMATIC::resolveCrossReference( wxString* token, int aDepth ) const
916{
917 wxString remainder;
918 wxString ref = token->BeforeFirst( ':', &remainder );
919 KIID_PATH path( ref );
920 KIID uuid = path.back();
921 SCH_SHEET_PATH sheetPath;
922 SCH_ITEM* refItem = ResolveItem( KIID( uuid ), &sheetPath, true );
923
924 if( path.size() > 1 )
925 {
926 path.pop_back();
927 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
928 }
929
930 // Parse optional variant name from syntax ${REF:FIELD:VARIANT}
931 // remainder is "FIELD" or "FIELD:VARIANT"
932 wxString variantName;
933 wxString fieldName = remainder;
934 int colonPos = remainder.Find( ':' );
935
936 if( colonPos != wxNOT_FOUND )
937 {
938 fieldName = remainder.Left( colonPos );
939 variantName = remainder.Mid( colonPos + 1 );
940 }
941
942 // Note: We don't expand nested variables or evaluate math expressions here.
943 // The multi-pass loop in GetShownText handles all variable and expression resolution
944 // before cross-references are resolved. This ensures table cell variables like ${ROW}
945 // are expanded correctly.
946
947 if( refItem && refItem->Type() == SCH_SYMBOL_T )
948 {
949 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
950
951 bool resolved = refSymbol->ResolveTextVar( &sheetPath, &fieldName, variantName, aDepth + 1 );
952
953 if( resolved )
954 {
955 *token = std::move( fieldName );
956 }
957 else
958 {
959 // Field/function not found on symbol
960 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ), refSymbol->GetRef( &sheetPath, false ), fieldName );
961 }
962
963 return true;
964 }
965 else if( refItem && refItem->Type() == SCH_SHEET_T )
966 {
967 SCH_SHEET* refSheet = static_cast<SCH_SHEET*>( refItem );
968
969 sheetPath.push_back( refSheet );
970
971 wxString remainderBefore = remainder;
972
973 if( refSheet->ResolveTextVar( &sheetPath, &remainder, aDepth + 1 ) )
974 *token = std::move( remainder );
975
976 // If the remainder still contains unresolved variables or expressions,
977 // return false so ExpandTextVars keeps the ${...} wrapper
978 if( remainderBefore.Contains( wxT( "${" ) ) || remainderBefore.Contains( wxT( "@{" ) ) )
979 return false;
980
981 return true; // Cross-reference is resolved
982 }
983
984 // If UUID resolution failed, try to resolve by reference designator
985 // This handles both exact matches (J601A) and parent references for multi-unit symbols (J601)
986 if( !refItem )
987 {
990
991 SCH_SYMBOL* foundSymbol = nullptr;
992 SCH_SHEET_PATH foundPath;
993
994 for( int ii = 0; ii < (int) refs.GetCount(); ii++ )
995 {
996 SCH_REFERENCE& reference = refs[ii];
997 wxString symbolRef = reference.GetSymbol()->GetRef( &reference.GetSheetPath(), false );
998
999 // Try exact match first
1000 if( symbolRef == ref )
1001 {
1002 foundSymbol = reference.GetSymbol();
1003 foundPath = reference.GetSheetPath();
1004 break;
1005 }
1006
1007 // For multi-unit symbols, try matching parent reference (e.g., J601 matches J601A)
1008 if( symbolRef.StartsWith( ref ) && symbolRef.Length() == ref.Length() + 1 )
1009 {
1010 wxChar lastChar = symbolRef.Last();
1011 if( lastChar >= 'A' && lastChar <= 'Z' )
1012 {
1013 foundSymbol = reference.GetSymbol();
1014 foundPath = reference.GetSheetPath();
1015 // Don't break - continue looking for exact match
1016 }
1017 }
1018 }
1019
1020 if( foundSymbol )
1021 {
1022 bool resolved = foundSymbol->ResolveTextVar( &foundPath, &fieldName, variantName, aDepth + 1 );
1023
1024 if( resolved )
1025 {
1026 *token = std::move( fieldName );
1027 }
1028 else
1029 {
1030 // Field/function not found on symbol
1031 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ), foundSymbol->GetRef( &foundPath, false ),
1032 fieldName );
1033 }
1034
1035 return true;
1036 }
1037
1038 // Symbol not found - set unresolved error
1039 *token = wxString::Format( wxT( "<Unresolved: %s>" ), ref );
1040 return true;
1041 }
1042
1043 // Reference not found - show error message
1044 *token = wxString::Format( wxT( "<Unknown reference: %s>" ), ref );
1045 return true;
1046}
1047
1048
1049std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetNamesMap() const
1050{
1051 std::map<int, wxString> namesMap;
1052
1053 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1054 {
1055 if( sheet.size() == 1 )
1056 namesMap[sheet.GetVirtualPageNumber()] = _( "<root sheet>" );
1057 else
1058 namesMap[sheet.GetVirtualPageNumber()] = sheet.Last()->GetName();
1059 }
1060
1061 return namesMap;
1062}
1063
1064
1065std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetPagesMap() const
1066{
1067 std::map<int, wxString> pagesMap;
1068
1069 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1070 pagesMap[sheet.GetVirtualPageNumber()] = sheet.GetPageNumber();
1071
1072 return pagesMap;
1073}
1074
1075
1076wxString SCHEMATIC::ConvertRefsToKIIDs( const wxString& aSource ) const
1077{
1078 wxString newbuf;
1079 size_t sourceLen = aSource.length();
1080
1081 for( size_t i = 0; i < sourceLen; ++i )
1082 {
1083 // Check for escaped expressions: \${ or \@{
1084 // These should be copied verbatim without any ref→KIID conversion
1085 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
1086 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
1087 {
1088 // Copy the escape sequence and the entire escaped expression
1089 newbuf.append( aSource[i] ); // backslash
1090 newbuf.append( aSource[i + 1] ); // $ or @
1091 newbuf.append( aSource[i + 2] ); // {
1092 i += 2;
1093
1094 // Find and copy everything until the matching closing brace
1095 int braceDepth = 1;
1096 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
1097 {
1098 if( aSource[i] == '{' )
1099 braceDepth++;
1100 else if( aSource[i] == '}' )
1101 braceDepth--;
1102
1103 newbuf.append( aSource[i] );
1104 }
1105 i--; // Back up one since the for loop will increment
1106 continue;
1107 }
1108
1109 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
1110 {
1111 wxString token;
1112 bool isCrossRef = false;
1113 int nesting = 0;
1114
1115 for( i = i + 2; i < sourceLen; ++i )
1116 {
1117 if( aSource[i] == '{' && ( aSource[i - 1] == '_' || aSource[i - 1] == '^' || aSource[i - 1] == '~' ) )
1118 {
1119 nesting++;
1120 }
1121
1122 if( aSource[i] == '}' )
1123 {
1124 nesting--;
1125
1126 if( nesting < 0 )
1127 break;
1128 }
1129
1130 if( aSource[i] == ':' )
1131 isCrossRef = true;
1132
1133 token.append( aSource[i] );
1134 }
1135
1136 if( isCrossRef )
1137 {
1138 wxString remainder;
1139 wxString ref = token.BeforeFirst( ':', &remainder );
1140 SCH_REFERENCE_LIST references;
1141
1142 Hierarchy().GetSymbols( references, SYMBOL_FILTER_ALL );
1143
1144 for( size_t jj = 0; jj < references.GetCount(); jj++ )
1145 {
1146 SCH_SYMBOL* refSymbol = references[jj].GetSymbol();
1147
1148 if( ref == refSymbol->GetRef( &references[jj].GetSheetPath(), true ) )
1149 {
1150 KIID_PATH path = references[jj].GetSheetPath().Path();
1151 path.push_back( refSymbol->m_Uuid );
1152
1153 token = path.AsString() + wxS( ":" ) + remainder;
1154 break;
1155 }
1156 }
1157 }
1158
1159 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1160 }
1161 else
1162 {
1163 newbuf.append( aSource[i] );
1164 }
1165 }
1166
1167 return newbuf;
1168}
1169
1170
1171wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
1172{
1173 wxString newbuf;
1174 size_t sourceLen = aSource.length();
1175
1176 for( size_t i = 0; i < sourceLen; ++i )
1177 {
1178 // Check for escaped expressions: \${ or \@{
1179 // These should be copied verbatim without any KIID→ref conversion
1180 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
1181 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
1182 {
1183 // Copy the escape sequence and the entire escaped expression
1184 newbuf.append( aSource[i] ); // backslash
1185 newbuf.append( aSource[i + 1] ); // $ or @
1186 newbuf.append( aSource[i + 2] ); // {
1187 i += 2;
1188
1189 // Find and copy everything until the matching closing brace
1190 int braceDepth = 1;
1191 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
1192 {
1193 if( aSource[i] == '{' )
1194 braceDepth++;
1195 else if( aSource[i] == '}' )
1196 braceDepth--;
1197
1198 newbuf.append( aSource[i] );
1199 }
1200 i--; // Back up one since the for loop will increment
1201 continue;
1202 }
1203
1204 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
1205 {
1206 wxString token;
1207 bool isCrossRef = false;
1208
1209 for( i = i + 2; i < sourceLen; ++i )
1210 {
1211 if( aSource[i] == '}' )
1212 break;
1213
1214 if( aSource[i] == ':' )
1215 isCrossRef = true;
1216
1217 token.append( aSource[i] );
1218 }
1219
1220 if( isCrossRef )
1221 {
1222 wxString remainder;
1223 wxString ref = token.BeforeFirst( ':', &remainder );
1224 KIID_PATH path( ref );
1225 KIID uuid = path.back();
1226 SCH_SHEET_PATH sheetPath;
1227 SCH_ITEM* refItem = ResolveItem( uuid, &sheetPath, true );
1228
1229 if( path.size() > 1 )
1230 {
1231 path.pop_back();
1232 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
1233 }
1234
1235 if( refItem && refItem->Type() == SCH_SYMBOL_T )
1236 {
1237 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
1238 token = refSymbol->GetRef( &sheetPath, true ) + wxS( ":" ) + remainder;
1239 }
1240 }
1241
1242 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1243 }
1244 else
1245 {
1246 newbuf.append( aSource[i] );
1247 }
1248 }
1249
1250 return newbuf;
1251}
1252
1253
1260
1261
1263{
1264 // Filename is rootSheetName-sheetName-...-sheetName
1265 // Note that we need to fetch the rootSheetName out of its filename, as the root SCH_SHEET's
1266 // name is just a timestamp.
1267
1268 // Skip virtual root if present
1269 size_t startIdx = 0;
1270 if( CurrentSheet().size() > 0 && CurrentSheet().at( 0 )->IsVirtualRootSheet() )
1271 startIdx = 1;
1272
1273 // Handle the case where we only have a virtual root (shouldn't happen in practice)
1274 if( startIdx >= CurrentSheet().size() )
1275 return wxEmptyString;
1276
1277 SCH_SHEET* topSheet = CurrentSheet().at( startIdx );
1278
1279 // A top-level sheet keeps its file on the screen, because the SHEET_FILENAME field is only
1280 // set on the sheet instances that a parent sheet owns
1281 wxString topFileName;
1282
1283 if( topSheet->GetScreen() )
1284 topFileName = topSheet->GetScreen()->GetFileName();
1285
1286 if( topFileName.IsEmpty() )
1287 topFileName = topSheet->GetFileName();
1288
1289 wxFileName rootFn( topFileName );
1290 wxString filename = rootFn.GetName();
1291
1292 for( unsigned i = startIdx + 1; i < CurrentSheet().size(); i++ )
1293 filename += wxT( "-" ) + CurrentSheet().at( i )->GetName();
1294
1295 return filename;
1296}
1297
1298
1300{
1301 SCH_SCREEN* screen;
1302 SCH_SCREENS s_list( Root() );
1303
1304 // Set the sheet count, and the sheet number (1 for root sheet)
1305 int sheet_count;
1306
1307 // Handle virtual root case
1308 if( Root().m_Uuid == niluuid )
1309 {
1310 // Virtual root: count all top-level sheets
1311 sheet_count = 0;
1312
1313 for( const SCH_SHEET* topSheet : m_topLevelSheets )
1314 {
1315 if( topSheet )
1316 sheet_count += topSheet->CountSheets();
1317 }
1318 }
1319 else
1320 {
1321 // Traditional single root
1322 sheet_count = Root().CountSheets();
1323 }
1324
1325 int sheet_number = 1;
1326
1327 if( m_hierarchy.empty() )
1328 {
1329 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1330 screen->SetPageCount( sheet_count );
1331
1332 CurrentSheet().SetVirtualPageNumber( sheet_number );
1333 screen = CurrentSheet().LastScreen();
1334
1335 if( screen )
1336 screen->SetVirtualPageNumber( sheet_number );
1337
1338 return;
1339 }
1340
1341 const KIID_PATH& current_sheetpath = CurrentSheet().Path();
1342
1343 // @todo Remove all pseudo page number system is left over from prior to real page number
1344 // implementation.
1345 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1346 {
1347 if( sheet.Path() == current_sheetpath ) // Current sheet path found
1348 break;
1349
1350 sheet_number++; // Not found, increment before this current path
1351 }
1352
1353 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1354 screen->SetPageCount( sheet_count );
1355
1356 CurrentSheet().SetVirtualPageNumber( sheet_number );
1357 CurrentSheet().LastScreen()->SetVirtualPageNumber( sheet_number );
1358 CurrentSheet().LastScreen()->SetPageNumber( CurrentSheet().GetPageNumber() );
1359}
1360
1361
1363{
1364 std::map<wxString, std::set<int>>& pageRefsMap = GetPageRefsMap();
1365
1366 pageRefsMap.clear();
1367
1368 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1369 {
1370 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1371 {
1372 SCH_GLOBALLABEL* global = static_cast<SCH_GLOBALLABEL*>( item );
1373 wxString resolvedLabel = global->GetShownText( &sheet, FOR_GUI );
1374
1375 pageRefsMap[resolvedLabel].insert( sheet.GetVirtualPageNumber() );
1376 }
1377 }
1378
1379 bool show = Settings().m_IntersheetRefsShow;
1380
1381 // Refresh all visible global labels. Note that we have to collect them first as the
1382 // SCH_SCREEN::Update() call is going to invalidate the RTree iterator.
1383
1384 std::vector<SCH_GLOBALLABEL*> currentSheetGlobalLabels;
1385
1386 for( EDA_ITEM* item : CurrentSheet().LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1387 currentSheetGlobalLabels.push_back( static_cast<SCH_GLOBALLABEL*>( item ) );
1388
1389 for( SCH_GLOBALLABEL* globalLabel : currentSheetGlobalLabels )
1390 {
1391 std::vector<SCH_FIELD>& fields = globalLabel->GetFields();
1392
1393 fields[0].SetVisible( show );
1394
1395 if( show )
1396 {
1397 if( fields.size() == 1 && fields[0].GetTextPos() == globalLabel->GetPosition() )
1398 globalLabel->AutoplaceFields( CurrentSheet().LastScreen(), AUTOPLACE_AUTO );
1399
1400 CurrentSheet().LastScreen()->Update( globalLabel );
1401
1402 for( SCH_FIELD& field : globalLabel->GetFields() )
1403 field.ClearBoundingBoxCache();
1404
1405 globalLabel->ClearBoundingBoxCache();
1406
1407 if( m_schematicHolder )
1408 m_schematicHolder->IntersheetRefUpdate( globalLabel );
1409 }
1410 }
1411}
1412
1413
1414void SCHEMATIC::SyncLibSymbolPinMaps( const wxString& aSchLibSymbolName, const LIB_SYMBOL& aSource,
1415 SCH_COMMIT* aCommit )
1416{
1417 SCH_SCREENS screens( Root() );
1418
1419 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1420 {
1421 auto it = screen->GetLibSymbols().find( aSchLibSymbolName );
1422
1423 if( it != screen->GetLibSymbols().end() && it->second && it->second != &aSource )
1424 {
1425 it->second->SetPinMaps( aSource.GetPinMaps() );
1426 it->second->SetAssociatedFootprints( aSource.GetAssociatedFootprints() );
1427 }
1428
1429 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1430 {
1431 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1432
1433 if( symbol->GetSchSymbolLibraryName() != aSchLibSymbolName )
1434 continue;
1435
1436 LIB_SYMBOL* copy = symbol->GetLibSymbolRef().get();
1437
1438 if( !copy || copy == &aSource )
1439 continue;
1440
1441 if( copy->GetPinMaps() == aSource.GetPinMaps()
1442 && copy->GetAssociatedFootprints() == aSource.GetAssociatedFootprints() )
1443 {
1444 continue;
1445 }
1446
1447 if( aCommit )
1448 aCommit->Modify( symbol, screen );
1449
1450 copy->SetPinMaps( aSource.GetPinMaps() );
1451 copy->SetAssociatedFootprints( aSource.GetAssociatedFootprints() );
1452 }
1453 }
1454}
1455
1456
1457wxString SCHEMATIC::GetOperatingPoint( const wxString& aNetName, int aPrecision, const wxString& aRange )
1458{
1459 wxString spiceNetName( aNetName );
1461 spiceNetName.MakeLower();
1462
1463 if( spiceNetName == wxS( "gnd" ) || spiceNetName == wxS( "0" ) )
1464 return wxEmptyString;
1465
1466 auto it = m_operatingPoints.find( spiceNetName );
1467
1468 if( it != m_operatingPoints.end() )
1469 return SPICE_VALUE( it->second ).ToString( { aPrecision, aRange } );
1470 else if( m_operatingPoints.empty() )
1471 return wxS( "--" );
1472 else
1473 return wxS( "?" );
1474}
1475
1476
1477int SCHEMATIC::FixupJunctionsAfterImport( const std::function<void( SCH_LINE*, SCH_LINE* )>& aOnSplit )
1478{
1479 SCH_SCREENS screens( Root() );
1480 int count = 0;
1481
1482 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1483 {
1484 std::deque<EDA_ITEM*> allItems;
1485
1486 for( SCH_ITEM* item : screen->Items() )
1487 allItems.push_back( item );
1488
1489 // Add missing junctions and breakup wires as needed
1490 for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
1491 {
1492 count++;
1493
1494 SCH_JUNCTION* junction = new SCH_JUNCTION( point );
1495 screen->Append( junction );
1496
1497 // Breakup wires
1498 for( SCH_LINE* wire : screen->GetBusesAndWires( point, true ) )
1499 {
1500 SCH_LINE* newSegment = wire->NonGroupAware_BreakAt( point );
1501 screen->Append( newSegment );
1502
1503 if( aOnSplit )
1504 aOnSplit( wire, newSegment );
1505 }
1506 }
1507 }
1508
1509 return count;
1510}
1511
1512
1513void SCHEMATIC::OnItemsAdded( std::vector<SCH_ITEM*>& aNewItems )
1514{
1516}
1517
1518
1519void SCHEMATIC::OnItemsRemoved( std::vector<SCH_ITEM*>& aRemovedItems )
1520{
1522}
1523
1524
1525void SCHEMATIC::OnItemsChanged( std::vector<SCH_ITEM*>& aItems )
1526{
1528}
1529
1530
1535
1536
1541
1542
1544{
1545 if( !alg::contains( m_listeners, aListener ) )
1546 m_listeners.push_back( aListener );
1547}
1548
1549
1551{
1552 auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
1553
1554 if( i != m_listeners.end() )
1555 {
1556 std::iter_swap( i, m_listeners.end() - 1 );
1557 m_listeners.pop_back();
1558 }
1559}
1560
1561
1563{
1564 m_listeners.clear();
1565}
1566
1567
1569{
1570 std::erase_if( m_unresolvedErcExclusions,
1571 [&]( const ERC_EXCLUSION& exclusion )
1572 {
1573 if( aErrorCode >= 0
1575 exclusion.ToProto().marker().error_type() ) != aErrorCode )
1576 return false;
1577
1578 ErcSettings().m_ErcExclusions.erase( exclusion );
1579 return true;
1580 } );
1581}
1582
1583
1585{
1586 // Use a sorted sheetList to reduce file churn
1587 SCH_SHEET_LIST sheetList = Hierarchy();
1588 ERC_SETTINGS& ercSettings = ErcSettings();
1589
1590 ercSettings.m_ErcExclusions.clear();
1591 ercSettings.m_ErcExclusions.insert( m_unresolvedErcExclusions.begin(), m_unresolvedErcExclusions.end() );
1592
1593 for( unsigned i = 0; i < sheetList.size(); i++ )
1594 {
1595 for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) )
1596 {
1597 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
1598
1599 if( marker->IsExcluded() )
1600 {
1601 ercSettings.m_ErcExclusions.insert( ERC_EXCLUSION::FromMarker( *marker ) );
1602 }
1603 }
1604 }
1605}
1606
1607
1609{
1610 SCH_SHEET_LIST sheetList = Hierarchy();
1611
1612 for( SCH_MARKER* marker : ResolveERCExclusions() )
1613 {
1614 SCH_SHEET_PATH errorPath;
1615 ignore_unused( sheetList.ResolveItem( marker->GetRCItem()->GetMainItemID(), &errorPath ) );
1616
1617 if( errorPath.LastScreen() )
1618 errorPath.LastScreen()->Append( marker );
1619 else
1620 RootScreen()->Append( marker );
1621 }
1622
1623 // Once we have the ERC Exclusions, record them in the project file so that
1624 // they are retained even before the schematic is saved (PCB Editor can also save the project)
1626}
1627
1628
1630{
1631 return static_cast<EMBEDDED_FILES*>( this );
1632}
1633
1634
1636{
1637 return static_cast<const EMBEDDED_FILES*>( this );
1638}
1639
1640
1641void SCHEMATIC::RunOnNestedEmbeddedFiles( const std::function<void( EMBEDDED_FILES* )>& aFunction )
1642{
1643 SCH_SCREENS screens( Root() );
1644
1645 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1646 {
1647 for( auto& [name, libSym] : screen->GetLibSymbols() )
1648 aFunction( libSym->GetEmbeddedFiles() );
1649 }
1650}
1651
1652
1653std::set<KIFONT::OUTLINE_FONT*> SCHEMATIC::GetFonts() const
1654{
1655 std::set<KIFONT::OUTLINE_FONT*> fonts;
1656
1657 SCH_SHEET_LIST sheetList = Hierarchy();
1658
1659 for( const SCH_SHEET_PATH& sheet : sheetList )
1660 {
1661 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
1662 {
1663 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
1664 {
1665 KIFONT::FONT* font = text->GetFont();
1666
1667 if( !font || font->IsStroke() )
1668 continue;
1669
1670 using EMBEDDING_PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
1671 auto* outline = static_cast<KIFONT::OUTLINE_FONT*>( font );
1672
1673 if( outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::EDITABLE
1674 || outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::INSTALLABLE )
1675 {
1676 fonts.insert( outline );
1677 }
1678 }
1679 }
1680 }
1681
1682 return fonts;
1683}
1684
1685
1687{
1688 std::set<KIFONT::OUTLINE_FONT*> fonts = GetFonts();
1689
1690 for( KIFONT::OUTLINE_FONT* font : fonts )
1691 {
1692 auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
1693
1694 if( !file )
1695 {
1696 wxLogTrace( "EMBED", "Failed to add font file: %s", font->GetFileName() );
1697 continue;
1698 }
1699
1701 }
1702}
1703
1704
1705std::set<const SCH_SCREEN*> SCHEMATIC::GetSchematicsSharedByMultipleProjects() const
1706{
1707 std::set<const SCH_SCREEN*> retv;
1708
1709 wxCHECK( m_rootSheet, retv );
1710
1711 SCH_SHEET_LIST hierarchy( m_rootSheet );
1712 SCH_SCREENS screens( m_rootSheet );
1713
1714 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1715 {
1716 for( const SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1717 {
1718 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
1719
1720 const std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = symbol->GetInstances();
1721
1722 for( const SCH_SYMBOL_INSTANCE& instance : symbolInstances )
1723 {
1724 if( !hierarchy.HasPath( instance.m_Path ) )
1725 {
1726 retv.insert( screen );
1727 break;
1728 }
1729 }
1730
1731 if( retv.count( screen ) )
1732 break;
1733 }
1734 }
1735
1736 return retv;
1737}
1738
1739
1741{
1742 wxCHECK( m_rootSheet, false );
1743
1744 SCH_SCREENS screens( m_rootSheet );
1745
1746 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1747 {
1748 wxCHECK2( screen, continue );
1749
1750 if( screen->GetRefCount() > 1 )
1751 return true;
1752 }
1753
1754 return false;
1755}
1756
1757
1758void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
1759{
1760 SCH_SELECTION_TOOL* selectionTool = m_schematicHolder ? m_schematicHolder->GetSelectionTool() : nullptr;
1761 std::vector<SCH_LINE*> lines;
1762 std::vector<SCH_JUNCTION*> junctions;
1763 std::vector<SCH_NO_CONNECT*> ncs;
1764 std::vector<SCH_ITEM*> items_to_remove;
1765 std::unordered_set<SCH_LINE*> generatedLines;
1766 std::vector<std::unique_ptr<SCH_LINE>> retiredLines;
1767 bool changed = true;
1768
1769 if( aScreen == nullptr )
1770 aScreen = GetCurrentScreen();
1771
1772 auto remove_item = [&]( SCH_ITEM* aItem ) -> void
1773 {
1774 changed = true;
1775
1776 if( !( aItem->GetFlags() & STRUCT_DELETED ) )
1777 {
1778 aItem->SetFlags( STRUCT_DELETED );
1779
1780 if( aItem->IsSelected() && selectionTool )
1781 selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ );
1782
1783 if( m_schematicHolder )
1784 {
1785 m_schematicHolder->RemoveFromScreen( aItem, aScreen );
1786 }
1787 else
1788 {
1789 aScreen->Remove( aItem );
1790 }
1791
1792 aCommit->RemovedForCleanup( aItem, aScreen );
1793
1794 if( aItem->Type() == SCH_LINE_T && generatedLines.erase( static_cast<SCH_LINE*>( aItem ) )
1795 && !aCommit->GetStatus( aItem, aScreen ) )
1796 {
1797 // An intermediate merge has no undo owner when its Add/Remove entries cancel.
1798 retiredLines.emplace_back( static_cast<SCH_LINE*>( aItem ) );
1799 }
1800 }
1801 };
1802
1803
1804 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
1805 {
1806 if( !aScreen->IsExplicitJunctionAllowed( item->GetPosition() ) )
1807 {
1808 if( item->IsSelected() || item->HasFlag( SELECTED_BY_DRAG ) )
1809 continue;
1810
1811 items_to_remove.push_back( item );
1812 }
1813 else
1814 junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
1815 }
1816
1817 for( SCH_ITEM* item : items_to_remove )
1818 remove_item( item );
1819
1820 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
1821 ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
1822
1823 alg::for_all_pairs( junctions.begin(), junctions.end(),
1824 [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
1825 {
1826 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
1827 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
1828 {
1829 return;
1830 }
1831
1832 if( aFirst->GetPosition() == aSecond->GetPosition() )
1833 remove_item( aSecond );
1834 } );
1835
1836 alg::for_all_pairs( ncs.begin(), ncs.end(),
1837 [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
1838 {
1839 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
1840 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
1841 {
1842 return;
1843 }
1844
1845 if( aFirst->GetPosition() == aSecond->GetPosition() )
1846 remove_item( aSecond );
1847 } );
1848
1849
1850 auto minX = []( const SCH_LINE* l )
1851 {
1852 return std::min( l->GetStartPoint().x, l->GetEndPoint().x );
1853 };
1854
1855 auto maxX = []( const SCH_LINE* l )
1856 {
1857 return std::max( l->GetStartPoint().x, l->GetEndPoint().x );
1858 };
1859
1860 auto minY = []( const SCH_LINE* l )
1861 {
1862 return std::min( l->GetStartPoint().y, l->GetEndPoint().y );
1863 };
1864
1865 auto maxY = []( const SCH_LINE* l )
1866 {
1867 return std::max( l->GetStartPoint().y, l->GetEndPoint().y );
1868 };
1869
1870 // Would be nice to put lines in a canonical form here by swapping
1871 // start <-> end as needed but I don't know what swapping breaks.
1872 while( changed )
1873 {
1874 changed = false;
1875 lines.clear();
1876
1877 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
1878 {
1879 if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
1880 lines.push_back( static_cast<SCH_LINE*>( item ) );
1881 }
1882
1883 // Sort by minimum X position
1884 std::sort( lines.begin(), lines.end(),
1885 [&]( const SCH_LINE* a, const SCH_LINE* b )
1886 {
1887 return minX( a ) < minX( b );
1888 } );
1889
1890 for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
1891 {
1892 SCH_LINE* firstLine = *it1;
1893
1894 if( firstLine->GetEditFlags() & STRUCT_DELETED )
1895 continue;
1896
1897 if( firstLine->IsNull() )
1898 {
1899 remove_item( firstLine );
1900 continue;
1901 }
1902
1903 int firstRightXEdge = maxX( firstLine );
1904 auto it2 = it1;
1905
1906 for( ++it2; it2 != lines.end(); ++it2 )
1907 {
1908 SCH_LINE* secondLine = *it2;
1909 int secondLeftXEdge = minX( secondLine );
1910
1911 // impossible to overlap remaining lines
1912 if( secondLeftXEdge > firstRightXEdge )
1913 break;
1914
1915 // No Y axis overlap
1916 if( !( std::max( minY( firstLine ), minY( secondLine ) )
1917 <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) )
1918 {
1919 continue;
1920 }
1921
1922 if( secondLine->GetFlags() & STRUCT_DELETED )
1923 continue;
1924
1925 if( !secondLine->IsParallel( firstLine ) || !secondLine->IsStrokeEquivalent( firstLine )
1926 || secondLine->GetLayer() != firstLine->GetLayer() )
1927 {
1928 continue;
1929 }
1930
1931 // Remove identical lines
1932 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
1933 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
1934 {
1935 remove_item( secondLine );
1936 continue;
1937 }
1938
1939 // See if we can merge an overlap (or two colinear touching segments with
1940 // no junction where they meet).
1941 SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
1942
1943 if( mergedLine != nullptr )
1944 {
1945 remove_item( firstLine );
1946 remove_item( secondLine );
1947
1948 if( m_schematicHolder )
1949 {
1950 m_schematicHolder->AddToScreen( mergedLine, aScreen );
1951 }
1952 else
1953 {
1954 aScreen->Append( mergedLine );
1955 }
1956
1957 generatedLines.insert( mergedLine );
1958 aCommit->Added( mergedLine, aScreen );
1959
1960 if( selectionTool && ( firstLine->IsSelected() || secondLine->IsSelected() ) )
1961 selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
1962
1963 break;
1964 }
1965 }
1966 }
1967 }
1968}
1969
1970
1972 const std::set<SCH_SCREEN*>& aLocalScreens )
1973{
1975 PROF_TIMER timer;
1976
1977 if( aCleanupFlags == LOCAL_CLEANUP )
1978 {
1979 if( aLocalScreens.empty() )
1980 CleanUp( aCommit, GetCurrentScreen() );
1981 else
1982 {
1983 for( SCH_SCREEN* screen : aLocalScreens )
1984 CleanUp( aCommit, screen );
1985 }
1986 }
1987 else if( aCleanupFlags == GLOBAL_CLEANUP )
1988 {
1989 std::unordered_set<SCH_SCREEN*> cleanedScreens;
1990
1991 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1992 {
1993 SCH_SCREEN* screen = sheet.LastScreen();
1994
1995 if( cleanedScreens.insert( screen ).second )
1996 CleanUp( aCommit, screen );
1997 }
1998 }
1999
2000 timer.Stop();
2001 wxLogTrace( "CONN_PROFILE", "SchematicCleanUp() %0.4f ms", timer.msecs() );
2002
2003 if( Settings().m_IntersheetRefsShow )
2005}
2006
2007
2008void SCHEMATIC::RebuildConnectivity( std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2009 PROGRESS_REPORTER* aProgressReporter,
2010 KIGFX::SCH_VIEW* aSchView )
2011{
2013 m_project->GetProjectFile().NetSettings()->ClearAllCaches();
2014 std::unordered_set<SCH_SCREEN*> screens;
2015
2016 for( const SCH_SHEET_PATH& path : Hierarchy() )
2017 {
2018 if( SCH_SCREEN* screen = path.LastScreen() )
2019 screens.insert( screen );
2020 }
2021
2022 SCH_RULE_AREA::UpdateRuleAreasInScreens( screens, aSchView );
2023 ConnectionGraph()->Recalculate( Hierarchy(), true, aChangedItemHandler, aProgressReporter );
2024}
2025
2026
2028 TOOL_MANAGER* aToolManager, PROGRESS_REPORTER* aProgressReporter,
2029 KIGFX::SCH_VIEW* aSchView,
2030 std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2031 PICKED_ITEMS_LIST* aLastChangeList,
2032 bool aCleanupDone )
2033{
2034 SCH_COMMIT localCommit( aToolManager );
2035
2036 if( !aCommit )
2037 aCommit = &localCommit;
2038
2039 if( !aCleanupDone )
2040 CleanUpConnections( aCommit, aCleanupFlags );
2041
2042 SCH_SHEET_LIST list = Hierarchy();
2043
2044 if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP
2045 || aLastChangeList == nullptr || ConnectionGraph()->IsMinor() )
2046 {
2047 if( !localCommit.Empty() )
2048 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2049
2050 RebuildConnectivity( aChangedItemHandler, aProgressReporter, aSchView );
2051 return;
2052 }
2053 else
2054 {
2055 struct CHANGED_ITEM
2056 {
2057 SCH_ITEM* item;
2058 SCH_ITEM* linked_item;
2059 SCH_SCREEN* screen;
2060 };
2061
2062 // Final change sets
2063 std::set<SCH_ITEM*> changed_items;
2064 std::set<VECTOR2I> pts;
2065 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> item_paths;
2066
2067 // Working change sets
2068 std::unordered_set<SCH_SCREEN*> changed_screens;
2069 std::set<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> changed_rule_areas;
2070 std::vector<CHANGED_ITEM> changed_connectable_items;
2071
2072 // Lambda to add an item to the connectivity update sets
2073 auto addItemToChangeSet = [&changed_items, &pts, &item_paths]( CHANGED_ITEM itemData )
2074 {
2075 std::vector<SCH_SHEET_PATH>& paths = itemData.screen->GetClientSheetPaths();
2076
2077 std::vector<VECTOR2I> tmp_pts = itemData.item->GetConnectionPoints();
2078 pts.insert( tmp_pts.begin(), tmp_pts.end() );
2079 changed_items.insert( itemData.item );
2080
2081 for( SCH_SHEET_PATH& path : paths )
2082 item_paths.insert( std::make_pair( path, itemData.item ) );
2083
2084 if( !itemData.linked_item || !itemData.linked_item->IsConnectable() )
2085 return;
2086
2087 tmp_pts = itemData.linked_item->GetConnectionPoints();
2088 pts.insert( tmp_pts.begin(), tmp_pts.end() );
2089 changed_items.insert( itemData.linked_item );
2090
2091 // We have to directly add the pins here because the link may not exist on the schematic
2092 // anymore and so won't be picked up by GetScreen()->Items().Overlapping() below.
2093 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( itemData.linked_item ) )
2094 {
2095 std::vector<SCH_PIN*> pins = symbol->GetPins();
2096 changed_items.insert( pins.begin(), pins.end() );
2097 }
2098
2099 for( SCH_SHEET_PATH& path : paths )
2100 item_paths.insert( std::make_pair( path, itemData.linked_item ) );
2101 };
2102
2103 // Get all changed connectable items and determine all changed screens
2104 for( unsigned ii = 0; ii < aLastChangeList->GetCount(); ++ii )
2105 {
2106 switch( aLastChangeList->GetPickedItemStatus( ii ) )
2107 {
2108 // Only care about changed, new, and deleted items, the other
2109 // cases are not connectivity-related
2110 case UNDO_REDO::CHANGED:
2111 case UNDO_REDO::NEWITEM:
2112 case UNDO_REDO::DELETED: break;
2113
2114 default: continue;
2115 }
2116
2117 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItem( ii ) );
2118
2119 if( item )
2120 {
2121 SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( aLastChangeList->GetScreenForItem( ii ) );
2122 changed_screens.insert( screen );
2123
2124 if( item->Type() == SCH_RULE_AREA_T )
2125 {
2126 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( item );
2127 changed_rule_areas.insert( { ruleArea, screen } );
2128 }
2129 else if( item->IsConnectable() )
2130 {
2131 SCH_ITEM* linked_item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItemLink( ii ) );
2132 changed_connectable_items.push_back( { item, linked_item, screen } );
2133 }
2134 }
2135 }
2136
2137 // Update rule areas in changed screens to propagate any directive connectivity changes
2138 std::vector<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> forceUpdateRuleAreas =
2139 SCH_RULE_AREA::UpdateRuleAreasInScreens( changed_screens, aSchView );
2140
2141 std::for_each( forceUpdateRuleAreas.begin(), forceUpdateRuleAreas.end(),
2142 [&]( std::pair<SCH_RULE_AREA*, SCH_SCREEN*>& updatedRuleArea )
2143 {
2144 changed_rule_areas.insert( updatedRuleArea );
2145 } );
2146
2147 // If a SCH_RULE_AREA was changed, we need to add all past and present contained items to
2148 // update their connectivity
2149 std::map<KIID, EDA_ITEM*> itemMap;
2150 list.FillItemMap( itemMap );
2151
2152 auto addPastAndPresentContainedItems = [&]( SCH_RULE_AREA* changedRuleArea, SCH_SCREEN* screen )
2153 {
2154 for( const KIID& pastItem : changedRuleArea->GetPastContainedItems() )
2155 {
2156 if( itemMap.contains( pastItem ) )
2157 addItemToChangeSet( { static_cast<SCH_ITEM*>( itemMap[pastItem] ), nullptr, screen } );
2158 }
2159
2160 for( SCH_ITEM* containedItem : changedRuleArea->GetContainedItems() )
2161 addItemToChangeSet( { containedItem, nullptr, screen } );
2162 };
2163
2164 for( const auto& [changedRuleArea, screen] : changed_rule_areas )
2165 addPastAndPresentContainedItems( changedRuleArea, screen );
2166
2167 // Add all changed items, and associated items, to the change set
2168 for( CHANGED_ITEM& changed_item_data : changed_connectable_items )
2169 {
2170 addItemToChangeSet( changed_item_data );
2171
2172 // If a SCH_DIRECTIVE_LABEL was changed which is attached to a SCH_RULE_AREA, we need
2173 // to add the contained items to the change set to force update of their connectivity
2174 if( changed_item_data.item->Type() == SCH_DIRECTIVE_LABEL_T )
2175 {
2176 const std::vector<VECTOR2I> labelConnectionPoints = changed_item_data.item->GetConnectionPoints();
2177
2178 auto candidateRuleAreas = changed_item_data.screen->Items().Overlapping(
2179 SCH_RULE_AREA_T, changed_item_data.item->GetBoundingBox() );
2180
2181 for( SCH_ITEM* candidateRuleArea : candidateRuleAreas )
2182 {
2183 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( candidateRuleArea );
2184 std::vector<SHAPE*> borderShapes = ruleArea->MakeEffectiveShapes( true );
2185
2186 if( ruleArea->GetPolyShape().CollideEdge( labelConnectionPoints[0], nullptr, 5 ) )
2187 addPastAndPresentContainedItems( ruleArea, changed_item_data.screen );
2188 }
2189 }
2190 }
2191
2192 for( const VECTOR2I& pt : pts )
2193 {
2194 for( SCH_ITEM* item : GetCurrentScreen()->Items().Overlapping( pt ) )
2195 {
2196 // Leave this check in place. Overlapping items are not necessarily connectable.
2197 if( !item->IsConnectable() )
2198 continue;
2199
2200 SCH_SCREEN* screen = GetCurrentScreen();
2201 std::vector<SCH_SHEET_PATH>& paths = screen->GetClientSheetPaths();
2202
2203 if( item->Type() == SCH_LINE_T )
2204 {
2205 if( item->HitTest( pt ) )
2206 {
2207 changed_items.insert( item );
2208
2209 for( SCH_SHEET_PATH& path : paths )
2210 item_paths.insert( std::make_pair( path, item ) );
2211 }
2212 }
2213 else if( item->Type() == SCH_SYMBOL_T && item->IsConnected( pt ) )
2214 {
2215 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2216 std::vector<SCH_PIN*> pins = symbol->GetPins();
2217
2218 changed_items.insert( pins.begin(), pins.end() );
2219
2220 for( SCH_PIN* pin : pins )
2221 {
2222 for( SCH_SHEET_PATH& path : paths )
2223 item_paths.insert( std::make_pair( path, pin ) );
2224 }
2225 }
2226 else if( item->Type() == SCH_SHEET_T )
2227 {
2228 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
2229
2230 wxCHECK2( sheet, continue );
2231
2232 std::vector<SCH_SHEET_PIN*> sheetPins = sheet->GetPins();
2233 changed_items.insert( sheetPins.begin(), sheetPins.end() );
2234
2235 for( SCH_SHEET_PIN* pin : sheetPins )
2236 {
2237 for( SCH_SHEET_PATH& path : paths )
2238 item_paths.insert( std::make_pair( path, pin ) );
2239 }
2240 }
2241 else
2242 {
2243 if( item->IsConnected( pt ) )
2244 {
2245 changed_items.insert( item );
2246
2247 for( SCH_SHEET_PATH& path : paths )
2248 item_paths.insert( std::make_pair( path, item ) );
2249 }
2250 }
2251 }
2252 }
2253
2254 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> all_items =
2255 ConnectionGraph()->ExtractAffectedItems( changed_items );
2256
2257 all_items.insert( item_paths.begin(), item_paths.end() );
2258
2259 CONNECTION_GRAPH new_graph( this );
2260
2261 new_graph.SetLastCodes( ConnectionGraph() );
2262
2263 std::shared_ptr<NET_SETTINGS> netSettings = m_project->GetProjectFile().NetSettings();
2264
2265 std::set<wxString> affectedNets;
2266
2267 for( auto& [path, item] : all_items )
2268 {
2269 wxCHECK2( item, continue );
2270 item->SetConnectivityDirty();
2271 SCH_CONNECTION* conn = item->Connection();
2272
2273 if( conn )
2274 affectedNets.insert( conn->Name() );
2275 }
2276
2277 // For label items, also capture the old net name from the linked item (original state).
2278 // This ensures cache is cleared for both old and new net names when a label is renamed.
2279 for( const CHANGED_ITEM& changedItem : changed_connectable_items )
2280 {
2281 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( changedItem.item ) )
2282 {
2283 const wxString& driverName = label->GetCachedDriverName();
2284
2285 if( !driverName.IsEmpty() )
2286 affectedNets.insert( driverName );
2287 }
2288
2289 if( SCH_LABEL_BASE* linkedLabel = dynamic_cast<SCH_LABEL_BASE*>( changedItem.linked_item ) )
2290 {
2291 const wxString& driverName = linkedLabel->GetCachedDriverName();
2292
2293 if( !driverName.IsEmpty() )
2294 affectedNets.insert( driverName );
2295 }
2296 }
2297
2298 // Reset resolved netclass cache for this connection
2299 for( const wxString& netName : affectedNets )
2300 netSettings->ClearCacheForNet( netName );
2301
2302 new_graph.Recalculate( list, false, aChangedItemHandler, aProgressReporter );
2303 ConnectionGraph()->Merge( new_graph );
2304
2305 }
2306
2307 if( !localCommit.Empty() )
2308 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2309
2310}
2311
2312
2314{
2315 Reset();
2316
2318
2319 // Create the actual first top-level sheet
2320 SCH_SHEET* rootSheet = new SCH_SHEET( this );
2321 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
2322
2323 rootSheet->SetScreen( rootScreen );
2324 rootSheet->SyncUuidToScreen();
2325 rootScreen->SetFileName( "untitled.kicad_sch" ); // Set default filename to avoid conflicts
2326 rootScreen->SetPageNumber( wxT( "1" ) );
2327
2328 // Don't leave root page number empty
2329 SCH_SHEET_PATH rootSheetPath;
2330 rootSheetPath.push_back( m_rootSheet );
2331 rootSheetPath.push_back( rootSheet );
2332 rootSheetPath.SetPageNumber( wxT( "1" ) );
2333
2334 SetTopLevelSheets( { rootSheet } );
2335}
2336
2337
2338std::vector<SCH_SHEET*> SCHEMATIC::GetTopLevelSheets() const
2339{
2340 return m_topLevelSheets;
2341}
2342
2344{
2345 if( aIndex < 0 )
2346 return nullptr;
2347
2348 size_t index = static_cast<size_t>( aIndex );
2349
2350 if( index >= m_topLevelSheets.size() )
2351 return nullptr;
2352
2353 return m_topLevelSheets[index];
2354}
2355
2356
2357bool SCHEMATIC::IsTopLevelSheetUuid( const KIID& aUuid ) const
2358{
2359 if( !m_rootSheet )
2360 return false;
2361
2362 // A non-virtual root is the only sheet above the hierarchy
2363 if( m_rootSheet->m_Uuid != niluuid )
2364 return aUuid == m_rootSheet->m_Uuid;
2365
2366 for( const SCH_SHEET* sheet : m_topLevelSheets )
2367 {
2368 if( sheet && sheet->m_Uuid == aUuid )
2369 return true;
2370 }
2371
2372 return false;
2373}
2374
2375
2377{
2378 KIID_PATH path = aPath;
2379
2380 if( m_rootSheet && m_rootSheet->m_Uuid == niluuid && !path.empty() && path.front() == niluuid )
2381 path.erase( path.begin() );
2382
2383 return path;
2384}
2385
2386
2388{
2389 const KIID_PATH path = NormalizeInstancePath( aPath );
2390
2391 return !path.empty() && IsTopLevelSheetUuid( path.front() );
2392}
2393
2394
2396{
2397 wxCHECK_RET( aSheet, wxS( "Cannot add null sheet!" ) );
2398 wxCHECK_RET( aSheet->GetScreen(), wxS( "Cannot add virtual root as top-level sheet!" ) );
2399
2401
2402 // Set parent to virtual root
2403 aSheet->SetParent( m_rootSheet );
2404
2405 // Add to the virtual root's screen if it exists
2406 if( m_rootSheet->GetScreen() )
2407 {
2408 m_rootSheet->GetScreen()->Append( aSheet );
2409 }
2410
2411 // Add to our list
2412 m_topLevelSheets.push_back( aSheet );
2413
2415 rebuildHierarchyState( true );
2416}
2417
2419{
2420 auto it = std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet );
2421
2422 if( it == m_topLevelSheets.end() )
2423 return false;
2424
2425 if( m_topLevelSheets.size() == 1 )
2426 return false;
2427
2428 m_topLevelSheets.erase( it );
2429
2430 if( m_rootSheet && m_rootSheet->GetScreen() )
2431 m_rootSheet->GetScreen()->Remove( aSheet, false );
2432
2433 // If we're removing the current sheet, switch to another one
2434 if( !m_currentSheet->empty() && m_currentSheet->at( 0 ) == aSheet )
2435 {
2436 m_currentSheet->clear();
2437 if( !m_topLevelSheets.empty() )
2438 {
2439 m_currentSheet->push_back( m_topLevelSheets[0] );
2440 }
2441 }
2442
2443 rebuildHierarchyState( true );
2444 return true;
2445}
2446
2447
2448bool SCHEMATIC::IsTopLevelSheet( const SCH_SHEET* aSheet ) const
2449{
2450 return std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet ) != m_topLevelSheets.end();
2451}
2452
2453
2455{
2456 SCH_SHEET_LIST hierarchy;
2457
2458 wxLogTrace( traceSchSheetPaths, "BuildSheetListSortedByPageNumbers: %zu top-level sheets",
2459 m_topLevelSheets.size() );
2460
2461 // Can't build hierarchy without top-level sheets
2462 if( m_topLevelSheets.empty() )
2463 return hierarchy;
2464
2465 // For each top-level sheet, build its hierarchy
2466 for( SCH_SHEET* sheet : m_topLevelSheets )
2467 {
2468 if( sheet )
2469 {
2470 wxLogTrace( traceSchSheetPaths, " Top-level sheet: '%s' (UUID=%s, isVirtualRoot=%d)", sheet->GetName(),
2471 sheet->m_Uuid.AsString(), sheet->m_Uuid == niluuid ? 1 : 0 );
2472
2473 // Build the sheet list for this top-level sheet
2474 SCH_SHEET_LIST sheetList;
2475 sheetList.BuildSheetList( sheet, false );
2476
2477 // Add all sheets from this top-level sheet's hierarchy
2478 for( const SCH_SHEET_PATH& path : sheetList )
2479 {
2480 hierarchy.push_back( path );
2481 }
2482 }
2483 }
2484
2485 hierarchy.SortByPageNumbers();
2486
2487 return hierarchy;
2488}
2489
2490
2492{
2493 SCH_SHEET_LIST sheets;
2494
2495 // For each top-level sheet, build its hierarchy
2496 for( SCH_SHEET* sheet : m_topLevelSheets )
2497 {
2498 if( sheet )
2499 {
2500 SCH_SHEET_LIST sheetList;
2501 sheetList.BuildSheetList( sheet, false );
2502
2503 // Add all sheets from this top-level sheet's hierarchy
2504 for( const SCH_SHEET_PATH& path : sheetList )
2505 {
2506 sheets.push_back( path );
2507 }
2508 }
2509 }
2510
2511 return sheets;
2512}
2513
2514
2516{
2517 wxArrayString variantNames;
2518
2519 // There is no default variant name. This is just a place holder for UI controls.
2520 variantNames.Add( GetDefaultVariantName() );
2521
2522 for( const wxString& name : m_variantNames )
2523 variantNames.Add( name );
2524
2525 variantNames.Sort( SortVariantNames );
2526
2527 return variantNames;
2528}
2529
2530
2532{
2533 if( m_currentVariant.IsEmpty() || ( m_currentVariant == GetDefaultVariantName() ) )
2534 return wxEmptyString;
2535
2536 return m_currentVariant;
2537}
2538
2539
2540bool SCHEMATIC::HasVariant( const wxString& aVariantName ) const
2541{
2542 for( const wxString& name : m_variantNames )
2543 {
2544 if( name.CmpNoCase( aVariantName ) == 0 )
2545 return true;
2546 }
2547
2548 return false;
2549}
2550
2551
2552void SCHEMATIC::SetCurrentVariant( const wxString& aVariantName )
2553{
2554 wxString newVariant;
2555
2556 // Internally an empty string is the default variant. Set to default if the variant name doesn't exist.
2557 if( ( aVariantName != GetDefaultVariantName() ) && m_variantNames.contains( aVariantName ) )
2558 newVariant = aVariantName;
2559
2560 if( m_currentVariant == newVariant )
2561 return;
2562
2563 m_currentVariant = newVariant;
2564
2565 // Variant-specific field values affect text geometry, so bounding box caches computed
2566 // with the previous variant's text are now stale.
2567 if( m_rootSheet )
2568 {
2569 SCH_SCREENS allScreens( m_rootSheet );
2570
2571 for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
2572 {
2573 for( SCH_ITEM* item : screen->Items() )
2574 item->ClearCaches();
2575 }
2576 }
2577
2578 // Variant-driven cross-ref / local-field value changes require a
2579 // reactive fan-out so dependent text items repaint without waiting for
2580 // an unrelated edit to nudge the view.
2581 if( m_textVarAdapter )
2582 m_textVarAdapter->Tracker().InvalidateVariantScoped();
2583}
2584
2585
2586void SCHEMATIC::AddVariant( const wxString& aVariantName )
2587{
2588 m_variantNames.emplace( aVariantName );
2589
2590 // Ensure the variant is registered in the project file
2591 auto& descriptions = Settings().m_VariantDescriptions;
2592
2593 if( descriptions.find( aVariantName ) == descriptions.end() )
2594 descriptions[aVariantName] = wxEmptyString;
2595}
2596
2597
2598void SCHEMATIC::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit )
2599{
2600 wxCHECK( m_rootSheet, /* void */ );
2601
2602 SCH_SCREENS allScreens( m_rootSheet );
2603
2604 allScreens.DeleteVariant( aVariantName, aCommit );
2605
2606 if( m_currentVariant == aVariantName )
2607 SetCurrentVariant( wxEmptyString );
2608
2609 m_variantNames.erase( aVariantName );
2610 Settings().m_VariantDescriptions.erase( aVariantName );
2611}
2612
2613
2614void SCHEMATIC::RenameVariant( const wxString& aOldName, const wxString& aNewName,
2615 SCH_COMMIT* aCommit )
2616{
2617 wxCHECK( m_rootSheet, /* void */ );
2618 wxCHECK( !aOldName.IsEmpty() && !aNewName.IsEmpty(), /* void */ );
2619 wxCHECK( m_variantNames.contains( aOldName ), /* void */ );
2620
2621 m_variantNames.erase( aOldName );
2622 m_variantNames.insert( aNewName );
2623
2624 auto& descriptions = Settings().m_VariantDescriptions;
2625
2626 if( descriptions.count( aOldName ) )
2627 {
2628 descriptions[aNewName] = descriptions[aOldName];
2629 descriptions.erase( aOldName );
2630 }
2631
2632 // Retarget through SetCurrentVariant so the whole-schematic cache/text-var invalidation runs;
2633 // otherwise ${VARIANT} text on off-sheet items keeps its stale render cache after the rename.
2634 if( m_currentVariant == aOldName )
2635 SetCurrentVariant( aNewName );
2636
2637 SCH_SCREENS allScreens( m_rootSheet );
2638 allScreens.RenameVariant( aOldName, aNewName, aCommit );
2639}
2640
2641
2642void SCHEMATIC::CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
2643 SCH_COMMIT* aCommit )
2644{
2645 wxCHECK( m_rootSheet, /* void */ );
2646 wxCHECK( !aSourceVariant.IsEmpty() && !aNewVariant.IsEmpty(), /* void */ );
2647 wxCHECK( m_variantNames.contains( aSourceVariant ), /* void */ );
2648 wxCHECK( !m_variantNames.contains( aNewVariant ), /* void */ );
2649
2650 AddVariant( aNewVariant );
2651
2652 auto& descriptions = Settings().m_VariantDescriptions;
2653
2654 if( descriptions.count( aSourceVariant ) )
2655 descriptions[aNewVariant] = descriptions[aSourceVariant];
2656
2657 SCH_SCREENS allScreens( m_rootSheet );
2658 allScreens.CopyVariant( aSourceVariant, aNewVariant, aCommit );
2659}
2660
2661
2662wxString SCHEMATIC::GetVariantDescription( const wxString& aVariantName ) const
2663{
2664 const auto& descriptions = Settings().m_VariantDescriptions;
2665 auto it = descriptions.find( aVariantName );
2666
2667 if( it != descriptions.end() )
2668 return it->second;
2669
2670 return wxEmptyString;
2671}
2672
2673
2674void SCHEMATIC::SetVariantDescription( const wxString& aVariantName, const wxString& aDescription )
2675{
2676 auto& descriptions = Settings().m_VariantDescriptions;
2677
2678 if( aDescription.IsEmpty() )
2679 descriptions.erase( aVariantName );
2680 else
2681 descriptions[aVariantName] = aDescription;
2682}
2683
2684
2686{
2687 if( m_rootSheet && m_rootSheet->GetScreen() )
2688 {
2689 SCH_SCREENS screens( m_rootSheet );
2690 std::set<wxString> variantNames = screens.GetVariantNames();
2691 m_variantNames.insert( variantNames.begin(), variantNames.end() );
2692
2693 // Register any unknown variants to the project file with empty descriptions
2694 auto& descriptions = Settings().m_VariantDescriptions;
2695
2696 for( const wxString& name : variantNames )
2697 {
2698 if( descriptions.find( name ) == descriptions.end() )
2699 descriptions[name] = wxEmptyString;
2700 }
2701
2702 // Also include variants from the project file that may not have any diffs yet.
2703 // This ensures newly created variants with no symbol changes are preserved.
2704 for( const auto& [name, description] : descriptions )
2705 m_variantNames.insert( name );
2706 }
2707}
2708
2709
2710void SCHEMATIC::SaveToHistory( const wxString& aProjectPath, std::vector<HISTORY_FILE_DATA>& aFileData )
2711{
2712 if( !IsValid() )
2713 return;
2714
2715 wxString projPath = m_project->GetProjectPath();
2716
2717 if( projPath.IsEmpty() )
2718 return; // no project yet
2719
2720 // Verify we're saving for the correct project
2721 if( !projPath.IsSameAs( aProjectPath ) )
2722 {
2723 wxLogTrace( traceAutoSave, wxS( "[history] sch saver skipping - project path mismatch: %s vs %s" ), projPath,
2724 aProjectPath );
2725 return;
2726 }
2727
2728 if( !projPath.EndsWith( wxFILE_SEP_PATH ) )
2729 projPath += wxFILE_SEP_PATH;
2730
2731 SCH_SHEET_LIST sheetList = Hierarchy();
2732
2734
2735 KICAD_FORMAT::FORMAT_MODE mode = KICAD_FORMAT::FORMAT_MODE::NORMAL;
2736
2737 if( ADVANCED_CFG::GetCfg().m_CompactSave )
2738 mode = KICAD_FORMAT::FORMAT_MODE::COMPACT_TEXT_PROPERTIES;
2739
2740 // In ZIP mode the only caller is the autosave timer, so skipping clean sheets
2741 // avoids spurious _autosave-* files. In INCREMENTAL mode the manual-save flow
2742 // clears dirty flags before calling here, so filtering would skip the whole
2743 // snapshot. Git's diff-against-HEAD check rejects no-op commits there anyway.
2744 bool filterClean = !Pgm().GetCommonSettings()->AutosaveUsesLocalHistory();
2745
2746 for( const SCH_SHEET_PATH& path : sheetList )
2747 {
2748 SCH_SHEET* sheet = path.Last();
2749 SCH_SCREEN* screen = path.LastScreen();
2750
2751 if( !sheet || !screen )
2752 continue;
2753
2754 if( filterClean && !screen->IsContentModified() )
2755 continue;
2756
2757 wxFileName abs = m_project->AbsolutePath( screen->GetFileName() );
2758
2759 if( !abs.IsOk() )
2760 continue;
2761
2762 wxString absPath = abs.GetFullPath();
2763
2764 if( absPath.IsEmpty() || !absPath.StartsWith( projPath ) )
2765 continue;
2766
2767 wxString rel = absPath.Mid( projPath.length() );
2768
2769 try
2770 {
2771 STRING_FORMATTER formatter;
2772 pi.FormatSchematicToFormatter( &formatter, sheet, this );
2773
2774 HISTORY_FILE_DATA entry;
2775 entry.relativePath = rel;
2776 entry.content = std::move( formatter.MutableString() );
2777 entry.prettify = true;
2778 entry.formatMode = mode;
2779 aFileData.push_back( std::move( entry ) );
2780
2781 wxLogTrace( traceAutoSave,
2782 wxS( "[history] sch saver serialized %zu bytes for '%s' -> '%s'" ),
2783 aFileData.back().content.size(), absPath, rel );
2784 }
2785 catch( const IO_ERROR& ioe )
2786 {
2787 wxLogTrace( traceAutoSave, wxS( "[history] sch saver serialize failed for '%s': %s" ),
2788 absPath, wxString::FromUTF8( ioe.What() ) );
2789 }
2790 }
2791}
2792
int index
const char * name
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void SetPageCount(int aPageCount)
void SetPageNumber(const wxString &aPageNumber)
Definition base_screen.h:75
bool IsContentModified() const
Definition base_screen.h:56
void SetVirtualPageNumber(int aPageNumber)
Definition base_screen.h:72
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:68
bool Empty() const
Definition commit.h:142
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Returns status of an item.
Definition commit.cpp:232
bool AutosaveUsesLocalHistory() const
The backup format is the single switch that selects the autosave mechanism: the incremental format re...
Calculate the connectivity of a schematic and generate netlists.
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
std::set< std::pair< SCH_SHEET_PATH, SCH_ITEM * > > ExtractAffectedItems(const std::set< SCH_ITEM * > &aItems)
For a set of items, this will remove the connected items and their associated data including subgraph...
void SetLastCodes(const CONNECTION_GRAPH *aOther)
void Merge(CONNECTION_GRAPH &aGraph)
Combine the input graph contents into the current graph.
A subgraph is a set of items that are electrically connected on a single sheet.
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Return the priority (higher is more important) of a candidate driver.
const SCH_CONNECTION * GetDriverConnection() const
EDA_ITEM_FLAGS GetEditFlags() const
Definition eda_item.h:170
const KIID m_Uuid
Definition eda_item.h:597
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
bool IsSelected() const
Definition eda_item.h:134
EDA_ITEM * GetParent() const
Definition eda_item.h:112
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:84
SHAPE_POLY_SET & GetPolyShape()
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
EMBEDDED_FILES()=default
Container for an ERC exclusion, which is a SCH_MARKER plus an optional comment.
static ERC_EXCLUSION FromLegacyStrings(const SCH_SHEET_LIST &aSheetList, const wxString &aMarkerData, const wxString &aComment)
const kiapi::schematic::ErcExclusion & ToProto() const
void SetComment(const wxString &aComment)
static ERC_EXCLUSION FromMarker(const SCH_MARKER &aMarker)
wxString GetComment() const
std::string GetSortKey() const
Container for ERC settings.
std::set< std::pair< wxString, wxString > > m_ErcExclusionsLegacy
std::set< ERC_EXCLUSION, ERC_EXCLUSION_COMPARE > m_ErcExclusions
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
wxAny Get(PROPERTY_BASE *aProperty) const
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()
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
virtual bool IsStroke() const
Definition font.h:101
Class OUTLINE_FONT implements outline font drawing.
Definition kiid.h:46
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
Define a library symbol object.
Definition lib_symbol.h:119
const PIN_MAP_SET & GetPinMaps() const
Pin-to-pad mapping (issue #2282).
Definition lib_symbol.h:261
const std::vector< ASSOCIATED_FOOTPRINT > & GetAssociatedFootprints() const
Definition lib_symbol.h:265
bool IsExcluded() const
Definition marker_base.h:89
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:90
static void ConvertToSpiceMarkup(wxString *aNetName)
Convert an escaped schematic net name to SPICE, preserving literal slashes when mapping ground names.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
A holder to handle information on schematic or board items.
UNDO_REDO GetPickedItemStatus(unsigned int aIdx) const
EDA_ITEM * GetPickedItemLink(unsigned int aIdx) const
unsigned GetCount() const
BASE_SCREEN * GetScreenForItem(unsigned int aIdx) const
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
A small class to help profiling.
Definition profile.h:46
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:86
double msecs(bool aSinceLast=false)
Definition profile.h:147
A progress reporter interface for use in multi-threaded environments.
The backing store for a PROJECT, in JSON format.
Container for project specific data.
Definition project.h:63
const wxString & Name() const
Definition property.h:221
static PROPERTY_MANAGER & Instance()
class PROPERTY_LISTENER_SUBSCRIPTION RegisterListener(TYPE_ID aType, PROPERTY_LISTENER aListenerFunc)
Register a listener for the given type and return a move-only subscription that auto-unregisters in i...
virtual void OnSchItemsRemoved(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:80
virtual void OnSchItemsChanged(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:81
virtual void OnSchSelectionChanged(SCHEMATIC &aSch)
Definition schematic.h:87
virtual void OnSchSheetChanged(SCHEMATIC &aSch)
Definition schematic.h:85
virtual void OnSchItemsAdded(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:79
These are loaded from Eeschema settings but then overwritten by the project settings.
std::map< wxString, wxString > m_VariantDescriptions
A map of variant names to their descriptions.
void SetCurrentVariant(const wxString &aVariantName)
bool m_settingTopLevelSheets
Re-entry guard to prevent infinite recursion between ensureDefaultTopLevelSheet and RefreshHierarchy ...
Definition schematic.h:792
std::unique_ptr< SCH_CONNECTIVITY::NETCHAIN_MANAGER > m_netChains
Definition schematic.h:741
void Reset()
Initialize this schematic to a blank one, unloading anything existing.
std::set< const SCH_SCREEN * > GetSchematicsSharedByMultipleProjects() const
Return a list of schematic files in the current project that contain instance data for multiple proje...
void CreateDefaultScreens()
void SetLegacySymbolInstanceData()
Update the symbol value and footprint instance data for legacy designs.
void OnItemsAdded(std::vector< SCH_ITEM * > &aNewItems)
Must be used if Add() is used using a BULK_x ADD_MODE to generate a change event for listeners.
CONNECTION_GRAPH * m_connectionGraph
Hold and calculate connectivity information of this schematic.
Definition schematic.h:740
bool IsTopLevelSheet(const SCH_SHEET *aSheet) const
Check if a sheet is a top-level sheet (direct child of virtual root).
void loadBusAliasesFromProject()
void AddTopLevelSheet(SCH_SHEET *aSheet)
Add a new top-level sheet to the schematic.
SCH_SHEET_LIST m_hierarchy
Cache of the entire schematic hierarchy sorted by sheet page number.
Definition schematic.h:765
void rebuildHierarchyState(bool aResetConnectionGraph)
void ResolveERCExclusionsPostUpdate()
Update markers to match recorded exclusions.
void DeleteVariant(const wxString &aVariantName, SCH_COMMIT *aCommit=nullptr)
Delete all information for aVariantName.
void RecomputeIntersheetRefs()
Update the schematic's page reference map for all global labels, and refresh the labels so that they ...
void CacheExistingAnnotation()
Store all existing annotations in the REFDES_TRACKER.
wxString GetVariantDescription(const wxString &aVariantName) const
Return the description for a variant.
void LoadVariants()
This is a throw away method for variant testing.
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
void RemoveListener(SCHEMATIC_LISTENER *aListener)
Remove the specified listener.
bool HasVariant(const wxString &aVariantName) const
Return true if a variant with this name exists in the schematic (case-insensitively).
bool resolveCrossReference(wxString *aToken, int aDepth) const
bool IsComplexHierarchy() const
Test if the schematic is a complex hierarchy.
void OnSchSheetChanged()
Notify the schematic and its listeners that the current sheet has been changed.
wxString GetFileName() const
Helper to retrieve the filename from the root sheet screen.
SCH_SHEET_PATH * m_currentSheet
The sheet path of the sheet currently being edited or displayed.
Definition schematic.h:737
std::vector< SCH_SHEET * > m_topLevelSheets
List of top-level sheets (direct children of virtual root)
Definition schematic.h:728
wxString GetOperatingPoint(const wxString &aNetName, int aPrecision, const wxString &aRange)
void CleanUp(SCH_COMMIT *aCommit, SCH_SCREEN *aScreen=nullptr)
Perform routine schematic cleaning including breaking wire and buses and deleting identical objects s...
bool IsTopLevelSheetUuid(const KIID &aUuid) const
Check if a UUID names one of this schematic's top level sheets.
void OnItemsRemoved(std::vector< SCH_ITEM * > &aRemovedItems)
Must be used if Remove() is used using a BULK_x REMOVE_MODE to generate a change event for listeners.
void AddVariant(const wxString &aVariantName)
void OnSchSelectionChanged()
Notify the schematic and its listeners that the editor selection has changed.
virtual ~SCHEMATIC()
void AdoptContent(SCHEMATIC_CONTENT &&aContent) noexcept
Take a staged schematic over from an importer in one indivisible step.
std::shared_ptr< BUS_ALIAS > GetBusAlias(const wxString &aLabel) const
Return a pointer to a bus alias object for the given label, or null if one doesn't exist.
void CopyVariant(const wxString &aSourceVariant, const wxString &aNewVariant, SCH_COMMIT *aCommit=nullptr)
Copy a variant from aSourceVariant to aNewVariant.
std::vector< SCH_MARKER * > ResolveERCExclusions()
void SaveToHistory(const wxString &aProjectPath, std::vector< HISTORY_FILE_DATA > &aFileData)
Serialize schematic sheets into HISTORY_FILE_DATA for non-blocking history commit.
void EmbedFonts() override
Embed fonts in the schematic.
SCHEMATIC_SETTINGS & Settings() const
SCH_SCREEN * GetCurrentScreen() const
Definition schematic.h:313
wxString ConvertKIIDsToRefs(const wxString &aSource) const
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Definition schematic.h:193
void ensureVirtualRoot()
void ensureCurrentSheetIsTopLevel()
void SyncLibSymbolPinMaps(const wxString &aSchLibSymbolName, const LIB_SYMBOL &aSource, SCH_COMMIT *aCommit)
void RecordERCExclusions()
Scan existing markers and record data from any that are Excluded.
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
wxString m_currentVariant
Definition schematic.h:786
std::map< wxString, std::set< int > > & GetPageRefsMap()
Definition schematic.h:365
SCH_SHEET_LIST BuildUnorderedSheetList() const
void RenameVariant(const wxString &aOldName, const wxString &aNewName, SCH_COMMIT *aCommit=nullptr)
Rename a variant from aOldName to aNewName.
std::map< wxString, std::set< int > > m_labelToPageRefsMap
Holds a map of labels to the page sequence (virtual page number) that they appear on.
Definition schematic.h:750
std::set< KIFONT::OUTLINE_FONT * > GetFonts() const override
Get a set of fonts used in the schematic.
SCH_SHEET * GetTopLevelSheet(int aIndex=0) const
bool RemoveTopLevelSheet(SCH_SHEET *aSheet)
Remove a top-level sheet from the schematic.
std::optional< IMPORT_NET_MAP > m_importNetMap
Definition schematic.h:719
bool Contains(const SCH_REFERENCE &aRef) const
Check if the schematic contains the specified reference.
void ClearUnresolvedERCExclusions(int aErrorCode=-1)
std::vector< ERC_EXCLUSION > m_unresolvedErcExclusions
Exclusions whose saved identity cannot currently be reconstructed as a marker.
Definition schematic.h:768
wxString ConvertRefsToKIIDs(const wxString &aSource) const
void SetProject(PROJECT *aPrj)
wxString GetCurrentVariant() const
Return the current variant being edited.
void AddListener(SCHEMATIC_LISTENER *aListener)
Add a listener to the schematic to receive calls whenever something on the schematic has been modifie...
void CleanUpConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, const std::set< SCH_SCREEN * > &aLocalScreens={})
Prepare source geometry and intersheet references before rebuilding connectivity.
std::map< int, wxString > GetVirtualPageToSheetPagesMap() const
EMBEDDED_FILES * GetEmbeddedFiles() override
PROJECT * m_project
Definition schematic.h:718
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:317
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
SCHEMATIC(PROJECT *aPrj)
Definition schematic.cpp:78
bool ResolveTextVar(const SCH_SHEET_PATH *aSheetPath, wxString *token, int aDepth) const
std::set< wxString > GetNetClassAssignmentCandidates()
Return the set of netname candidates for netclass assignment.
std::vector< std::shared_ptr< BUS_ALIAS > > m_busAliases
Definition schematic.h:784
void InvokeListeners(Func &&aFunc, Args &&... args)
Definition schematic.h:707
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:288
void updateProjectBusAliases()
void SetVariantDescription(const wxString &aVariantName, const wxString &aDescription)
Set the description for a variant.
static bool m_IsSchematicExists
True if a SCHEMATIC exists, false if not.
Definition schematic.h:676
void RemoveAllListeners()
Remove all listeners.
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
Replace the top level sheets, rebuilding the hierarchy and connectivity around them.
void RebuildConnectivity(std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, KIGFX::SCH_VIEW *aSchView=nullptr)
Fully rebuild connectivity without changing source geometry.
std::unique_ptr< class SCHEMATIC_TEXT_VAR_ADAPTER > m_textVarAdapter
Reactive text-variable dependency adapter.
Definition schematic.h:796
PROPERTY_LISTENER_SUBSCRIPTION m_fieldListenerSubscription
PROPERTY_MANAGER listener subscription installed in the ctor.
Definition schematic.h:801
void GetContextualTextVars(wxArrayString *aVars) const
void ensureDefaultTopLevelSheet()
SCH_SHEET & Root() const
Definition schematic.h:199
std::map< int, wxString > GetVirtualPageToSheetNamesMap() const
void RecalculateConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, TOOL_MANAGER *aToolManager, PROGRESS_REPORTER *aProgressReporter=nullptr, KIGFX::SCH_VIEW *aSchView=nullptr, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PICKED_ITEMS_LIST *aLastChangeList=nullptr, bool aCleanupDone=false)
Generate the connection data for the entire schematic hierarchy.
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
std::vector< SCH_SHEET * > GetTopLevelSheets() const
Get the list of top-level sheets.
wxArrayString GetVariantNamesForUI() const
Return an array of variant names for using in wxWidgets UI controls.
void RunOnNestedEmbeddedFiles(const std::function< void(EMBEDDED_FILES *)> &aFunction) override
Provide access to nested embedded files, such as symbols in schematics and footprints in boards.
SCHEMATIC_HOLDER * m_schematicHolder
What currently "Holds" the schematic, i.e.
Definition schematic.h:778
wxString GetUniqueFilenameForCurrentSheet()
Get the unique file name for the current sheet.
void SetSheetNumberAndCount()
Set the m_ScreenNumber and m_NumberOfScreens members for screens.
void SetBusAliases(const std::vector< std::shared_ptr< BUS_ALIAS > > &aAliases)
std::vector< SCHEMATIC_LISTENER * > m_listeners
Currently installed listeners.
Definition schematic.h:773
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:303
bool ResolveCrossReference(wxString *token, int aDepth) const
Resolves text vars that refer to other items.
int FixupJunctionsAfterImport(const std::function< void(SCH_LINE *, SCH_LINE *)> &aOnSplit={})
Add junctions to this schematic where required.
KIID_PATH NormalizeInstancePath(const KIID_PATH &aPath) const
Strip the leading virtual root from a stored instance path, which SCH_SHEET_PATH::Path() omits but st...
std::map< wxString, double > m_operatingPoints
Simulation operating points for text variable substitution.
Definition schematic.h:760
ERC_SETTINGS & ErcSettings() const
std::set< wxString > m_variantNames
Definition schematic.h:788
void RefreshHierarchy()
void OnItemsChanged(std::vector< SCH_ITEM * > &aItems)
Notify the schematic and its listeners that an item on the schematic has been modified in some way.
SCH_SHEET * m_rootSheet
The virtual root sheet (has no screen, contains all top-level sheets)
Definition schematic.h:725
bool IsInstancePathInProject(const KIID_PATH &aPath) const
Test whether an instance path is rooted in this schematic's top level sheets.
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
void RemovedForCleanup(SCH_ITEM *aItem, SCH_SCREEN *aScreen)
Retain the pre-edit state of an item already removed from its screen by cleanup.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString Name(bool aIgnoreSheet=false) const
bool IsBus() const
FIELD_T GetId() const
Definition sch_field.h:142
A SCH_IO derivation for loading schematic files using the new s-expression file format.
void FormatSchematicToFormatter(OUTPUTFORMATTER *aOut, SCH_SHEET *aSheet, SCHEMATIC *aSchematic, const std::map< std::string, UTF8 > *aProperties=nullptr)
Serialize a schematic sheet to an OUTPUTFORMATTER without file I/O or Prettify.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:165
virtual bool IsConnectable() const
Definition sch_item.h:531
int GetUnit() const
Definition sch_item.h:237
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:345
VECTOR2I GetPosition() const override
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, int aDepth=0) const override
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
SCH_LINE * NonGroupAware_BreakAt(const VECTOR2I &aPoint)
This version should only be used when importing files.
Definition sch_line.cpp:680
bool IsParallel(const SCH_LINE *aLine) const
Definition sch_line.cpp:534
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
SCH_LINE * MergeOverlap(SCH_SCREEN *aScreen, SCH_LINE *aLine, bool aCheckJunctions)
Check line against aLine to see if it overlaps and merge if it does.
Definition sch_line.cpp:547
bool IsNull() const
Definition sch_line.h:134
bool IsStrokeEquivalent(const SCH_LINE *aLine)
Definition sch_line.h:228
bool IsEndPoint(const VECTOR2I &aPoint) const override
Test if aPt is an end point of this schematic object.
Definition sch_line.h:88
static SCH_MARKER * FromProto(const kiapi::schematic::ErcMarker &aMsg, const SCH_SHEET_LIST &aSheetList)
VECTOR2I GetPosition() const override
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
A helper to define a symbol's reference designator in a schematic.
const SCH_SHEET_PATH & GetSheetPath() const
SCH_SYMBOL * GetSymbol() const
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const override
Make a set of SHAPE objects representing the EDA_SHAPE.
const std::unordered_set< SCH_ITEM * > & GetContainedItems() const
Return a set of all items contained within the rule area.
static std::vector< std::pair< SCH_RULE_AREA *, SCH_SCREEN * > > UpdateRuleAreasInScreens(std::unordered_set< SCH_SCREEN * > &screens, KIGFX::SCH_VIEW *view)
Update all rule area connectvity / caches in the given sheet paths.
const std::unordered_set< KIID > & GetPastContainedItems() const
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:758
SCH_SCREEN * GetNext()
void CopyVariant(const wxString &aSourceVariant, const wxString &aNewVariant, SCH_COMMIT *aCommit=nullptr)
SCH_SCREEN * GetFirst()
void RenameVariant(const wxString &aOldName, const wxString &aNewName, SCH_COMMIT *aCommit=nullptr)
std::set< wxString > GetVariantNames() const
void DeleteVariant(const wxString &aVariantName, SCH_COMMIT *aCommit=nullptr)
void SetLegacySymbolInstanceData()
Update the symbol value and footprint instance data for legacy designs.
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
std::vector< SCH_SHEET_PATH > & GetClientSheetPaths()
Return the number of times this screen is used.
Definition sch_screen.h:191
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
const wxString & GetFileName() const
Definition sch_screen.h:153
void DecRefCount()
bool IsExplicitJunctionAllowed(const VECTOR2I &aPosition) const
Indicate that a junction dot may be placed at the given location.
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:164
void Update(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Update aItem's bounding box in the tree.
int GetRefCount() const
Definition sch_screen.h:171
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
std::optional< SCH_SHEET_PATH > GetSheetPathByKIIDPath(const KIID_PATH &aPath, bool aIncludeLastSheet=true) const
Finds a SCH_SHEET_PATH that matches the provided KIID_PATH.
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Fetch a SCH_ITEM by ID.
void SortByPageNumbers(bool aUpdateVirtualPageNums=true)
Sort the list of sheets by page number.
void BuildSheetList(SCH_SHEET *aSheet, bool aCheckIntegrity)
Build the list of sheets and their sheet path from aSheet.
bool HasPath(const KIID_PATH &aPath) const
void GetSymbols(SCH_REFERENCE_LIST &aReferences, SYMBOL_FILTER aSymbolFilter, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_SCREEN * LastScreen()
wxString GetPageNumber() const
SCH_SHEET * at(size_t aIndex) const
Forwarded method from std::vector.
void SetVirtualPageNumber(int aPageNumber)
Set the sheet instance virtual page number.
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false, bool aEscapeSheetNames=false) const
Return the sheet path in a human readable form made from the sheet names.
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:384
void SyncUuidToScreen()
Take the identity of the screen this sheet owns.
wxString GetName() const
Definition sch_sheet.h:142
SCH_SCREEN * m_screen
Definition sch_sheet.h:687
int CountSheets() const
Count the number of sheets found in "this" sheet including all of the subsheets.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:241
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the sheet.
Schematic symbol object.
Definition sch_symbol.h:75
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:134
wxString GetSchSymbolLibraryName() const
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:164
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:183
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX *aClosestVertex=nullptr, int aClearance=0) const
Check whether aPoint collides with any edge of any of the contours of the polygon.
Helper class to recognize Spice formatted values.
Definition spice_value.h:52
wxString ToString() const
Return string value as when converting double to string (e.g.
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:430
std::string & MutableString()
Definition richio.h:458
static ENVIRONMENT * Current()
std::pair< wxString, int > CROSS_REFERENCE_KEY
bool TextVarResolver(wxString *aToken, const PROJECT *aProject, RESOLUTION_CONTEXT aContext) const
static void GetContextualTextVars(wxArrayString *aVars)
Master controller class:
@ FOR_GUI
Definition common.h:89
@ INTERNAL
Definition common.h:92
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
#define SELECTED_BY_DRAG
Item was algorithmically selected as a dragged item.
#define STRUCT_DELETED
flag indication structures to be erased
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
const wxChar *const traceSchSheetPaths
Flag to enable debug output of schematic symbol sheet path manipulation code.
void ignore_unused(const T &)
Definition ignore.h:20
KIID niluuid(0)
@ LAYER_WIRE
Definition layer_ids.h:474
@ LAYER_BUS
Definition layer_ids.h:475
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition kicad_algo.h:80
#define _HKI(x)
Definition page_info.cpp:40
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define TYPE_HASH(x)
Definition property.h:74
void CollectOtherUnits(const wxString &aRef, int aUnit, const LIB_ID &aLibId, SCH_SHEET_PATH &aSheet, std::vector< SCH_SYMBOL * > *otherUnits)
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
#define DELETE_REMOVED_ITEMS
Definition sch_commit.h:43
@ AUTOPLACE_AUTO
Definition sch_item.h:70
@ SYMBOL_FILTER_ALL
SCH_CLEANUP_FLAGS
Definition schematic.h:91
@ LOCAL_CLEANUP
Definition schematic.h:93
@ GLOBAL_CLEANUP
Definition schematic.h:94
wxString GetDefaultVariantName()
int SortVariantNames(const wxString &aLhs, const wxString &aRhs)
Data produced by a registered saver on the UI thread, consumed by either the background local-history...
std::string content
Serialized content (mutually exclusive with sourcePath)
KICAD_FORMAT::FORMAT_MODE formatMode
wxString relativePath
Destination path relative to the project root.
A schematic staged off to the side by an importer, ready to be swapped into a live SCHEMATIC in one s...
Definition schematic.h:108
A simple container for schematic symbol instance information.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS_PIN * pin
wxLogTrace helper definitions.
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_NO_CONNECT_T
Definition typeinfo.h:156
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:167
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_MARKER_T
Definition typeinfo.h:154
@ SCH_RULE_AREA_T
Definition typeinfo.h:166
@ SCHEMATIC_T
Definition typeinfo.h:200
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_JUNCTION_T
Definition typeinfo.h:155
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683