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 <tuple>
28#include <variant>
29#include <bus_alias.h>
30#include <commit.h>
31#include <connection_graph.h>
32#include <core/ignore.h>
33#include <core/kicad_algo.h>
34#include <core/profile.h>
35#include <sch_collectors.h>
36#include <erc/erc_settings.h>
37#include <font/outline_font.h>
39#include <pgm_base.h>
40#include <progress_reporter.h>
41#include <project.h>
45#include <refdes_tracker.h>
46#include <schematic.h>
48#include <sch_bus_entry.h>
49#include <sch_commit.h>
50#include <sch_junction.h>
51#include <sch_label.h>
52#include <sch_line.h>
53#include <sch_marker.h>
54#include <api/schematic/schematic_rules.pb.h>
55#include <sch_no_connect.h>
56#include <sch_rule_area.h>
57#include <sch_symbol.h>
58#include <sch_pin.h>
59#include <sch_sheet.h>
60#include <sch_screen.h>
61#include <sch_sheet_pin.h>
62#include <sch_selection_tool.h>
63#include <sim/spice_settings.h>
64#include <sim/spice_value.h>
65#include <trace_helpers.h>
66#include <string_utils.h>
68#include <tool/tool_manager.h>
69#include <undo_redo_container.h>
70#include <local_history.h>
71#include <richio.h>
72#include <sch_io/sch_io_mgr.h>
74#include <sch_io/sch_io.h>
75
77#include <wx/log.h>
78
80 EDA_ITEM( nullptr, SCHEMATIC_T ),
81 m_project( nullptr ),
82 m_rootSheet( nullptr ),
83 m_schematicHolder( nullptr )
84{
86 m_netChains = std::make_unique<SCH_CONNECTIVITY::NETCHAIN_MANAGER>( this );
88 m_connectivity = std::make_unique<SCH_CONNECTIVITY::FACADE>();
89
90 SetProject( aPrj );
91
92 // Install the text-variable dependency adapter before any sheets load so
93 // add-notifications reach the tracker.
94 m_textVarAdapter = std::make_unique<SCHEMATIC_TEXT_VAR_ADAPTER>( *this );
96
99 [&]( INSPECTABLE* aItem, PROPERTY_BASE* aProperty, COMMIT* aCommit )
100 {
101 // Special case: propagate value, footprint, and datasheet fields to other units
102 // of a given symbol if they aren't in the selection
103
104 SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( aItem );
105
106 if( !field || !IsValid() )
107 return;
108
109 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( field->GetParent() );
110
111 if( !symbol || aProperty->Name() != _HKI( "Text" ) )
112 return;
113
114 // TODO(JE) This will need to get smarter to enable API access
115 SCH_SHEET_PATH sheetPath = CurrentSheet();
116
117 wxString newValue = aItem->Get<wxString>( aProperty );
118
119 if( field->GetId() == FIELD_T::REFERENCE )
120 {
121 symbol->SetRef( &sheetPath, newValue );
122
123 // The user might want to change all the units to the new ref. Or they
124 // might not. Since we have no way of knowing, we default to the most
125 // concrete action (change only the selected reference).
126 return;
127 }
128
129 wxString ref = symbol->GetRef( &sheetPath );
130 int unit = symbol->GetUnit();
131 LIB_ID libId = symbol->GetLibId();
132
133 for( SCH_SHEET_PATH& sheet : Hierarchy() )
134 {
135 std::vector<SCH_SYMBOL*> otherUnits;
136
137 CollectOtherUnits( ref, unit, libId, sheet, &otherUnits );
138
139 for( SCH_SYMBOL* otherUnit : otherUnits )
140 {
141 switch( field->GetId() )
142 {
143 case FIELD_T::VALUE:
146 {
147 if( aCommit )
148 aCommit->Modify( otherUnit, sheet.LastScreen() );
149
150 otherUnit->GetField( field->GetId() )->SetText( newValue );
151 break;
152 }
153
154 default: break;
155 }
156 }
157 }
158 } );
159
160 Reset();
161}
162
163
165{
166 m_connectivity->Clear();
168
169 delete m_currentSheet;
170 delete m_connectionGraph;
171 delete m_rootSheet;
172}
173
174
176{
177 m_importNetMap.reset();
179 m_connectivity->Clear();
180
181 delete m_rootSheet;
182
183 m_rootSheet = nullptr;
184 m_topLevelSheets.clear();
185 m_hierarchy.clear();
186
187 m_connectionGraph->Reset();
188 m_currentSheet->clear();
189
190 m_busAliases.clear();
191
195}
196
197
199{
200 if( m_project )
201 {
202 PROJECT_FILE& project = m_project->GetProjectFile();
203
204 // ERC exclusions migrations can't be resolved until the schematic is loaded.
205 // Make sure to process them here if they exist so that they get persisted by the save below.
206 if( project.m_ErcSettings && !project.m_ErcSettings->m_ErcExclusionsLegacy.empty() )
208
209 // d'tor will save settings to file
210 delete project.m_ErcSettings;
211 project.m_ErcSettings = nullptr;
212
213 // d'tor will save settings to file
214 delete project.m_SchematicSettings;
215 project.m_SchematicSettings = nullptr;
216 }
217
219 m_project = aPrj;
220
221 if( m_project )
222 {
223 PROJECT_FILE& project = m_project->GetProjectFile();
224 project.m_ErcSettings = new ERC_SETTINGS( &project, "erc" );
225 project.m_SchematicSettings = new SCHEMATIC_SETTINGS( &project, "schematic" );
226
227 project.m_SchematicSettings->LoadFromFile();
228 project.m_SchematicSettings->m_NgspiceSettings->LoadFromFile();
229 project.m_ErcSettings->LoadFromFile();
230
232 }
233}
234
235
237{
238 wxASSERT( m_project );
239
240 // Cache all existing annotations in the REFDES_TRACKER
241 std::shared_ptr<REFDES_TRACKER> refdesTracker = m_project->GetProjectFile().m_SchematicSettings->m_refDesTracker;
242
243 SCH_SHEET_LIST sheets = Hierarchy();
244 SCH_REFERENCE_LIST references;
245
246 sheets.GetSymbols( references, SYMBOL_FILTER_NON_POWER );
247
248 for( const SCH_REFERENCE& ref : references )
249 {
250 refdesTracker->Insert( ref.GetCanonicalRef( false ).ToStdString() );
251 }
252}
253
254
255bool SCHEMATIC::Contains( const SCH_REFERENCE& aRef ) const
256{
257 SCH_SHEET_LIST sheets = Hierarchy();
258 SCH_REFERENCE_LIST references;
259
264 sheets.GetSymbols( references, SYMBOL_FILTER_NON_POWER );
265
266 return std::any_of( references.begin(), references.end(),
267 [&]( const SCH_REFERENCE& ref )
268 {
269 return ref.GetCanonicalRef( true ) == aRef.GetCanonicalRef( true );
270 } );
271}
272
273
275{
276 if( m_rootSheet && m_rootSheet->m_Uuid == niluuid )
277 {
278 if( !m_rootSheet->GetScreen() )
279 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
280
281 return;
282 }
283
284 SCH_SHEET* previousRoot = m_rootSheet;
285
286 m_rootSheet = new SCH_SHEET( this );
287 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
288 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
289
290 if( previousRoot )
291 {
292 previousRoot->SetParent( m_rootSheet );
293
294 if( m_rootSheet->GetScreen() )
295 m_rootSheet->GetScreen()->Append( previousRoot );
296
297 m_topLevelSheets.clear();
298 m_topLevelSheets.push_back( previousRoot );
299 }
300}
301
302
304{
305 // Early exit if we're already in the process of setting top-level sheets to avoid recursion
307 return;
308
310
311 if( !m_topLevelSheets.empty() )
312 return;
313
314 SCH_SHEET* rootSheet = new SCH_SHEET( this );
315 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
316
317 rootSheet->SetScreen( rootScreen );
318 rootSheet->SyncUuidToScreen();
319
320 SetTopLevelSheets( { rootSheet } );
321
322 SCH_SHEET_PATH rootSheetPath;
323 rootSheetPath.push_back( m_rootSheet );
324 rootSheetPath.push_back( rootSheet );
325 rootSheetPath.SetPageNumber( wxT( "1" ) );
326}
327
328
330{
331 if( m_topLevelSheets.empty() )
332 return;
333
334 if( m_currentSheet->empty() || !IsTopLevelSheet( m_currentSheet->at( 0 ) ) )
335 {
336 m_currentSheet->clear();
337 m_currentSheet->push_back( m_topLevelSheets[0] );
338 }
339}
340
341
342void SCHEMATIC::rebuildHierarchyState( bool aResetConnectionGraph )
343{
344 // Loaders and importers fill screens directly, bypassing the item listeners
345 if( m_textVarAdapter )
346 m_textVarAdapter->MarkHierarchyChanged();
347
349
350 if( aResetConnectionGraph )
351 {
352 m_connectivity->Clear();
353
354 if( m_project )
355 m_connectionGraph->Reset();
356 }
357
358 m_variantNames.clear();
359
360 if( m_rootSheet && m_rootSheet->GetScreen() )
361 {
362 SCH_SCREENS screens( m_rootSheet );
363 std::set<wxString> variantNames = screens.GetVariantNames();
364 m_variantNames.insert( variantNames.begin(), variantNames.end() );
365 }
366
367 // Also include variants from the project file that may not have any diffs yet.
368 // This ensures newly created variants with no symbol changes are preserved.
369 if( m_project )
370 {
371 for( const auto& [name, description] : Settings().m_VariantDescriptions )
372 m_variantNames.insert( name );
373 }
374}
375
376
377void SCHEMATIC::SetTopLevelSheets( const std::vector<SCH_SHEET*>& aSheets )
378{
379 wxCHECK_RET( !aSheets.empty(), wxS( "Cannot set empty top-level sheets!" ) );
380
381 // Set the recursion guard early before any calls to ensureDefaultTopLevelSheet()
382 bool wasAlreadySetting = m_settingTopLevelSheets;
384
385 std::vector<SCH_SHEET*> validSheets;
386 validSheets.reserve( aSheets.size() );
387
388 for( SCH_SHEET* sheet : aSheets )
389 {
390 // Skip null sheets and virtual roots (which have niluuid)
391 if( sheet && sheet->m_Uuid != niluuid )
392 validSheets.push_back( sheet );
393 }
394
395 if( validSheets.empty() )
396 {
397 // Guard against re-entry to prevent infinite recursion
398 if( !wasAlreadySetting )
400
401 m_settingTopLevelSheets = wasAlreadySetting;
402 return;
403 }
404
406
407 std::set<SCH_SHEET*> desiredSheets( validSheets.begin(), validSheets.end() );
408
409 if( m_rootSheet->GetScreen() )
410 {
411 for( SCH_ITEM* item : m_rootSheet->GetScreen()->Items() )
412 {
413 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
414
415 if( sheet && !desiredSheets.contains( sheet ) )
416 delete sheet;
417 }
418
419 m_rootSheet->GetScreen()->Clear( false );
420 }
421
422 m_currentSheet->clear();
423 m_topLevelSheets.clear();
424
425 for( SCH_SHEET* sheet : validSheets )
426 {
427 // Parent to the root sheet, not the root screen. SCH_SCREEN::Append() reparents to
428 // itself, so this has to follow it. A headless import reaches the schematic through
429 // the sheet, and SCHEMATIC::AdoptContent() parents the same way.
430 if( m_rootSheet->GetScreen() )
431 m_rootSheet->GetScreen()->Append( sheet );
432
433 sheet->SetParent( m_rootSheet );
434 m_topLevelSheets.push_back( sheet );
435 }
436
438 rebuildHierarchyState( true );
439
440 m_settingTopLevelSheets = wasAlreadySetting;
441}
442
443
445{
446 wxCHECK_RET( aContent.connectionGraph && aContent.connectionGraph->m_ownedNetChains,
447 wxS( "AdoptContent requires a staged graph with its own netchain manager" ) );
448
449 SCH_SHEET* target = aContent.targetSheet ? aContent.targetSheet : m_rootSheet;
450 SCH_SCREEN* outgoingScreen = nullptr;
451
452 // Replacing the screen while also replacing the top level sheets frees the outgoing sheets
453 // twice, once through the screen's R-tree and once through the sheet list
454 wxCHECK_RET( !aContent.screen || aContent.topLevelSheets.empty(),
455 wxS( "AdoptContent cannot replace both the screen and the top level sheets" ) );
456
457 // The virtual root's screen owns the top level sheets through its R-tree, so replacing it
458 // outright frees sheets that m_topLevelSheets still names
459 wxCHECK_RET( !aContent.screen || target != m_rootSheet,
460 wxS( "AdoptContent cannot replace the virtual root screen" ) );
461
462 // Replacing the top level sheets deletes the outgoing ones and everything below them, so the
463 // only coherent target is the virtual root whose container index is being replaced with it
464 wxCHECK_RET( aContent.topLevelSheets.empty() || target == m_rootSheet,
465 wxS( "AdoptContent can only replace the top level sheets through the virtual root" ) );
466
467 m_connectivity->Clear();
468
469 if( aContent.screen )
470 {
471 // A sheet and its screen are one identity to the rest of the schematic, so the
472 // incoming screen inherits the identity of the sheet it is hung on.
473 aContent.screen->m_uuid = target->m_Uuid;
474 aContent.screen->IncRefCount();
475 outgoingScreen = std::exchange( target->m_screen, aContent.screen.release() );
476 }
477 else if( SCH_SCREEN* screen = target->GetScreen() )
478 {
479 screen->m_rtree = std::move( aContent.screenItems );
480
481 if( aContent.screenLibSymbols )
482 screen->m_libSymbols.swap( aContent.screenLibSymbols->m_libSymbols );
483
484 --screen->m_modification_sync;
485 screen->BumpConnectivityRevision();
486
487 // The index now owns what it names, so the staged items lose their owners.
488 for( std::unique_ptr<SCH_ITEM>& item : aContent.itemOwners )
489 item.release();
490 }
491
492 std::vector<SCH_SHEET*> outgoingTopLevelSheets;
493
494 if( !aContent.topLevelSheets.empty() )
495 {
496 outgoingTopLevelSheets.swap( m_topLevelSheets );
497 m_topLevelSheets = std::move( aContent.topLevelSheets );
498
499 for( SCH_SHEET* sheet : m_topLevelSheets )
500 sheet->SetParent( m_rootSheet );
501 }
502
503 m_hierarchy.swap( aContent.hierarchy );
504
505 if( aContent.currentSheet )
506 m_currentSheet->Swap( *aContent.currentSheet );
507
508 m_labelToPageRefsMap.clear();
509
510 CONNECTION_GRAPH* outgoingGraph = std::exchange( m_connectionGraph, aContent.connectionGraph.release() );
511
512 // The outgoing graph, deleted below, still points at a replaced manager
513 std::unique_ptr<SCH_CONNECTIVITY::NETCHAIN_MANAGER> outgoingNetChains;
514
515 if( aContent.preserveNetChains )
516 m_connectionGraph->BorrowNetChains( *m_netChains );
517 else
518 outgoingNetChains = std::exchange( m_netChains, m_connectionGraph->ReleaseNetChains() );
519
520 m_connectionGraph->SetSchematic( this );
521
522
523 // The hierarchy and the current sheet named sheets that the outgoing screen and the
524 // outgoing top level sheets own, so neither could be destroyed before now.
525 for( SCH_SHEET* sheet : outgoingTopLevelSheets )
526 delete sheet;
527
528 if( outgoingScreen )
529 {
530 outgoingScreen->DecRefCount();
531
532 if( outgoingScreen->GetRefCount() == 0 )
533 delete outgoingScreen;
534 }
535
536 if( aContent.embeddedFiles )
537 {
538 *GetEmbeddedFiles() = std::move( *aContent.embeddedFiles );
539 Settings().m_SchDrawingSheetFileName.swap( aContent.drawingSheetFileName );
540 }
541
542 // Anything still reachable once the outgoing sheets and screen are gone survived the adoption
543 // while holding SCH_CONNECTIONs that name the outgoing graph. Symbol and sheet pins carry
544 // their own connection maps, so the indexed items alone are not enough, and Recalculate only
545 // re-points what it considers dirty.
546 SCH_SCREENS retained( Root() );
547
548 for( SCH_SCREEN* screen = retained.GetFirst(); screen; screen = retained.GetNext() )
549 {
550 for( SCH_ITEM* item : screen->Items() )
551 {
552 item->SetConnectionGraph( m_connectionGraph );
553
554 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
555 {
556 for( std::unique_ptr<SCH_PIN>& pin : symbol->GetRawPins() )
557 pin->SetConnectionGraph( m_connectionGraph );
558 }
559 else if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
560 {
561 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
562 pin->SetConnectionGraph( m_connectionGraph );
563 }
564 }
565 }
566
567 delete outgoingGraph;
568
569 if( m_textVarAdapter )
570 m_textVarAdapter->RebuildIndex();
571}
572
573
575{
576 // Virtual root's screen is just a container - return the first top-level sheet's screen
577 // which is what callers actually want
578 if( !m_topLevelSheets.empty() && m_topLevelSheets[0] )
579 return m_topLevelSheets[0]->GetScreen();
580
581 return nullptr;
582}
583
584
586{
587 wxCHECK( !m_hierarchy.empty(), m_hierarchy );
588
589 return m_hierarchy;
590}
591
592
594{
595 return !m_hierarchy.empty() && m_hierarchy.IsModified();
596}
597
598
607
608
609void SCHEMATIC::GetContextualTextVars( wxArrayString* aVars ) const
610{
611 auto add =
612 [&]( const wxString& aVar )
613 {
614 if( !alg::contains( *aVars, aVar ) )
615 aVars->push_back( aVar );
616 };
617
618 add( wxT( "#" ) );
619 add( wxT( "##" ) );
620 add( wxT( "SHEETPATH" ) );
621 add( wxT( "SHEETNAME" ) );
622 add( wxT( "FILENAME" ) );
623 add( wxT( "FILEPATH" ) );
624 add( wxT( "PROJECTNAME" ) );
625 add( wxT( "VARIANT" ) );
626 add( wxT( "VARIANT_DESC" ) );
627
628 if( !CurrentSheet().empty() )
630
631 for( std::pair<wxString, wxString> entry : m_project->GetTextVars() )
632 add( entry.first );
633}
634
635
636bool SCHEMATIC::ResolveTextVar( const SCH_SHEET_PATH* aSheetPath, wxString* token, int aDepth ) const
637{
638 wxCHECK( aSheetPath, false );
639
640 if( token->IsSameAs( wxT( "#" ) ) )
641 {
642 *token = aSheetPath->GetPageNumber();
643 return true;
644 }
645 else if( token->IsSameAs( wxT( "##" ) ) )
646 {
647 int i = 0;
648
649 for( SCH_SHEET* sheet : GetTopLevelSheets() )
650 i += sheet->CountSheets();
651
652 *token = wxString::Format( "%i", i );
653 return true;
654 }
655 else if( token->IsSameAs( wxT( "SHEETPATH" ) ) )
656 {
657 *token = aSheetPath->PathHumanReadable();
658 return true;
659 }
660 else if( token->IsSameAs( wxT( "SHEETNAME" ) ) )
661 {
662 *token = aSheetPath->Last()->GetName();
663 return true;
664 }
665 else if( token->IsSameAs( wxT( "FILENAME" ) ) )
666 {
667 wxFileName fn( GetFileName() );
668 *token = fn.GetFullName();
669 return true;
670 }
671 else if( token->IsSameAs( wxT( "FILEPATH" ) ) )
672 {
673 wxFileName fn( GetFileName() );
674 *token = fn.GetFullPath();
675 return true;
676 }
677 else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) )
678 {
679 *token = m_project->GetProjectName();
680 return true;
681 }
682 else if( token->IsSameAs( wxT( "VARIANTNAME" ) ) || token->IsSameAs( wxT( "VARIANT" ) ) )
683 {
684 *token = m_currentVariant;
685 return true;
686 }
687 else if( token->IsSameAs( wxT( "VARIANT_DESC" ) ) )
688 {
690 return true;
691 }
692
693 // aSheetPath->LastScreen() can be null during schematic loading
694 if( aSheetPath->LastScreen()
695 && aSheetPath->LastScreen()->GetTitleBlock().TextVarResolver( token, m_project, INTERNAL ) )
696 {
697 return true;
698 }
699
700 if( m_project->TextVarResolver( token ) )
701 return true;
702
703 return false;
704}
705
706
708{
709 // With virtual root pattern, m_rootSheet is the virtual root with no file
710 // Return filename from first top-level sheet if available
711 if( !IsValid() )
712 return wxString( wxEmptyString );
713
714 if( !m_topLevelSheets.empty() && m_topLevelSheets[0]->GetScreen() )
715 return m_topLevelSheets[0]->GetScreen()->GetFileName();
716
717 return wxString( wxEmptyString );
718}
719
720
722{
723 if( !m_project )
724 {
725 static SCHEMATIC_SETTINGS defaultSettings( nullptr, "schematic" );
726 return defaultSettings;
727 }
728 wxASSERT( m_project );
729 return *m_project->GetProjectFile().m_SchematicSettings;
730}
731
732
734{
735 wxASSERT( m_project );
736 return *m_project->GetProjectFile().m_ErcSettings;
737}
738
739
740std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
741{
742 SCH_SHEET_LIST sheetList = Hierarchy();
743 ERC_SETTINGS& settings = ErcSettings();
744
747
748 // Child exclusions need the loaded hierarchy to recover nonpersistent item IDs.
749 for( const auto& [markerData, comment] : settings.m_ErcExclusionsLegacy )
750 {
751 ERC_EXCLUSION exclusion = ERC_EXCLUSION::FromLegacyStrings( sheetList, markerData, comment );
752
753 if( !exclusion.GetSortKey().empty() )
754 {
755 // Legacy format can sometimes have the same exclusion multiple times,
756 // without and with a comment. If this happens, replace the existing one
757 // if we can go from no comment to comment
758 auto [it, inserted] = settings.m_ErcExclusions.insert( exclusion );
759
760 if( !inserted && !comment.empty() && it->GetComment().empty() )
761 {
762 ERC_EXCLUSION updated = *it;
763 updated.SetComment( comment );
764 settings.m_ErcExclusions.erase( it );
765 settings.m_ErcExclusions.insert( updated );
766 }
767 }
768 }
769
770 settings.m_ErcExclusionsLegacy.clear();
771
772 using GROUP_ANCHOR = std::variant<KIID, std::pair<int, int>>;
773 using GROUP_KEY = std::tuple<int, KIID_PATH, GROUP_ANCHOR>;
774 std::map<GROUP_KEY, SCH_MARKER*> groupMarkers;
775 using NC_GROUPS = std::map<std::pair<KIID_PATH, VECTOR2I>, SCH_CONNECTIVITY::NO_CONNECT_PIN_CONFLICT,
777 std::optional<NC_GROUPS> ncGroups;
778 std::optional<std::map<GROUP_KEY, KIID>> ncFlagGroups;
779 std::optional<std::map<GROUP_KEY, KIID>> hierarchyGroups;
780 std::optional<std::vector<SCH_CONNECTIVITY::LABEL_WIRE_CONFLICT>> labelWireGroups;
781 std::optional<std::vector<SCH_CONNECTIVITY::FOUR_WAY_JUNCTION>> fourWayGroups;
782
783 const auto groupKey =
784 [&]( const SCH_MARKER& aMarker ) -> std::optional<GROUP_KEY>
785 {
786 const auto error = std::static_pointer_cast<ERC_ITEM>( aMarker.GetRCItem() );
787
788 const bool ncPin = error->GetErrorCode() == ERCE_NOCONNECT_CONNECTED
789 && !error->MainItemHasSheetPath() && !error->AuxItemHasSheetPath();
790
791 static const std::set<int> groupedCodes = { ERCE_WIRE_DANGLING, ERCE_BUS_TO_NET_CONFLICT,
796
797 if( !ADVANCED_CFG::GetCfg().m_ConnectivityEngine || !groupedCodes.contains( error->GetErrorCode() )
798 || !error->IsSheetSpecific() )
799 {
800 return std::nullopt;
801 }
802
803 if( error->GetErrorCode() == ERCE_FOUR_WAY_JUNCTION )
804 {
805 if( error->MainItemHasSheetPath() || error->AuxItemHasSheetPath() )
806 return std::nullopt;
807
808 if( !fourWayGroups )
809 fourWayGroups = m_connectivity->Engine().FourWayJunctions();
810
811 const KIID_PATH& path = error->GetSpecificSheetPath().PathRef();
812 const VECTOR2I position = aMarker.GetPosition();
813 const auto group = std::lower_bound( fourWayGroups->begin(), fourWayGroups->end(),
814 std::tie( path, position.x, position.y ),
815 []( const auto& junction, const auto& location )
816 {
817 return std::tie( junction.sheet, junction.position.x, junction.position.y ) < location;
818 } );
819
820 if( group == fourWayGroups->end() || group->sheet != path || group->position != position )
821 return std::nullopt;
822
823 for( const KIID& id : error->GetIDs() )
824 {
825 if( !std::binary_search( group->equivalentItems.begin(), group->equivalentItems.end(), id ) )
826 return std::nullopt;
827 }
828
829 return GROUP_KEY{ ERCE_FOUR_WAY_JUNCTION, path, std::pair{ position.x, position.y } };
830 }
831
832 const SCH_ITEM* main = ResolveItem( error->GetMainItemID(), nullptr, true );
833
834 if( !main || main->GetPosition() != aMarker.GetPosition() )
835 return std::nullopt;
836
837 if( error->GetErrorCode() == ERCE_PIN_NOT_CONNECTED )
838 {
839 if( main->Type() != SCH_PIN_T )
840 return std::nullopt;
841
842 // Power-symbol errors identify individual pins, not a canonical witness for an island
843 if( static_cast<const SCH_PIN*>( main )->GetParentSymbol()->IsPower() )
844 return std::nullopt;
845 }
846
847 const KIID_PATH& path = error->GetSpecificSheetPath().PathRef();
848 const auto instance = m_connectivity->Keys().FindInstance( path );
849
850 if( !instance )
851 return std::nullopt;
852
853 if( error->GetErrorCode() == ERCE_LABEL_MULTIPLE_WIRES )
854 {
855 if( ( main->Type() != SCH_LABEL_T
856 && main->Type() != SCH_GLOBAL_LABEL_T
857 && main->Type() != SCH_HIER_LABEL_T )
858 || error->MainItemHasSheetPath()
859 || error->AuxItemHasSheetPath() )
860 {
861 return std::nullopt;
862 }
863
864 if( !labelWireGroups )
865 labelWireGroups = m_connectivity->Engine().LabelWireConflicts();
866
867 const auto group = std::find_if( labelWireGroups->begin(), labelWireGroups->end(),
868 [&]( const auto& conflict )
869 {
870 return conflict.sheet == path
871 && conflict.position == aMarker.GetPosition();
872 } );
873
874 if( group == labelWireGroups->end() )
875 return std::nullopt;
876
877 for( const KIID& id : error->GetIDs() )
878 {
879 if( id != main->m_Uuid && !std::binary_search( group->wires.begin(), group->wires.end(), id ) )
880 return std::nullopt;
881 }
882
883 return GROUP_KEY{ ERCE_LABEL_MULTIPLE_WIRES, path, group->label };
884 }
885
886 if( error->GetErrorCode() == ERCE_HIERACHICAL_LABEL )
887 {
888 if( !error->MainItemHasSheetPath()
889 || error->AuxItemHasSheetPath()
890 || error->GetMainItemSheetPath().PathRef() != path )
891 {
892 return std::nullopt;
893 }
894
895 if( !hierarchyGroups )
896 {
897 hierarchyGroups.emplace();
898
899 for( const auto& diagnostic : m_connectivity->Engine().HierarchyErrors() )
900 {
901 for( const KIID& id : diagnostic.equivalentItems )
902 {
903 hierarchyGroups->emplace( GROUP_KEY{ ERCE_HIERACHICAL_LABEL, diagnostic.sheet, id },
904 diagnostic.item );
905 }
906 }
907 }
908
909 const auto found = hierarchyGroups->find( { ERCE_HIERACHICAL_LABEL, path, main->m_Uuid } );
910
911 if( found == hierarchyGroups->end() )
912 return std::nullopt;
913
914 return GROUP_KEY{ ERCE_HIERACHICAL_LABEL, path, found->second };
915 }
916
917 if( ncPin )
918 {
919 if( !ncGroups )
920 {
921 ncGroups.emplace();
922
923 for( auto& group : m_connectivity->Engine().NoConnectPinConflicts() )
924 ncGroups->emplace( std::make_pair( group.sheet, group.position ), std::move( group ) );
925 }
926
927 const auto found = ncGroups->find( { path, aMarker.GetPosition() } );
928
929 if( found == ncGroups->end() )
930 return std::nullopt;
931
932 const auto& group = found->second;
933 const auto isPin =
934 [&]( const KIID& id )
935 {
936 return std::binary_search( group.pins.begin(), group.pins.end(), id );
937 };
938
939 if( !isPin( error->GetMainItemID() ) )
940 return std::nullopt;
941
942 for( const KIID& id : error->GetIDs() )
943 {
944 if( id == niluuid )
945 continue;
946
947 if( !m_connectivity->GetSubgraphForItem( id, path )
948 || ( !isPin( id ) && !std::binary_search( group.others.begin(), group.others.end(), id ) ) )
949 {
950 return std::nullopt;
951 }
952 }
953
954 return GROUP_KEY{ error->GetErrorCode(), path, group.pins.front() };
955 }
956
957 if( error->GetErrorCode() == ERCE_NOCONNECT_CONNECTED
958 || error->GetErrorCode() == ERCE_NOCONNECT_NOT_CONNECTED )
959 {
960 if( !error->MainItemHasSheetPath()
961 || error->AuxItemHasSheetPath()
962 || error->GetMainItemSheetPath().PathRef() != path )
963 {
964 return std::nullopt;
965 }
966
967 KIID flag = niluuid;
968
969 for( const KIID& id : error->GetIDs() )
970 {
971 const SCH_ITEM* item = ResolveItem( id, nullptr, true );
972
973 if( item && item->Type() == SCH_NO_CONNECT_T )
974 flag = id;
975 }
976
977 const auto& islandOf = m_connectivity->Published().Auxiliary().IslandOf();
978 const auto flagIsland = islandOf.find( { flag, *instance } );
979
980 if( flagIsland == islandOf.end() )
981 return std::nullopt;
982
983 if( !ncFlagGroups )
984 {
985 ncFlagGroups.emplace();
986
987 for( const auto& diagnostic : m_connectivity->Engine().NoConnectFlagErrors() )
988 {
989 const auto inst = m_connectivity->Keys().FindInstance( diagnostic.sheet );
990 const auto record = inst ? islandOf.find( { diagnostic.flag, *inst } ) : islandOf.end();
991
992 if( record == islandOf.end() )
993 continue;
994
995 const int code = diagnostic.connected ? ERCE_NOCONNECT_CONNECTED
997 ncFlagGroups->emplace( GROUP_KEY{ code, diagnostic.sheet, record->second.anchor },
998 diagnostic.flag );
999 }
1000 }
1001
1002 const auto group = ncFlagGroups->find( { error->GetErrorCode(), path, flagIsland->second.anchor } );
1003
1004 if( group == ncFlagGroups->end() )
1005 return std::nullopt;
1006
1007 const auto& rows = m_connectivity->Published().Rows();
1008 const auto flagRow = rows.find( { flag, *instance } );
1009
1010 if( flagRow == rows.end() )
1011 return std::nullopt;
1012
1013 for( const KIID& id : error->GetIDs() )
1014 {
1015 if( id == niluuid || id == flag )
1016 continue;
1017
1018 const SCH_ITEM* item = ResolveItem( id, nullptr, true );
1019 const auto row = rows.find( { id, *instance } );
1020
1021 if( !item || item->Type() != SCH_PIN_T
1022 || row == rows.end()
1023 || row->second.component != flagRow->second.component
1024 || !m_connectivity->GetSubgraphForItem( id, path ) )
1025 {
1026 return std::nullopt;
1027 }
1028 }
1029
1030 return GROUP_KEY{ error->GetErrorCode(), path, group->second };
1031 }
1032
1033 const auto& islands = m_connectivity->Published().Auxiliary().IslandOf();
1034 std::optional<GROUP_KEY> key;
1035
1036 for( const KIID& id : error->GetIDs() )
1037 {
1038 if( id == niluuid )
1039 continue;
1040
1041 const auto island = islands.find( { id, *instance } );
1042
1043 if( !m_connectivity->GetSubgraphForItem( id, path )
1044 || island == islands.end()
1045 || ( key && std::get<2>( *key ) != GROUP_ANCHOR{ island->second.anchor } ) )
1046 {
1047 return std::nullopt;
1048 }
1049
1050 key = GROUP_KEY{ error->GetErrorCode(), path, island->second.anchor };
1051 }
1052
1053 return key;
1054 };
1055
1056 std::set<ERC_EXCLUSION, ERC_EXCLUSION_COMPARE> legacyPathlessExclusions;
1057 std::optional<std::vector<SCH_CONNECTIVITY::OFF_GRID_ENDPOINT>> offGridEndpoints;
1058
1059 const auto findPathless =
1060 [&]( const ERC_EXCLUSION& aLookup )
1061 {
1062 kiapi::schematic::ErcExclusion legacy = aLookup.ToProto();
1063 legacy.mutable_marker()->clear_sheet_specific_path();
1064 legacy.mutable_marker()->clear_main_item_sheet_path();
1065 legacy.mutable_marker()->clear_aux_item_sheet_path();
1066 auto found = settings.m_ErcExclusions.find( ERC_EXCLUSION::FromProto( legacy ) );
1067
1068 if( found != settings.m_ErcExclusions.end() )
1069 legacyPathlessExclusions.insert( *found );
1070
1071 return found;
1072 };
1073
1074 for( const SCH_SHEET_PATH& sheet : sheetList )
1075 {
1076 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
1077 {
1078 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
1079 ERC_EXCLUSION lookup = ERC_EXCLUSION::FromMarker( *marker );
1080 auto it = settings.m_ErcExclusions.find( lookup );
1081
1082 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
1083
1084 if( it == settings.m_ErcExclusions.end()
1085 && !settings.m_ErcExclusions.empty()
1086 && error->IsSheetSpecific()
1087 && error->GetSpecificSheetPath().PathRef() == sheet.PathRef() )
1088 {
1089 const bool engine = ADVANCED_CFG::GetCfg().m_ConnectivityEngine;
1090 const bool hasMain = error->MainItemHasSheetPath();
1091 const bool hasAux = error->AuxItemHasSheetPath();
1092 const bool mainHere = hasMain && error->GetMainItemSheetPath().PathRef() == sheet.PathRef();
1093 const bool auxHere = hasAux && error->GetAuxItemSheetPath().PathRef() == sheet.PathRef();
1094
1095 switch( error->GetErrorCode() )
1096 {
1098 if( engine && !hasMain && !hasAux && sheet.Last()->IsTopLevelSheet() )
1099 {
1100 const SCH_ITEM* source = ResolveItem( error->GetMainItemID(), nullptr, true );
1101
1102 // Root-label exclusions predating instance-specific ERC omitted the sheet path
1103 if( source && source->Type() == SCH_HIER_LABEL_T )
1104 it = findPathless( lookup );
1105 }
1106
1107 break;
1108
1110 case ERCE_GENERIC_ERROR:
1114 if( !marker->IsExcluded() && mainHere && !hasAux )
1115 {
1116 // Older sheet-specific exclusions omitted the main item's sheet path
1117 kiapi::schematic::ErcExclusion previous = lookup.ToProto();
1118 previous.mutable_marker()->clear_main_item_sheet_path();
1119 it = settings.m_ErcExclusions.find( ERC_EXCLUSION::FromProto( previous ) );
1120 }
1121
1122 break;
1123
1132 // Older source diagnostics excluded the item across all of its sheet instances
1133 if( !marker->IsExcluded() && mainHere && !hasAux )
1134 it = findPathless( lookup );
1135
1136 if( it == settings.m_ErcExclusions.end()
1137 && engine
1138 && mainHere
1139 && !hasAux
1140 && error->GetErrorCode() == ERCE_ENDPOINT_OFF_GRID )
1141 {
1142 if( !offGridEndpoints )
1143 {
1144 offGridEndpoints =
1145 m_connectivity->Engine().OffGridEndpoints( Settings().m_ConnectionGridSize );
1146 }
1147
1148 for( const SCH_CONNECTIVITY::OFF_GRID_ENDPOINT& endpoint : *offGridEndpoints )
1149 {
1150 if( endpoint.sheet != sheet.PathRef()
1151 || endpoint.item != error->GetMainItemID()
1152 || endpoint.position != marker->GetPosition() )
1153 {
1154 continue;
1155 }
1156
1157 for( const auto& [pin, position] : endpoint.equivalentPins )
1158 {
1159 std::shared_ptr<ERC_ITEM> alternate = ERC_ITEM::Create( ERCE_ENDPOINT_OFF_GRID );
1160 alternate->SetItems( std::vector<KIID>{ pin } );
1161 alternate->SetSheetSpecificPath( sheet );
1162 alternate->SetItemsSheetPaths( sheet );
1163 SCH_MARKER witness( std::move( alternate ), position );
1164 const ERC_EXCLUSION candidate = ERC_EXCLUSION::FromMarker( witness );
1165 it = settings.m_ErcExclusions.find( candidate );
1166
1167 if( it == settings.m_ErcExclusions.end() )
1168 it = findPathless( candidate );
1169
1170 if( it != settings.m_ErcExclusions.end() )
1171 break;
1172 }
1173
1174 break;
1175 }
1176 }
1177
1178 break;
1179
1181 // Older duplicate-sheet exclusions covered this pair in every parent instance
1182 if( !marker->IsExcluded() && mainHere && auxHere )
1183 it = findPathless( lookup );
1184
1185 break;
1186
1188 // Older exclusions identified the unit pair without either sheet instance
1189 if( hasMain && auxHere )
1190 it = findPathless( lookup );
1191
1192 break;
1193
1194 default:
1195 break;
1196 }
1197 }
1198
1199 if( it != settings.m_ErcExclusions.end() )
1200 {
1201 marker->SetExcluded( true, it->GetComment() );
1202
1203 if( !legacyPathlessExclusions.contains( *it ) )
1204 settings.m_ErcExclusions.erase( it );
1205 }
1206 else if( !marker->IsExcluded() )
1207 {
1208 if( const std::optional<GROUP_KEY> key = groupKey( *marker ) )
1209 groupMarkers.emplace( *key, marker );
1210 }
1211 }
1212 }
1213
1214 for( const ERC_EXCLUSION& exclusion : legacyPathlessExclusions )
1215 settings.m_ErcExclusions.erase( exclusion );
1216
1217 std::vector<SCH_MARKER*> newMarkers;
1218
1219 for( const ERC_EXCLUSION& exclusion : settings.m_ErcExclusions )
1220 {
1221 SCH_MARKER* marker = SCH_MARKER::FromProto( exclusion.ToProto().marker(), sheetList );
1222
1223 if( marker )
1224 {
1225 // Canonical connectivity witnesses can change the marker anchor and reported IDs
1226 if( const std::optional<GROUP_KEY> key = groupKey( *marker ) )
1227 {
1228 const auto current = groupMarkers.find( *key );
1229 const auto itemCount =
1230 []( const SCH_MARKER& aMarker )
1231 {
1232 return std::ranges::count_if( aMarker.GetRCItem()->GetIDs(),
1233 []( const KIID& id )
1234 {
1235 return id != niluuid;
1236 } );
1237 };
1238
1239 if( current != groupMarkers.end()
1240 && itemCount( *current->second ) == itemCount( *marker ) )
1241 {
1242 current->second->SetExcluded( true, exclusion.GetComment() );
1243 groupMarkers.erase( current );
1244 delete marker;
1245 continue;
1246 }
1247 }
1248
1249 marker->SetExcluded( true, exclusion.GetComment() );
1250 newMarkers.push_back( marker );
1251 }
1252 else
1253 {
1254 m_unresolvedErcExclusions.push_back( exclusion );
1255 }
1256 }
1257
1258 settings.m_ErcExclusions.clear();
1259
1260 return newMarkers;
1261}
1262
1263
1264std::shared_ptr<BUS_ALIAS> SCHEMATIC::GetBusAlias( const wxString& aLabel ) const
1265{
1266 for( auto it = m_busAliases.rbegin(); it != m_busAliases.rend(); ++it )
1267 {
1268 const auto& alias = *it;
1269
1270 if( alias && alias->GetName() == aLabel )
1271 return alias;
1272 }
1273
1274 return nullptr;
1275}
1276
1277
1278void SCHEMATIC::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
1279{
1280 if( !aAlias )
1281 return;
1282
1283 auto sameDefinition =
1284 [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
1285 {
1286 return candidate
1287 && candidate->GetName() == aAlias->GetName()
1288 && candidate->Members() == aAlias->Members();
1289 };
1290
1291 auto it = std::find_if( m_busAliases.begin(), m_busAliases.end(), sameDefinition );
1292
1293 if( it != m_busAliases.end() )
1294 std::rotate( it, std::next( it ), m_busAliases.end() );
1295 else
1296 m_busAliases.push_back( aAlias->Clone() );
1297
1299}
1300
1301
1302void SCHEMATIC::SetBusAliases( const std::vector<std::shared_ptr<BUS_ALIAS>>& aAliases )
1303{
1304 std::vector<std::shared_ptr<BUS_ALIAS>> aliases;
1305
1306 for( const std::shared_ptr<BUS_ALIAS>& alias : aAliases )
1307 {
1308 if( !alias )
1309 continue;
1310
1311 std::shared_ptr<BUS_ALIAS> clone = alias->Clone();
1312
1313 auto sameDefinition =
1314 [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
1315 {
1316 return candidate
1317 && candidate->GetName() == clone->GetName()
1318 && candidate->Members() == clone->Members();
1319 };
1320
1321 auto it = std::find_if( aliases.begin(), aliases.end(), sameDefinition );
1322
1323 if( it != aliases.end() )
1324 std::rotate( it, std::next( it ), aliases.end() );
1325 else
1326 aliases.push_back( clone );
1327 }
1328
1329 m_busAliases.swap( aliases );
1331
1332 if( m_project )
1333 m_project->GetProjectFile().m_BusAliasesDefined = true;
1334}
1335
1336
1338{
1339 return m_project && m_project->GetProjectFile().m_BusAliasesDefined;
1340}
1341
1342
1344{
1345 m_busAliases.clear();
1346
1347 if( !m_project )
1348 return;
1349
1350 const std::map<wxString, std::vector<wxString>>& projectAliases = m_project->GetProjectFile().m_BusAliases;
1351
1352 for( const auto& [name, members] : projectAliases )
1353 {
1354 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
1355
1356 busAlias->SetName( name );
1357 busAlias->SetMembers( members );
1358
1359 m_busAliases.push_back( busAlias );
1360 }
1361}
1362
1363
1365{
1366 if( !m_project )
1367 return;
1368
1369 std::map<wxString, std::vector<wxString>>& projectAliases = m_project->GetProjectFile().m_BusAliases;
1370
1371 projectAliases.clear();
1372
1373 for( const std::shared_ptr<BUS_ALIAS>& alias : m_busAliases )
1374 {
1375 if( !alias )
1376 continue;
1377
1378 projectAliases.insert_or_assign( alias->GetName(), alias->Members() );
1379 }
1380}
1381
1382
1384{
1385 std::set<wxString> names;
1386
1387 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
1388 {
1389 for( const SCH_CONNECTIVITY::NET_GROUP& group : Connectivity().GetNetMap() )
1390 {
1391 // A net without items, such as a member of an unplaced bus, has nothing to assign a netclass to
1392 if( std::ranges::all_of( group.instances,
1393 []( const SCH_CONNECTIVITY::NET_VIEW& net )
1394 {
1395 return net.Items().empty();
1396 } ) )
1397 {
1398 continue;
1399 }
1400
1401 const auto& net = group.instances.front();
1402
1405 {
1406 names.insert( group.name );
1407 }
1408 }
1409
1410 return names;
1411 }
1412
1413 for( const auto& [key, subgraphList] : m_connectionGraph->GetNetMap() )
1414 {
1415 CONNECTION_SUBGRAPH* firstSubgraph = subgraphList[0];
1416
1417 if( !firstSubgraph->GetDriverConnection()->IsBus()
1419 {
1420 names.insert( key.Name );
1421 }
1422 }
1423
1424 return names;
1425}
1426
1427
1428bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
1429{
1431
1432 if( !environment || !environment->IsCollectingSources() )
1433 return resolveCrossReference( token, aDepth );
1434
1435 const TEXT_EVAL::ENVIRONMENT::CROSS_REFERENCE_KEY key{ *token, aDepth };
1436 const bool resolved = resolveCrossReference( token, aDepth );
1437 environment->RecordCrossReference( key, { *token, resolved } );
1438 return resolved;
1439}
1440
1441
1442bool SCHEMATIC::resolveCrossReference( wxString* token, int aDepth ) const
1443{
1444 wxString remainder;
1445 wxString ref = token->BeforeFirst( ':', &remainder );
1446 KIID_PATH path( ref );
1447 KIID uuid( 0 );
1448
1449 if( path.size() > 1 )
1450 uuid = path.back();
1451
1452 SCH_SHEET_PATH sheetPath;
1453 SCH_ITEM* refItem = ResolveItem( KIID( uuid ), &sheetPath, true );
1454
1455 if( path.size() > 1 )
1456 {
1457 path.pop_back();
1458 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
1459 }
1460
1461 // Parse optional variant name from syntax ${REF:FIELD:VARIANT}
1462 // remainder is "FIELD" or "FIELD:VARIANT"
1463 wxString variantName;
1464 wxString fieldName = remainder;
1465 int colonPos = remainder.Find( ':' );
1466
1467 if( colonPos != wxNOT_FOUND )
1468 {
1469 fieldName = remainder.Left( colonPos );
1470 variantName = remainder.Mid( colonPos + 1 );
1471 }
1472
1473 // Note: We don't expand nested variables or evaluate math expressions here.
1474 // The multi-pass loop in GetShownText handles all variable and expression resolution
1475 // before cross-references are resolved. This ensures table cell variables like ${ROW}
1476 // are expanded correctly.
1477
1478 if( refItem && refItem->Type() == SCH_SYMBOL_T )
1479 {
1480 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
1481
1482 if( refSymbol->ResolveTextVar( &sheetPath, &fieldName, variantName, aDepth + 1 ) )
1483 {
1484 *token = std::move( fieldName );
1485 }
1486 else
1487 {
1488 // Field/function not found on symbol
1489 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ),
1490 refSymbol->GetRef( &sheetPath, false ),
1491 fieldName );
1492 }
1493
1494 return true; // Stop looking
1495 }
1496 else if( refItem && refItem->Type() == SCH_SHEET_T )
1497 {
1498 SCH_SHEET* refSheet = static_cast<SCH_SHEET*>( refItem );
1499
1500 sheetPath.push_back( refSheet );
1501
1502 if( refSheet->ResolveTextVar( &sheetPath, &remainder, aDepth + 1 ) )
1503 {
1504 *token = std::move( remainder );
1505 }
1506 else
1507 {
1508 // Field/function not found on sheet
1509 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ),
1510 refSheet->GetName(),
1511 remainder );
1512 }
1513
1514 return true; // Stop looking
1515 }
1516
1517 // If UUID resolution failed, try to resolve by reference designator
1518 // This handles both exact matches (J601A) and parent references for multi-unit symbols (J601)
1519 if( !refItem )
1520 {
1521 SCH_REFERENCE_LIST refs;
1523
1524 SCH_SYMBOL* foundSymbol = nullptr;
1525 SCH_SHEET_PATH foundPath;
1526
1527 for( int ii = 0; ii < (int) refs.GetCount(); ii++ )
1528 {
1529 SCH_REFERENCE& reference = refs[ii];
1530 wxString symbolRef = reference.GetSymbol()->GetRef( &reference.GetSheetPath(), false );
1531
1532 // Try exact match first
1533 if( symbolRef == ref )
1534 {
1535 foundSymbol = reference.GetSymbol();
1536 foundPath = reference.GetSheetPath();
1537 break;
1538 }
1539
1540 // For multi-unit symbols, try matching parent reference (e.g., J601 matches J601A)
1541 if( symbolRef.StartsWith( ref ) && symbolRef.Length() == ref.Length() + 1 )
1542 {
1543 wxChar lastChar = symbolRef.Last();
1544 if( lastChar >= 'A' && lastChar <= 'Z' )
1545 {
1546 foundSymbol = reference.GetSymbol();
1547 foundPath = reference.GetSheetPath();
1548 // Don't break - continue looking for exact match
1549 }
1550 }
1551 }
1552
1553 if( foundSymbol )
1554 {
1555 if( foundSymbol->ResolveTextVar( &foundPath, &fieldName, variantName, aDepth + 1 ) )
1556 {
1557 *token = std::move( fieldName );
1558 }
1559 else
1560 {
1561 // Field/function not found on symbol
1562 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ),
1563 foundSymbol->GetRef( &foundPath, false ),
1564 fieldName );
1565 }
1566
1567 return true; // Stop looking
1568 }
1569
1570 // Symbol not found - set unresolved error
1571 *token = wxString::Format( wxT( "<Unresolved: %s>" ), ref );
1572 return true;
1573 }
1574
1575 // Reference not found - show error message
1576 *token = wxString::Format( wxT( "<Unknown reference: %s>" ), ref );
1577 return true;
1578}
1579
1580
1581std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetNamesMap() const
1582{
1583 std::map<int, wxString> namesMap;
1584
1585 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1586 {
1587 if( sheet.size() == 1 )
1588 namesMap[sheet.GetVirtualPageNumber()] = _( "<root sheet>" );
1589 else
1590 namesMap[sheet.GetVirtualPageNumber()] = sheet.Last()->GetName();
1591 }
1592
1593 return namesMap;
1594}
1595
1596
1597std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetPagesMap() const
1598{
1599 std::map<int, wxString> pagesMap;
1600
1601 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1602 pagesMap[sheet.GetVirtualPageNumber()] = sheet.GetPageNumber();
1603
1604 return pagesMap;
1605}
1606
1607
1608wxString SCHEMATIC::ConvertRefsToKIIDs( const wxString& aSource ) const
1609{
1610 wxString newbuf;
1611 size_t sourceLen = aSource.length();
1612
1613 for( size_t i = 0; i < sourceLen; ++i )
1614 {
1615 // Check for escaped expressions: \${ or \@{
1616 // These should be copied verbatim without any ref→KIID conversion
1617 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
1618 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
1619 {
1620 // Copy the escape sequence and the entire escaped expression
1621 newbuf.append( aSource[i] ); // backslash
1622 newbuf.append( aSource[i + 1] ); // $ or @
1623 newbuf.append( aSource[i + 2] ); // {
1624 i += 2;
1625
1626 // Find and copy everything until the matching closing brace
1627 int braceDepth = 1;
1628 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
1629 {
1630 if( aSource[i] == '{' )
1631 braceDepth++;
1632 else if( aSource[i] == '}' )
1633 braceDepth--;
1634
1635 newbuf.append( aSource[i] );
1636 }
1637 i--; // Back up one since the for loop will increment
1638 continue;
1639 }
1640
1641 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
1642 {
1643 wxString token;
1644 bool isCrossRef = false;
1645 int nesting = 0;
1646
1647 for( i = i + 2; i < sourceLen; ++i )
1648 {
1649 if( aSource[i] == '{' && ( aSource[i - 1] == '_' || aSource[i - 1] == '^' || aSource[i - 1] == '~' ) )
1650 {
1651 nesting++;
1652 }
1653
1654 if( aSource[i] == '}' )
1655 {
1656 nesting--;
1657
1658 if( nesting < 0 )
1659 break;
1660 }
1661
1662 if( aSource[i] == ':' )
1663 isCrossRef = true;
1664
1665 token.append( aSource[i] );
1666 }
1667
1668 if( isCrossRef )
1669 {
1670 wxString remainder;
1671 wxString ref = token.BeforeFirst( ':', &remainder );
1672 SCH_REFERENCE_LIST references;
1673
1675
1676 for( size_t jj = 0; jj < references.GetCount(); jj++ )
1677 {
1678 SCH_SYMBOL* refSymbol = references[jj].GetSymbol();
1679
1680 if( ref == refSymbol->GetRef( &references[jj].GetSheetPath(), true ) )
1681 {
1682 KIID_PATH path = references[jj].GetSheetPath().Path();
1683 path.push_back( refSymbol->m_Uuid );
1684
1685 token = path.AsString() + wxS( ":" ) + remainder;
1686 break;
1687 }
1688 }
1689 }
1690
1691 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1692 }
1693 else
1694 {
1695 newbuf.append( aSource[i] );
1696 }
1697 }
1698
1699 return newbuf;
1700}
1701
1702
1703wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
1704{
1705 wxString newbuf;
1706 size_t sourceLen = aSource.length();
1707
1708 for( size_t i = 0; i < sourceLen; ++i )
1709 {
1710 // Check for escaped expressions: \${ or \@{
1711 // These should be copied verbatim without any KIID→ref conversion
1712 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
1713 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
1714 {
1715 // Copy the escape sequence and the entire escaped expression
1716 newbuf.append( aSource[i] ); // backslash
1717 newbuf.append( aSource[i + 1] ); // $ or @
1718 newbuf.append( aSource[i + 2] ); // {
1719 i += 2;
1720
1721 // Find and copy everything until the matching closing brace
1722 int braceDepth = 1;
1723 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
1724 {
1725 if( aSource[i] == '{' )
1726 braceDepth++;
1727 else if( aSource[i] == '}' )
1728 braceDepth--;
1729
1730 newbuf.append( aSource[i] );
1731 }
1732 i--; // Back up one since the for loop will increment
1733 continue;
1734 }
1735
1736 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
1737 {
1738 wxString token;
1739 bool isCrossRef = false;
1740
1741 for( i = i + 2; i < sourceLen; ++i )
1742 {
1743 if( aSource[i] == '}' )
1744 break;
1745
1746 if( aSource[i] == ':' )
1747 isCrossRef = true;
1748
1749 token.append( aSource[i] );
1750 }
1751
1752 if( isCrossRef )
1753 {
1754 wxString remainder;
1755 wxString ref = token.BeforeFirst( ':', &remainder );
1756 KIID_PATH path( ref );
1757 KIID uuid = path.back();
1758 SCH_SHEET_PATH sheetPath;
1759 SCH_ITEM* refItem = ResolveItem( uuid, &sheetPath, true );
1760
1761 if( path.size() > 1 )
1762 {
1763 path.pop_back();
1764 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
1765 }
1766
1767 if( refItem && refItem->Type() == SCH_SYMBOL_T )
1768 {
1769 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
1770 token = refSymbol->GetRef( &sheetPath, true ) + wxS( ":" ) + remainder;
1771 }
1772 }
1773
1774 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1775 }
1776 else
1777 {
1778 newbuf.append( aSource[i] );
1779 }
1780 }
1781
1782 return newbuf;
1783}
1784
1785
1792
1793
1795{
1796 // Filename is rootSheetName-sheetName-...-sheetName
1797 // Note that we need to fetch the rootSheetName out of its filename, as the root SCH_SHEET's
1798 // name is just a timestamp.
1799
1800 // Skip virtual root if present
1801 size_t startIdx = 0;
1802 if( CurrentSheet().size() > 0 && CurrentSheet().at( 0 )->IsVirtualRootSheet() )
1803 startIdx = 1;
1804
1805 // Handle the case where we only have a virtual root (shouldn't happen in practice)
1806 if( startIdx >= CurrentSheet().size() )
1807 return wxEmptyString;
1808
1809 SCH_SHEET* topSheet = CurrentSheet().at( startIdx );
1810
1811 // A top-level sheet keeps its file on the screen, because the SHEET_FILENAME field is only
1812 // set on the sheet instances that a parent sheet owns
1813 wxString topFileName;
1814
1815 if( topSheet->GetScreen() )
1816 topFileName = topSheet->GetScreen()->GetFileName();
1817
1818 if( topFileName.IsEmpty() )
1819 topFileName = topSheet->GetFileName();
1820
1821 wxFileName rootFn( topFileName );
1822 wxString filename = rootFn.GetName();
1823
1824 for( unsigned i = startIdx + 1; i < CurrentSheet().size(); i++ )
1825 filename += wxT( "-" ) + CurrentSheet().at( i )->GetName();
1826
1827 return filename;
1828}
1829
1830
1832{
1833 SCH_SCREEN* screen;
1834 SCH_SCREENS s_list( Root() );
1835
1836 // Set the sheet count, and the sheet number (1 for root sheet)
1837 int sheet_count;
1838
1839 // Handle virtual root case
1840 if( Root().m_Uuid == niluuid )
1841 {
1842 // Virtual root: count all top-level sheets
1843 sheet_count = 0;
1844
1845 for( const SCH_SHEET* topSheet : m_topLevelSheets )
1846 {
1847 if( topSheet )
1848 sheet_count += topSheet->CountSheets();
1849 }
1850 }
1851 else
1852 {
1853 // Traditional single root
1854 sheet_count = Root().CountSheets();
1855 }
1856
1857 int sheet_number = 1;
1858
1859 if( m_hierarchy.empty() )
1860 {
1861 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1862 screen->SetPageCount( sheet_count );
1863
1864 CurrentSheet().SetVirtualPageNumber( sheet_number );
1865 screen = CurrentSheet().LastScreen();
1866
1867 if( screen )
1868 screen->SetVirtualPageNumber( sheet_number );
1869
1870 return;
1871 }
1872
1873 const KIID_PATH& current_sheetpath = CurrentSheet().Path();
1874
1875 // @todo Remove all pseudo page number system is left over from prior to real page number
1876 // implementation.
1877 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1878 {
1879 if( sheet.Path() == current_sheetpath ) // Current sheet path found
1880 break;
1881
1882 sheet_number++; // Not found, increment before this current path
1883 }
1884
1885 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1886 screen->SetPageCount( sheet_count );
1887
1888 CurrentSheet().SetVirtualPageNumber( sheet_number );
1889 CurrentSheet().LastScreen()->SetVirtualPageNumber( sheet_number );
1890 CurrentSheet().LastScreen()->SetPageNumber( CurrentSheet().GetPageNumber() );
1891}
1892
1893
1895{
1896 std::map<wxString, std::set<int>>& pageRefsMap = GetPageRefsMap();
1897
1898 pageRefsMap.clear();
1899
1900 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1901 {
1902 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1903 {
1904 SCH_GLOBALLABEL* global = static_cast<SCH_GLOBALLABEL*>( item );
1905 wxString resolvedLabel = global->GetShownText( &sheet, FOR_GUI );
1906
1907 pageRefsMap[resolvedLabel].insert( sheet.GetVirtualPageNumber() );
1908 }
1909 }
1910
1911 bool show = Settings().m_IntersheetRefsShow;
1912
1913 // Refresh all visible global labels. Note that we have to collect them first as the
1914 // SCH_SCREEN::Update() call is going to invalidate the RTree iterator.
1915
1916 std::vector<SCH_GLOBALLABEL*> currentSheetGlobalLabels;
1917
1918 for( EDA_ITEM* item : CurrentSheet().LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1919 currentSheetGlobalLabels.push_back( static_cast<SCH_GLOBALLABEL*>( item ) );
1920
1921 for( SCH_GLOBALLABEL* globalLabel : currentSheetGlobalLabels )
1922 {
1923 std::vector<SCH_FIELD>& fields = globalLabel->GetFields();
1924
1925 fields[0].SetVisible( show );
1926
1927 if( show )
1928 {
1929 if( fields.size() == 1 && fields[0].GetTextPos() == globalLabel->GetPosition() )
1930 globalLabel->AutoplaceFields( CurrentSheet().LastScreen(), AUTOPLACE_AUTO );
1931
1932 CurrentSheet().LastScreen()->Update( globalLabel );
1933
1934 for( SCH_FIELD& field : globalLabel->GetFields() )
1935 field.ClearBoundingBoxCache();
1936
1937 globalLabel->ClearBoundingBoxCache();
1938
1939 if( m_schematicHolder )
1940 m_schematicHolder->IntersheetRefUpdate( globalLabel );
1941 }
1942 }
1943}
1944
1945
1946void SCHEMATIC::SyncLibSymbolPinMaps( const wxString& aSchLibSymbolName, const LIB_SYMBOL& aSource,
1947 SCH_COMMIT* aCommit )
1948{
1949 SCH_SCREENS screens( Root() );
1950
1951 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1952 {
1953 auto it = screen->GetLibSymbols().find( aSchLibSymbolName );
1954
1955 if( it != screen->GetLibSymbols().end() && it->second && it->second != &aSource )
1956 {
1957 it->second->SetPinMaps( aSource.GetPinMaps() );
1958 it->second->SetAssociatedFootprints( aSource.GetAssociatedFootprints() );
1959 }
1960
1961 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1962 {
1963 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1964
1965 if( symbol->GetSchSymbolLibraryName() != aSchLibSymbolName )
1966 continue;
1967
1968 LIB_SYMBOL* copy = symbol->GetLibSymbolRef().get();
1969
1970 if( !copy || copy == &aSource )
1971 continue;
1972
1973 if( copy->GetPinMaps() == aSource.GetPinMaps()
1974 && copy->GetAssociatedFootprints() == aSource.GetAssociatedFootprints() )
1975 {
1976 continue;
1977 }
1978
1979 if( aCommit )
1980 aCommit->Modify( symbol, screen );
1981
1982 copy->SetPinMaps( aSource.GetPinMaps() );
1983 copy->SetAssociatedFootprints( aSource.GetAssociatedFootprints() );
1984 }
1985 }
1986}
1987
1988
1989wxString SCHEMATIC::GetOperatingPoint( const wxString& aNetName, int aPrecision, const wxString& aRange )
1990{
1991 wxString spiceNetName( aNetName );
1993 spiceNetName.MakeLower();
1994
1995 if( spiceNetName == wxS( "gnd" ) || spiceNetName == wxS( "0" ) )
1996 return wxEmptyString;
1997
1998 auto it = m_operatingPoints.find( spiceNetName );
1999
2000 if( it != m_operatingPoints.end() )
2001 return SPICE_VALUE( it->second ).ToString( { aPrecision, aRange } );
2002 else if( m_operatingPoints.empty() )
2003 return wxS( "--" );
2004 else
2005 return wxS( "?" );
2006}
2007
2008
2009int SCHEMATIC::FixupJunctionsAfterImport( const std::function<void( SCH_LINE*, SCH_LINE* )>& aOnSplit )
2010{
2011 SCH_SCREENS screens( Root() );
2012 int count = 0;
2013
2014 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2015 {
2016 std::deque<EDA_ITEM*> allItems;
2017
2018 for( SCH_ITEM* item : screen->Items() )
2019 allItems.push_back( item );
2020
2021 // Add missing junctions and breakup wires as needed
2022 for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
2023 {
2024 count++;
2025
2026 SCH_JUNCTION* junction = new SCH_JUNCTION( point );
2027 screen->Append( junction );
2028
2029 // Breakup wires
2030 for( SCH_LINE* wire : screen->GetBusesAndWires( point, true ) )
2031 {
2032 SCH_LINE* newSegment = wire->NonGroupAware_BreakAt( point );
2033 screen->Append( newSegment );
2034
2035 if( aOnSplit )
2036 aOnSplit( wire, newSegment );
2037 }
2038 }
2039 }
2040
2041 return count;
2042}
2043
2044
2045void SCHEMATIC::OnItemsAdded( std::vector<SCH_ITEM*>& aNewItems )
2046{
2048}
2049
2050
2051void SCHEMATIC::OnItemsRemoved( std::vector<SCH_ITEM*>& aRemovedItems )
2052{
2054}
2055
2056
2057void SCHEMATIC::OnItemsChanged( std::vector<SCH_ITEM*>& aItems )
2058{
2060}
2061
2062
2067
2068
2073
2074
2076{
2077 if( !alg::contains( m_listeners, aListener ) )
2078 m_listeners.push_back( aListener );
2079}
2080
2081
2083{
2084 auto it = std::find( m_listeners.begin(), m_listeners.end(), aListener );
2085
2086 if( it != m_listeners.end() )
2087 {
2088 std::iter_swap( it, m_listeners.end() - 1 );
2089 m_listeners.pop_back();
2090 }
2091}
2092
2093
2095{
2096 m_listeners.clear();
2097}
2098
2099
2101{
2102 std::erase_if( m_unresolvedErcExclusions,
2103 [&]( const ERC_EXCLUSION& exclusion )
2104 {
2105 if( aErrorCode >= 0 )
2106 {
2107 kiapi::schematic::ErcErrorType error = exclusion.ToProto().marker().error_type();
2108
2110 return false;
2111 }
2112
2113 ErcSettings().m_ErcExclusions.erase( exclusion );
2114 return true;
2115 } );
2116}
2117
2118
2120{
2121 // Use a sorted sheetList to reduce file churn
2122 SCH_SHEET_LIST sheetList = Hierarchy();
2123 ERC_SETTINGS& ercSettings = ErcSettings();
2124
2125 ercSettings.m_ErcExclusions.clear();
2126 ercSettings.m_ErcExclusions.insert( m_unresolvedErcExclusions.begin(), m_unresolvedErcExclusions.end() );
2127
2128 for( unsigned i = 0; i < sheetList.size(); i++ )
2129 {
2130 for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) )
2131 {
2132 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
2133
2134 if( marker->IsExcluded() )
2135 {
2136 ercSettings.m_ErcExclusions.insert( ERC_EXCLUSION::FromMarker( *marker ) );
2137 }
2138 }
2139 }
2140}
2141
2142
2144{
2145 SCH_SHEET_LIST sheetList = Hierarchy();
2146
2147 for( SCH_MARKER* marker : ResolveERCExclusions() )
2148 {
2149 SCH_SHEET_PATH errorPath;
2150 ignore_unused( sheetList.ResolveItem( marker->GetRCItem()->GetMainItemID(), &errorPath ) );
2151
2152 if( errorPath.LastScreen() )
2153 errorPath.LastScreen()->Append( marker );
2154 else
2155 RootScreen()->Append( marker );
2156 }
2157
2158 // Once we have the ERC Exclusions, record them in the project file so that
2159 // they are retained even before the schematic is saved (PCB Editor can also save the project)
2161}
2162
2163
2165{
2166 return static_cast<EMBEDDED_FILES*>( this );
2167}
2168
2169
2171{
2172 return static_cast<const EMBEDDED_FILES*>( this );
2173}
2174
2175
2176void SCHEMATIC::RunOnNestedEmbeddedFiles( const std::function<void( EMBEDDED_FILES* )>& aFunction )
2177{
2178 SCH_SCREENS screens( Root() );
2179
2180 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2181 {
2182 for( auto& [name, libSym] : screen->GetLibSymbols() )
2183 aFunction( libSym->GetEmbeddedFiles() );
2184 }
2185}
2186
2187
2188std::set<KIFONT::OUTLINE_FONT*> SCHEMATIC::GetFonts() const
2189{
2190 std::set<KIFONT::OUTLINE_FONT*> fonts;
2191
2192 SCH_SHEET_LIST sheetList = Hierarchy();
2193
2194 for( const SCH_SHEET_PATH& sheet : sheetList )
2195 {
2196 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
2197 {
2198 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
2199 {
2200 KIFONT::FONT* font = text->GetFont();
2201
2202 if( !font || font->IsStroke() )
2203 continue;
2204
2205 using EMBEDDING_PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
2206 KIFONT::OUTLINE_FONT* outline = static_cast<KIFONT::OUTLINE_FONT*>( font );
2207
2208 if( outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::EDITABLE
2209 || outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::INSTALLABLE )
2210 {
2211 fonts.insert( outline );
2212 }
2213 }
2214 }
2215 }
2216
2217 return fonts;
2218}
2219
2220
2222{
2223 std::set<KIFONT::OUTLINE_FONT*> fonts = GetFonts();
2224
2225 for( KIFONT::OUTLINE_FONT* font : fonts )
2226 {
2227 EMBEDDED_FILES::EMBEDDED_FILE* file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
2228
2229 if( !file )
2230 {
2231 wxLogTrace( "EMBED", "Failed to add font file: %s", font->GetFileName() );
2232 continue;
2233 }
2234
2236 }
2237}
2238
2239
2240std::set<const SCH_SCREEN*> SCHEMATIC::GetSchematicsSharedByMultipleProjects() const
2241{
2242 std::set<const SCH_SCREEN*> retv;
2243
2244 wxCHECK( m_rootSheet, retv );
2245
2246 SCH_SHEET_LIST hierarchy( m_rootSheet );
2247 SCH_SCREENS screens( m_rootSheet );
2248
2249 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2250 {
2251 for( const SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
2252 {
2253 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
2254
2255 const std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = symbol->GetInstances();
2256
2257 for( const SCH_SYMBOL_INSTANCE& instance : symbolInstances )
2258 {
2259 if( !hierarchy.HasPath( instance.m_Path ) )
2260 {
2261 retv.insert( screen );
2262 break;
2263 }
2264 }
2265
2266 if( retv.count( screen ) )
2267 break;
2268 }
2269 }
2270
2271 return retv;
2272}
2273
2274
2276{
2277 wxCHECK( m_rootSheet, false );
2278
2279 SCH_SCREENS screens( m_rootSheet );
2280
2281 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2282 {
2283 wxCHECK2( screen, continue );
2284
2285 if( screen->GetRefCount() > 1 )
2286 return true;
2287 }
2288
2289 return false;
2290}
2291
2292
2293void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
2294{
2295 SCH_SELECTION_TOOL* selectionTool = m_schematicHolder ? m_schematicHolder->GetSelectionTool() : nullptr;
2296 std::vector<SCH_LINE*> lines;
2297 std::vector<SCH_JUNCTION*> junctions;
2298 std::vector<SCH_NO_CONNECT*> ncs;
2299 std::vector<SCH_ITEM*> items_to_remove;
2300 std::unordered_set<SCH_LINE*> generatedLines;
2301 std::vector<std::unique_ptr<SCH_LINE>> retiredLines;
2302 bool changed = true;
2303
2304 if( aScreen == nullptr )
2305 aScreen = GetCurrentScreen();
2306
2307 auto remove_item =
2308 [&]( SCH_ITEM* aItem ) -> void
2309 {
2310 changed = true;
2311
2312 if( !( aItem->GetFlags() & STRUCT_DELETED ) )
2313 {
2314 aItem->SetFlags( STRUCT_DELETED );
2315
2316 if( aItem->IsSelected() && selectionTool )
2317 selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ );
2318
2319 if( m_schematicHolder )
2320 m_schematicHolder->RemoveFromScreen( aItem, aScreen );
2321 else
2322 aScreen->Remove( aItem );
2323
2324 aCommit->RemovedForCleanup( aItem, aScreen );
2325
2326 if( aItem->Type() == SCH_LINE_T && generatedLines.erase( static_cast<SCH_LINE*>( aItem ) )
2327 && !aCommit->GetStatus( aItem, aScreen ) )
2328 {
2329 // An intermediate merge has no undo owner when its Add/Remove entries cancel.
2330 retiredLines.emplace_back( static_cast<SCH_LINE*>( aItem ) );
2331 }
2332 }
2333 };
2334
2335 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
2336 {
2337 if( !aScreen->IsExplicitJunctionAllowed( item->GetPosition() ) )
2338 {
2339 if( item->IsSelected() || item->HasFlag( SELECTED_BY_DRAG ) )
2340 continue;
2341
2342 items_to_remove.push_back( item );
2343 }
2344 else
2345 junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
2346 }
2347
2348 for( SCH_ITEM* item : items_to_remove )
2349 remove_item( item );
2350
2351 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
2352 ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
2353
2354 alg::for_all_pairs( junctions.begin(), junctions.end(),
2355 [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
2356 {
2357 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
2358 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
2359 {
2360 return;
2361 }
2362
2363 if( aFirst->GetPosition() == aSecond->GetPosition() )
2364 remove_item( aSecond );
2365 } );
2366
2367 alg::for_all_pairs( ncs.begin(), ncs.end(),
2368 [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
2369 {
2370 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
2371 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
2372 {
2373 return;
2374 }
2375
2376 if( aFirst->GetPosition() == aSecond->GetPosition() )
2377 remove_item( aSecond );
2378 } );
2379
2380
2381 auto minX =
2382 []( const SCH_LINE* l )
2383 {
2384 return std::min( l->GetStartPoint().x, l->GetEndPoint().x );
2385 };
2386
2387 auto maxX =
2388 []( const SCH_LINE* l )
2389 {
2390 return std::max( l->GetStartPoint().x, l->GetEndPoint().x );
2391 };
2392
2393 auto minY =
2394 []( const SCH_LINE* l )
2395 {
2396 return std::min( l->GetStartPoint().y, l->GetEndPoint().y );
2397 };
2398
2399 auto maxY =
2400 []( const SCH_LINE* l )
2401 {
2402 return std::max( l->GetStartPoint().y, l->GetEndPoint().y );
2403 };
2404
2405 // Would be nice to put lines in a canonical form here by swapping
2406 // start <-> end as needed but I don't know what swapping breaks.
2407 while( changed )
2408 {
2409 changed = false;
2410 lines.clear();
2411
2412 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
2413 {
2414 if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
2415 lines.push_back( static_cast<SCH_LINE*>( item ) );
2416 }
2417
2418 // Sort by minimum X position
2419 std::sort( lines.begin(), lines.end(),
2420 [&]( const SCH_LINE* a, const SCH_LINE* b )
2421 {
2422 return minX( a ) < minX( b );
2423 } );
2424
2425 for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
2426 {
2427 SCH_LINE* firstLine = *it1;
2428
2429 if( firstLine->GetEditFlags() & STRUCT_DELETED )
2430 continue;
2431
2432 if( firstLine->IsNull() )
2433 {
2434 remove_item( firstLine );
2435 continue;
2436 }
2437
2438 int firstRightXEdge = maxX( firstLine );
2439 auto it2 = it1;
2440
2441 for( ++it2; it2 != lines.end(); ++it2 )
2442 {
2443 SCH_LINE* secondLine = *it2;
2444 int secondLeftXEdge = minX( secondLine );
2445
2446 // impossible to overlap remaining lines
2447 if( secondLeftXEdge > firstRightXEdge )
2448 break;
2449
2450 // No Y axis overlap
2451 if( !( std::max( minY( firstLine ), minY( secondLine ) )
2452 <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) )
2453 {
2454 continue;
2455 }
2456
2457 if( secondLine->GetFlags() & STRUCT_DELETED )
2458 continue;
2459
2460 if( !secondLine->IsParallel( firstLine ) || !secondLine->IsStrokeEquivalent( firstLine )
2461 || secondLine->GetLayer() != firstLine->GetLayer() )
2462 {
2463 continue;
2464 }
2465
2466 // Remove identical lines
2467 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
2468 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
2469 {
2470 remove_item( secondLine );
2471 continue;
2472 }
2473
2474 // See if we can merge an overlap (or two colinear touching segments with
2475 // no junction where they meet).
2476 SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
2477
2478 if( mergedLine != nullptr )
2479 {
2480 remove_item( firstLine );
2481 remove_item( secondLine );
2482
2483 if( m_schematicHolder )
2484 {
2485 m_schematicHolder->AddToScreen( mergedLine, aScreen );
2486 }
2487 else
2488 {
2489 aScreen->Append( mergedLine );
2490 }
2491
2492 generatedLines.insert( mergedLine );
2493 aCommit->Added( mergedLine, aScreen );
2494
2495 if( selectionTool && ( firstLine->IsSelected() || secondLine->IsSelected() ) )
2496 selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
2497
2498 break;
2499 }
2500 }
2501 }
2502 }
2503}
2504
2505
2507 const std::set<SCH_SCREEN*>& aLocalScreens )
2508{
2510 PROF_TIMER timer;
2511
2512 if( aCleanupFlags == LOCAL_CLEANUP )
2513 {
2514 if( aLocalScreens.empty() )
2515 CleanUp( aCommit, GetCurrentScreen() );
2516 else
2517 {
2518 for( SCH_SCREEN* screen : aLocalScreens )
2519 CleanUp( aCommit, screen );
2520 }
2521 }
2522 else if( aCleanupFlags == GLOBAL_CLEANUP )
2523 {
2524 std::unordered_set<SCH_SCREEN*> cleanedScreens;
2525
2526 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
2527 {
2528 SCH_SCREEN* screen = sheet.LastScreen();
2529
2530 if( cleanedScreens.insert( screen ).second )
2531 {
2532 screen->BumpConnectivityRevision();
2533 CleanUp( aCommit, screen );
2534 }
2535 }
2536 }
2537
2538 timer.Stop();
2539 wxLogTrace( "CONN_PROFILE", "SchematicCleanUp() %0.4f ms", timer.msecs() );
2540
2541 if( Settings().m_IntersheetRefsShow )
2543}
2544
2545
2546void SCHEMATIC::RebuildConnectivity( std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2547 PROGRESS_REPORTER* aProgressReporter,
2548 KIGFX::SCH_VIEW* aSchView )
2549{
2550 // Some importers populate screens after installing the hierarchy
2551 if( m_textVarAdapter )
2552 m_textVarAdapter->MarkHierarchyChanged();
2553
2555 m_project->GetProjectFile().NetSettings()->ClearAllCaches();
2556 std::unordered_set<SCH_SCREEN*> screens;
2557
2558 for( const SCH_SHEET_PATH& path : Hierarchy() )
2559 {
2560 if( SCH_SCREEN* screen = path.LastScreen() )
2561 screens.insert( screen );
2562 }
2563
2564 SCH_RULE_AREA::UpdateRuleAreasInScreens( screens, aSchView );
2565
2566 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
2567 {
2568 m_connectivity->Recalculate( *this, true,
2569 aChangedItemHandler ? *aChangedItemHandler
2570 : std::function<void( SCH_ITEM* )>() );
2571 }
2572 else
2573 {
2574 ConnectionGraph()->Recalculate( Hierarchy(), true, aChangedItemHandler, aProgressReporter );
2575 }
2576}
2577
2578
2580 TOOL_MANAGER* aToolManager, PROGRESS_REPORTER* aProgressReporter,
2581 KIGFX::SCH_VIEW* aSchView,
2582 std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2583 PICKED_ITEMS_LIST* aLastChangeList, bool aCleanupDone )
2584{
2585 SCH_COMMIT localCommit( aToolManager );
2586
2587 if( !aCommit )
2588 aCommit = &localCommit;
2589
2590 if( !aCleanupDone )
2591 CleanUpConnections( aCommit, aCleanupFlags );
2592
2593 SCH_SHEET_LIST list = Hierarchy();
2594
2595 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
2596 {
2597 if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP )
2598 {
2599 if( !localCommit.Empty() )
2600 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2601
2602 RebuildConnectivity( aChangedItemHandler, aProgressReporter, aSchView );
2603 return;
2604 }
2605
2606 std::unordered_set<SCH_SCREEN*> screens;
2607
2608 for( const SCH_SHEET_PATH& path : list )
2609 screens.insert( path.LastScreen() );
2610
2611 SCH_RULE_AREA::UpdateRuleAreasInScreens( screens, aSchView );
2612
2613 // Commit cleanup before callbacks, which may close or replace the schematic
2614 if( !localCommit.Empty() )
2615 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2616
2617 m_connectivity->Recalculate( *this, false, aChangedItemHandler ? *aChangedItemHandler
2618 : std::function<void( SCH_ITEM* )>() );
2619 return;
2620 }
2621
2622 if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP
2623 || aLastChangeList == nullptr || ConnectionGraph()->IsMinor() )
2624 {
2625 if( !localCommit.Empty() )
2626 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2627
2628 RebuildConnectivity( aChangedItemHandler, aProgressReporter, aSchView );
2629 return;
2630 }
2631 else
2632 {
2633 struct CHANGED_ITEM
2634 {
2635 SCH_ITEM* item;
2636 SCH_ITEM* linked_item;
2637 SCH_SCREEN* screen;
2638 };
2639
2640 // Final change sets
2641 std::set<SCH_ITEM*> changed_items;
2642 std::set<VECTOR2I> pts;
2643 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> item_paths;
2644
2645 // Working change sets
2646 std::unordered_set<SCH_SCREEN*> changed_screens;
2647 std::set<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> changed_rule_areas;
2648 std::vector<CHANGED_ITEM> changed_connectable_items;
2649
2650 // Lambda to add an item to the connectivity update sets
2651 auto addItemToChangeSet =
2652 [&changed_items, &pts, &item_paths]( CHANGED_ITEM itemData )
2653 {
2654 std::vector<SCH_SHEET_PATH>& paths = itemData.screen->GetClientSheetPaths();
2655
2656 std::vector<VECTOR2I> tmp_pts = itemData.item->GetConnectionPoints();
2657 pts.insert( tmp_pts.begin(), tmp_pts.end() );
2658 changed_items.insert( itemData.item );
2659
2660 for( SCH_SHEET_PATH& path : paths )
2661 item_paths.insert( std::make_pair( path, itemData.item ) );
2662
2663 if( !itemData.linked_item || !itemData.linked_item->IsConnectable() )
2664 return;
2665
2666 tmp_pts = itemData.linked_item->GetConnectionPoints();
2667 pts.insert( tmp_pts.begin(), tmp_pts.end() );
2668 changed_items.insert( itemData.linked_item );
2669
2670 // We have to directly add the pins here because the link may not exist on the schematic
2671 // anymore and so won't be picked up by GetScreen()->Items().Overlapping() below.
2672 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( itemData.linked_item ) )
2673 {
2674 std::vector<SCH_PIN*> pins = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
2675 changed_items.insert( pins.begin(), pins.end() );
2676 }
2677
2678 for( SCH_SHEET_PATH& path : paths )
2679 item_paths.insert( std::make_pair( path, itemData.linked_item ) );
2680 };
2681
2682 // Get all changed connectable items and determine all changed screens
2683 for( unsigned ii = 0; ii < aLastChangeList->GetCount(); ++ii )
2684 {
2685 switch( aLastChangeList->GetPickedItemStatus( ii ) )
2686 {
2687 // Only care about changed, new, and deleted items, the other
2688 // cases are not connectivity-related
2689 case UNDO_REDO::CHANGED:
2690 case UNDO_REDO::NEWITEM:
2691 case UNDO_REDO::DELETED: break;
2692
2693 default: continue;
2694 }
2695
2696 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItem( ii ) );
2697
2698 if( item )
2699 {
2700 SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( aLastChangeList->GetScreenForItem( ii ) );
2701 changed_screens.insert( screen );
2702
2703 if( item->Type() == SCH_RULE_AREA_T )
2704 {
2705 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( item );
2706 changed_rule_areas.insert( { ruleArea, screen } );
2707 }
2708 else if( item->IsConnectable() )
2709 {
2710 SCH_ITEM* linked_item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItemLink( ii ) );
2711 changed_connectable_items.push_back( { item, linked_item, screen } );
2712 }
2713 }
2714 }
2715
2716 // Update rule areas in changed screens to propagate any directive connectivity changes
2717 std::vector<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> forceUpdateRuleAreas =
2718 SCH_RULE_AREA::UpdateRuleAreasInScreens( changed_screens, aSchView );
2719
2720 std::for_each( forceUpdateRuleAreas.begin(), forceUpdateRuleAreas.end(),
2721 [&]( std::pair<SCH_RULE_AREA*, SCH_SCREEN*>& updatedRuleArea )
2722 {
2723 changed_rule_areas.insert( updatedRuleArea );
2724 } );
2725
2726 // If a SCH_RULE_AREA was changed, we need to add all past and present contained items to
2727 // update their connectivity
2728 std::unordered_map<KIID, EDA_ITEM*> itemMap;
2729 list.FillItemMap( itemMap );
2730
2731 auto addPastAndPresentContainedItems =
2732 [&]( SCH_RULE_AREA* changedRuleArea, SCH_SCREEN* screen )
2733 {
2734 for( const KIID& pastItem : changedRuleArea->GetPastContainedItems() )
2735 {
2736 if( itemMap.contains( pastItem ) )
2737 addItemToChangeSet( { static_cast<SCH_ITEM*>( itemMap[pastItem] ), nullptr, screen } );
2738 }
2739
2740 for( SCH_ITEM* containedItem : changedRuleArea->GetContainedItems() )
2741 addItemToChangeSet( { containedItem, nullptr, screen } );
2742 };
2743
2744 for( const auto& [changedRuleArea, screen] : changed_rule_areas )
2745 addPastAndPresentContainedItems( changedRuleArea, screen );
2746
2747 // Add all changed items, and associated items, to the change set
2748 for( CHANGED_ITEM& changed_item : changed_connectable_items )
2749 {
2750 addItemToChangeSet( changed_item );
2751
2752 // If a SCH_DIRECTIVE_LABEL was changed which is attached to a SCH_RULE_AREA, we need
2753 // to add the contained items to the change set to force update of their connectivity
2754 if( changed_item.item->Type() == SCH_DIRECTIVE_LABEL_T )
2755 {
2756 const std::vector<VECTOR2I> labelConnectionPoints = changed_item.item->GetConnectionPoints();
2757
2758 auto candidateRuleAreas = changed_item.screen->Items()
2759 .Overlapping( SCH_RULE_AREA_T, changed_item.item->GetBoundingBox() );
2760
2761 for( SCH_ITEM* candidateRuleArea : candidateRuleAreas )
2762 {
2763 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( candidateRuleArea );
2764
2765 if( ruleArea->GetPolyShape().CollideEdge( labelConnectionPoints[0], nullptr, 5 ) )
2766 addPastAndPresentContainedItems( ruleArea, changed_item.screen );
2767 }
2768 }
2769 }
2770
2771 for( const VECTOR2I& pt : pts )
2772 {
2773 for( SCH_ITEM* item : GetCurrentScreen()->Items().Overlapping( pt ) )
2774 {
2775 // Leave this check in place. Overlapping items are not necessarily connectable.
2776 if( !item->IsConnectable() )
2777 continue;
2778
2779 SCH_SCREEN* screen = GetCurrentScreen();
2780 std::vector<SCH_SHEET_PATH>& paths = screen->GetClientSheetPaths();
2781
2782 if( item->Type() == SCH_LINE_T )
2783 {
2784 if( item->HitTest( pt ) )
2785 {
2786 changed_items.insert( item );
2787
2788 for( SCH_SHEET_PATH& path : paths )
2789 item_paths.insert( std::make_pair( path, item ) );
2790 }
2791 }
2792 else if( item->Type() == SCH_SYMBOL_T && item->IsConnected( pt ) )
2793 {
2794 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2795 std::vector<SCH_PIN*> pins = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
2796
2797 changed_items.insert( pins.begin(), pins.end() );
2798
2799 for( SCH_PIN* pin : pins )
2800 {
2801 for( SCH_SHEET_PATH& path : paths )
2802 item_paths.insert( std::make_pair( path, pin ) );
2803 }
2804 }
2805 else if( item->Type() == SCH_SHEET_T )
2806 {
2807 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
2808
2809 wxCHECK2( sheet, continue );
2810
2811 std::vector<SCH_SHEET_PIN*> sheetPins = sheet->GetPins();
2812 changed_items.insert( sheetPins.begin(), sheetPins.end() );
2813
2814 for( SCH_SHEET_PIN* pin : sheetPins )
2815 {
2816 for( SCH_SHEET_PATH& path : paths )
2817 item_paths.insert( std::make_pair( path, pin ) );
2818 }
2819 }
2820 else
2821 {
2822 if( item->IsConnected( pt ) )
2823 {
2824 changed_items.insert( item );
2825
2826 for( SCH_SHEET_PATH& path : paths )
2827 item_paths.insert( std::make_pair( path, item ) );
2828 }
2829 }
2830 }
2831 }
2832
2833 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> all_items =
2834 ConnectionGraph()->ExtractAffectedItems( changed_items );
2835
2836 all_items.insert( item_paths.begin(), item_paths.end() );
2837
2838 CONNECTION_GRAPH new_graph( this );
2839
2840 new_graph.SetLastCodes( ConnectionGraph() );
2841
2842 std::shared_ptr<NET_SETTINGS> netSettings = m_project->GetProjectFile().NetSettings();
2843
2844 std::set<wxString> affectedNets;
2845
2846 for( auto& [path, item] : all_items )
2847 {
2848 wxCHECK2( item, continue );
2849 item->SetConnectivityDirty();
2850 SCH_CONNECTION* conn = item->Connection();
2851
2852 if( conn )
2853 affectedNets.insert( conn->Name() );
2854 }
2855
2856 // For label items, also capture the old net name from the linked item (original state).
2857 // This ensures cache is cleared for both old and new net names when a label is renamed.
2858 for( const CHANGED_ITEM& changedItem : changed_connectable_items )
2859 {
2860 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( changedItem.item ) )
2861 {
2862 const wxString& driverName = label->GetCachedDriverName();
2863
2864 if( !driverName.IsEmpty() )
2865 affectedNets.insert( driverName );
2866 }
2867
2868 if( SCH_LABEL_BASE* linkedLabel = dynamic_cast<SCH_LABEL_BASE*>( changedItem.linked_item ) )
2869 {
2870 const wxString& driverName = linkedLabel->GetCachedDriverName();
2871
2872 if( !driverName.IsEmpty() )
2873 affectedNets.insert( driverName );
2874 }
2875 }
2876
2877 // Reset resolved netclass cache for this connection
2878 for( const wxString& netName : affectedNets )
2879 netSettings->ClearCacheForNet( netName );
2880
2881 new_graph.Recalculate( list, false, aChangedItemHandler, aProgressReporter );
2882 ConnectionGraph()->Merge( new_graph );
2883 }
2884
2885 if( !localCommit.Empty() )
2886 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2887
2888}
2889
2890
2892{
2893 Reset();
2894
2896
2897 // Create the actual first top-level sheet
2898 SCH_SHEET* rootSheet = new SCH_SHEET( this );
2899 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
2900
2901 rootSheet->SetScreen( rootScreen );
2902 rootSheet->SyncUuidToScreen();
2903 rootScreen->SetFileName( "untitled.kicad_sch" ); // Set default filename to avoid conflicts
2904 rootScreen->SetPageNumber( wxT( "1" ) );
2905
2906 // Don't leave root page number empty
2907 SCH_SHEET_PATH rootSheetPath;
2908 rootSheetPath.push_back( m_rootSheet );
2909 rootSheetPath.push_back( rootSheet );
2910 rootSheetPath.SetPageNumber( wxT( "1" ) );
2911
2912 SetTopLevelSheets( { rootSheet } );
2913}
2914
2915
2916std::vector<SCH_SHEET*> SCHEMATIC::GetTopLevelSheets() const
2917{
2918 return m_topLevelSheets;
2919}
2920
2922{
2923 if( aIndex < 0 )
2924 return nullptr;
2925
2926 size_t index = static_cast<size_t>( aIndex );
2927
2928 if( index >= m_topLevelSheets.size() )
2929 return nullptr;
2930
2931 return m_topLevelSheets[index];
2932}
2933
2934
2935bool SCHEMATIC::IsTopLevelSheetUuid( const KIID& aUuid ) const
2936{
2937 if( !m_rootSheet )
2938 return false;
2939
2940 // A non-virtual root is the only sheet above the hierarchy
2941 if( m_rootSheet->m_Uuid != niluuid )
2942 return aUuid == m_rootSheet->m_Uuid;
2943
2944 for( const SCH_SHEET* sheet : m_topLevelSheets )
2945 {
2946 if( sheet && sheet->m_Uuid == aUuid )
2947 return true;
2948 }
2949
2950 return false;
2951}
2952
2953
2955{
2956 KIID_PATH path = aPath;
2957
2958 if( m_rootSheet && m_rootSheet->m_Uuid == niluuid && !path.empty() && path.front() == niluuid )
2959 path.erase( path.begin() );
2960
2961 return path;
2962}
2963
2964
2966{
2967 const KIID_PATH path = NormalizeInstancePath( aPath );
2968
2969 return !path.empty() && IsTopLevelSheetUuid( path.front() );
2970}
2971
2972
2974{
2975 wxCHECK_RET( aSheet, wxS( "Cannot add null sheet!" ) );
2976 wxCHECK_RET( aSheet->GetScreen(), wxS( "Cannot add virtual root as top-level sheet!" ) );
2977
2979
2980 // Set parent to virtual root
2981 aSheet->SetParent( m_rootSheet );
2982
2983 // Add to the virtual root's screen if it exists
2984 if( m_rootSheet->GetScreen() )
2985 {
2986 m_rootSheet->GetScreen()->Append( aSheet );
2987 }
2988
2989 // Add to our list
2990 m_topLevelSheets.push_back( aSheet );
2991
2993 rebuildHierarchyState( true );
2994}
2995
2997{
2998 auto it = std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet );
2999
3000 if( it == m_topLevelSheets.end() )
3001 return false;
3002
3003 if( m_topLevelSheets.size() == 1 )
3004 return false;
3005
3006 m_topLevelSheets.erase( it );
3007
3008 if( m_rootSheet && m_rootSheet->GetScreen() )
3009 m_rootSheet->GetScreen()->Remove( aSheet, false );
3010
3011 // If we're removing the current sheet, switch to another one
3012 if( !m_currentSheet->empty() && m_currentSheet->at( 0 ) == aSheet )
3013 {
3014 m_currentSheet->clear();
3015 if( !m_topLevelSheets.empty() )
3016 {
3017 m_currentSheet->push_back( m_topLevelSheets[0] );
3018 }
3019 }
3020
3021 rebuildHierarchyState( true );
3022 return true;
3023}
3024
3025
3026bool SCHEMATIC::IsTopLevelSheet( const SCH_SHEET* aSheet ) const
3027{
3028 return std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet ) != m_topLevelSheets.end();
3029}
3030
3031
3033{
3034 SCH_SHEET_LIST hierarchy;
3035
3036 wxLogTrace( traceSchSheetPaths, "BuildSheetListSortedByPageNumbers: %zu top-level sheets",
3037 m_topLevelSheets.size() );
3038
3039 // Can't build hierarchy without top-level sheets
3040 if( m_topLevelSheets.empty() )
3041 return hierarchy;
3042
3043 // For each top-level sheet, build its hierarchy
3044 for( SCH_SHEET* sheet : m_topLevelSheets )
3045 {
3046 if( sheet )
3047 {
3048 wxLogTrace( traceSchSheetPaths, " Top-level sheet: '%s' (UUID=%s, isVirtualRoot=%d)", sheet->GetName(),
3049 sheet->m_Uuid.AsString(), sheet->m_Uuid == niluuid ? 1 : 0 );
3050
3051 // Build the sheet list for this top-level sheet
3052 SCH_SHEET_LIST sheetList;
3053 sheetList.BuildSheetList( sheet, false );
3054
3055 // Add all sheets from this top-level sheet's hierarchy
3056 for( const SCH_SHEET_PATH& path : sheetList )
3057 {
3058 hierarchy.push_back( path );
3059 }
3060 }
3061 }
3062
3063 hierarchy.SortByPageNumbers();
3064
3065 return hierarchy;
3066}
3067
3068
3070{
3071 SCH_SHEET_LIST sheets;
3072
3073 // For each top-level sheet, build its hierarchy
3074 for( SCH_SHEET* sheet : m_topLevelSheets )
3075 {
3076 if( sheet )
3077 {
3078 SCH_SHEET_LIST sheetList;
3079 sheetList.BuildSheetList( sheet, false );
3080
3081 // Add all sheets from this top-level sheet's hierarchy
3082 for( const SCH_SHEET_PATH& path : sheetList )
3083 {
3084 sheets.push_back( path );
3085 }
3086 }
3087 }
3088
3089 return sheets;
3090}
3091
3092
3094{
3095 wxArrayString variantNames;
3096
3097 // There is no default variant name. This is just a place holder for UI controls.
3098 variantNames.Add( GetDefaultVariantName() );
3099
3100 for( const wxString& name : m_variantNames )
3101 variantNames.Add( name );
3102
3103 variantNames.Sort( SortVariantNames ); // SortVariantNames ensures the default is always first
3104
3105 return variantNames;
3106}
3107
3108
3110{
3111 if( m_currentVariant.IsEmpty() || ( m_currentVariant == GetDefaultVariantName() ) )
3112 return wxEmptyString;
3113
3114 return m_currentVariant;
3115}
3116
3117
3118bool SCHEMATIC::HasVariant( const wxString& aVariantName ) const
3119{
3120 for( const wxString& name : m_variantNames )
3121 {
3122 if( name.CmpNoCase( aVariantName ) == 0 )
3123 return true;
3124 }
3125
3126 return false;
3127}
3128
3129
3130void SCHEMATIC::SetCurrentVariant( const wxString& aVariantName )
3131{
3132 wxString newVariant;
3133
3134 // Internally an empty string is the default variant. Set to default if the variant name doesn't exist.
3135 if( ( aVariantName != GetDefaultVariantName() ) && m_variantNames.contains( aVariantName ) )
3136 newVariant = aVariantName;
3137
3138 m_currentVariant = newVariant;
3139
3140 // Variant-specific field values affect text geometry, so bounding box caches computed
3141 // with the previous variant's text are now stale.
3142 if( m_rootSheet )
3143 {
3144 SCH_SCREENS allScreens( m_rootSheet );
3145
3146 for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
3147 {
3148 for( SCH_ITEM* item : screen->Items() )
3149 item->ClearCaches();
3150 }
3151 }
3152
3153 // Variant-driven cross-ref / local-field value changes require a
3154 // reactive fan-out so dependent text items repaint without waiting for
3155 // an unrelated edit to nudge the view.
3156 if( m_textVarAdapter )
3157 m_textVarAdapter->Tracker().InvalidateVariantScoped();
3158}
3159
3160
3161void SCHEMATIC::AddVariant( const wxString& aVariantName )
3162{
3163 m_variantNames.emplace( aVariantName );
3164
3165 // Ensure the variant is registered in the project file
3166 std::map<wxString, wxString>& descriptions = Settings().m_VariantDescriptions;
3167
3168 if( descriptions.find( aVariantName ) == descriptions.end() )
3169 descriptions[aVariantName] = wxEmptyString;
3170}
3171
3172
3173void SCHEMATIC::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit )
3174{
3175 if( aCommit )
3176 {
3177 wxCHECK( m_rootSheet, /* void */ );
3178
3179 SCH_SCREENS allScreens( m_rootSheet );
3180
3181 allScreens.DeleteVariant( aVariantName, aCommit );
3182 }
3183
3184 if( m_currentVariant == aVariantName )
3185 SetCurrentVariant( wxEmptyString );
3186
3187 m_variantNames.erase( aVariantName );
3188 Settings().m_VariantDescriptions.erase( aVariantName );
3189}
3190
3191
3192void SCHEMATIC::RenameVariant( const wxString& aOldName, const wxString& aNewName, SCH_COMMIT* aCommit )
3193{
3194 wxCHECK( m_rootSheet, /* void */ );
3195 wxCHECK( !aOldName.IsEmpty() && !aNewName.IsEmpty(), /* void */ );
3196 wxCHECK( m_variantNames.contains( aOldName ), /* void */ );
3197
3198 m_variantNames.erase( aOldName );
3199 m_variantNames.insert( aNewName );
3200
3201 std::map<wxString, wxString>& descriptions = Settings().m_VariantDescriptions;
3202
3203 if( descriptions.count( aOldName ) )
3204 {
3205 descriptions[aNewName] = descriptions[aOldName];
3206 descriptions.erase( aOldName );
3207 }
3208
3209 // Retarget through SetCurrentVariant so the whole-schematic cache/text-var invalidation runs;
3210 // otherwise ${VARIANT} text on off-sheet items keeps its stale render cache after the rename.
3211 if( m_currentVariant == aOldName )
3212 SetCurrentVariant( aNewName );
3213
3214 SCH_SCREENS allScreens( m_rootSheet );
3215 allScreens.RenameVariant( aOldName, aNewName, aCommit );
3216}
3217
3218
3219void SCHEMATIC::CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
3220 SCH_COMMIT* aCommit )
3221{
3222 wxCHECK( m_rootSheet, /* void */ );
3223 wxCHECK( !aSourceVariant.IsEmpty() && !aNewVariant.IsEmpty(), /* void */ );
3224 wxCHECK( m_variantNames.contains( aSourceVariant ), /* void */ );
3225 wxCHECK( !m_variantNames.contains( aNewVariant ), /* void */ );
3226
3227 AddVariant( aNewVariant );
3228
3229 std::map<wxString, wxString>& descriptions = Settings().m_VariantDescriptions;
3230
3231 if( descriptions.count( aSourceVariant ) )
3232 descriptions[aNewVariant] = descriptions[aSourceVariant];
3233
3234 SCH_SCREENS allScreens( m_rootSheet );
3235 allScreens.CopyVariant( aSourceVariant, aNewVariant, aCommit );
3236}
3237
3238
3239wxString SCHEMATIC::GetVariantDescription( const wxString& aVariantName ) const
3240{
3241 const std::map<wxString, wxString>& descriptions = Settings().m_VariantDescriptions;
3242 auto it = descriptions.find( aVariantName );
3243
3244 if( it != descriptions.end() )
3245 return it->second;
3246
3247 return wxEmptyString;
3248}
3249
3250
3251void SCHEMATIC::SetVariantDescription( const wxString& aVariantName, const wxString& aDescription )
3252{
3253 std::map<wxString, wxString>& descriptions = Settings().m_VariantDescriptions;
3254
3255 if( aDescription.IsEmpty() )
3256 descriptions.erase( aVariantName );
3257 else
3258 descriptions[aVariantName] = aDescription;
3259
3260 if( m_textVarAdapter )
3261 m_textVarAdapter->Tracker().InvalidateVariantScoped();
3262}
3263
3264
3266{
3267 if( m_rootSheet && m_rootSheet->GetScreen() )
3268 {
3269 SCH_SCREENS screens( m_rootSheet );
3270 std::set<wxString> variantNames = screens.GetVariantNames();
3271 m_variantNames.insert( variantNames.begin(), variantNames.end() );
3272
3273 // Register any unknown variants to the project file with empty descriptions
3274 std::map<wxString, wxString>& descriptions = Settings().m_VariantDescriptions;
3275
3276 for( const wxString& name : variantNames )
3277 {
3278 if( descriptions.find( name ) == descriptions.end() )
3279 descriptions[name] = wxEmptyString;
3280 }
3281
3282 // Also include variants from the project file that may not have any diffs yet.
3283 // This ensures newly created variants with no symbol changes are preserved.
3284 for( const auto& [name, description] : descriptions )
3285 m_variantNames.insert( name );
3286 }
3287}
3288
3289
3290void SCHEMATIC::SaveToHistory( const wxString& aProjectPath, std::vector<HISTORY_FILE_DATA>& aFileData )
3291{
3292 if( !IsValid() )
3293 return;
3294
3295 wxString projPath = m_project->GetProjectPath();
3296
3297 if( projPath.IsEmpty() )
3298 return; // no project yet
3299
3300 // Verify we're saving for the correct project
3301 if( !projPath.IsSameAs( aProjectPath ) )
3302 {
3303 wxLogTrace( traceAutoSave, wxS( "[history] sch saver skipping - project path mismatch: %s vs %s" ), projPath,
3304 aProjectPath );
3305 return;
3306 }
3307
3308 if( !projPath.EndsWith( wxFILE_SEP_PATH ) )
3309 projPath += wxFILE_SEP_PATH;
3310
3311 SCH_SHEET_LIST sheetList = Hierarchy();
3312
3314
3315 KICAD_FORMAT::FORMAT_MODE mode = KICAD_FORMAT::FORMAT_MODE::NORMAL;
3316
3317 if( ADVANCED_CFG::GetCfg().m_CompactSave )
3318 mode = KICAD_FORMAT::FORMAT_MODE::COMPACT_TEXT_PROPERTIES;
3319
3320 // In ZIP mode the only caller is the autosave timer, so skipping clean sheets
3321 // avoids spurious _autosave-* files. In INCREMENTAL mode the manual-save flow
3322 // clears dirty flags before calling here, so filtering would skip the whole
3323 // snapshot. Git's diff-against-HEAD check rejects no-op commits there anyway.
3324 bool filterClean = !Pgm().GetCommonSettings()->AutosaveUsesLocalHistory();
3325
3326 for( const SCH_SHEET_PATH& path : sheetList )
3327 {
3328 SCH_SHEET* sheet = path.Last();
3329 SCH_SCREEN* screen = path.LastScreen();
3330
3331 if( !sheet || !screen )
3332 continue;
3333
3334 if( filterClean && !screen->IsContentModified() )
3335 continue;
3336
3337 wxFileName abs = m_project->AbsolutePath( screen->GetFileName() );
3338
3339 if( !abs.IsOk() )
3340 continue;
3341
3342 wxString absPath = abs.GetFullPath();
3343
3344 if( absPath.IsEmpty() || !absPath.StartsWith( projPath ) )
3345 continue;
3346
3347 wxString rel = absPath.Mid( projPath.length() );
3348
3349 try
3350 {
3351 STRING_FORMATTER formatter;
3352 pi.FormatSchematicToFormatter( &formatter, sheet, this );
3353
3354 HISTORY_FILE_DATA entry;
3355 entry.relativePath = rel;
3356 entry.content = std::move( formatter.MutableString() );
3357 entry.prettify = true;
3358 entry.formatMode = mode;
3359 aFileData.push_back( std::move( entry ) );
3360
3361 wxLogTrace( traceAutoSave,
3362 wxS( "[history] sch saver serialized %zu bytes for '%s' -> '%s'" ),
3363 aFileData.back().content.size(), absPath, rel );
3364 }
3365 catch( const IO_ERROR& ioe )
3366 {
3367 wxLogTrace( traceAutoSave, wxS( "[history] sch saver serialize failed for '%s': %s" ),
3368 absPath, wxString::FromUTF8( ioe.What() ) );
3369 }
3370 }
3371}
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:145
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:235
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:599
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
static ERC_EXCLUSION FromProto(const kiapi::schematic::ErcExclusion &aMessage)
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition erc_item.cpp:312
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
Implement outline font drawing.
EMBEDDING_PERMISSION GetEmbeddingPermission() const
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:114
const PIN_MAP_SET & GetPinMaps() const
Pin-to-pad mapping (issue #2282).
Definition lib_symbol.h:266
const std::vector< ASSOCIATED_FOOTPRINT > & GetAssociatedFootprints() const
Definition lib_symbol.h:270
bool IsExcluded() const
Definition marker_base.h:90
std::shared_ptr< RC_ITEM > GetRCItem() const
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:91
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:562
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:148
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:81
virtual void OnSchItemsChanged(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:82
virtual void OnSchSelectionChanged(SCHEMATIC &aSch)
Definition schematic.h:88
virtual void OnSchSheetChanged(SCHEMATIC &aSch)
Definition schematic.h:86
virtual void OnSchItemsAdded(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:80
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:810
std::unique_ptr< SCH_CONNECTIVITY::NETCHAIN_MANAGER > m_netChains
Definition schematic.h:759
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:757
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:783
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:754
std::vector< SCH_SHEET * > m_topLevelSheets
List of top-level sheets (direct children of virtual root)
Definition schematic.h:745
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:322
wxString ConvertKIIDsToRefs(const wxString &aSource) const
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Definition schematic.h:202
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:804
std::map< wxString, std::set< int > > & GetPageRefsMap()
Definition schematic.h:378
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:768
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:736
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:786
SCH_CONNECTIVITY::FACADE & Connectivity() const
Definition schematic.h:324
wxString ConvertRefsToKIIDs(const wxString &aSource) const
void SetProject(PROJECT *aPrj)
bool IsHierarchyModified() const
True when any sheet in the hierarchy has unsaved changes.
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={})
std::map< int, wxString > GetVirtualPageToSheetPagesMap() const
EMBEDDED_FILES * GetEmbeddedFiles() override
PROJECT * m_project
Definition schematic.h:735
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:327
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
SCHEMATIC(PROJECT *aPrj)
Definition schematic.cpp:79
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:802
void InvokeListeners(Func &&aFunc, Args &&... args)
Definition schematic.h:724
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:297
void updateProjectBusAliases()
void SetVariantDescription(const wxString &aVariantName, const wxString &aDescription)
Set the description for a variant.
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)
std::unique_ptr< class SCHEMATIC_TEXT_VAR_ADAPTER > m_textVarAdapter
Reactive text-variable dependency adapter.
Definition schematic.h:814
bool HasProjectBusAliases() const
PROPERTY_LISTENER_SUBSCRIPTION m_fieldListenerSubscription
PROPERTY_MANAGER listener subscription installed in the ctor.
Definition schematic.h:819
void GetContextualTextVars(wxArrayString *aVars) const
void ensureDefaultTopLevelSheet()
SCH_SHEET & Root() const
Definition schematic.h:208
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.
std::unique_ptr< SCH_CONNECTIVITY::FACADE > m_connectivity
Definition schematic.h:758
SCHEMATIC_HOLDER * m_schematicHolder
What currently "Holds" the schematic, i.e.
Definition schematic.h:796
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:791
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:312
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:778
ERC_SETTINGS & ErcSettings() const
std::set< wxString > m_variantNames
Definition schematic.h:806
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:742
bool IsInstancePathInProject(const KIID_PATH &aPath) const
Test whether an instance path is rooted in this schematic's top level sheets.
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
void RemovedForCleanup(SCH_ITEM *aItem, SCH_SCREEN *aScreen)
Retain the pre-edit state of an item already removed from its screen by cleanup.
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
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
SCH_ITEM * Driver() const
FIELD_T GetId() const
Definition sch_field.h:143
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:170
virtual bool IsConnectable() const
Definition sch_item.h:550
int GetUnit() const
Definition sch_item.h:243
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:353
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:683
bool IsParallel(const SCH_LINE *aLine) const
Definition sch_line.cpp:535
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:548
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
Definition sch_marker.h:104
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
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:826
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:226
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
void DecRefCount()
bool IsExplicitJunctionAllowed(const VECTOR2I &aPosition) const
Indicate that a junction dot may be placed at the given location.
void BumpConnectivityRevision(KICAD_T aChangedType=TYPE_NOT_INIT)
Increment the connectivity revisions of this screen.
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:168
void Update(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Refresh a changed item in the tree and invalidate connectivity.
int GetRefCount() const
Definition sch_screen.h:175
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:392
void SyncUuidToScreen()
Take the identity of the screen this sheet owns.
wxString GetName() const
Definition sch_sheet.h:148
SCH_SCREEN * m_screen
Definition sch_sheet.h:700
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:151
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:249
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:135
wxString GetSchSymbolLibraryName() const
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:165
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit, int aBodyStyle) const override
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:184
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 handle Spice way of expressing values (e.g. 10.5 Meg)
Definition spice_value.h:51
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
A text evaluation frame with one frozen clock and memoized external queries.
static ENVIRONMENT * Current()
void RecordCrossReference(const CROSS_REFERENCE_KEY &aKey, const CROSS_REFERENCE_VALUE &aValue)
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:93
@ INTERNAL
Definition common.h:96
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
@ ERCE_VARIANT_SYMBOL_INCOMPATIBLE
Variant symbol override fails pin compatibility.
@ ERCE_ENDPOINT_OFF_GRID
Pin or wire-end off grid.
@ ERCE_BUS_TO_BUS_CONFLICT
A connection between bus objects doesn't share at least one net.
@ ERCE_FOOTPRINT_LINK_ISSUES
The footprint link is invalid, or points to a missing (or inactive) footprint or library.
@ ERCE_BUS_TO_NET_CONFLICT
A bus wire is graphically connected to a net port/pin (or vice versa).
@ ERCE_NOCONNECT_NOT_CONNECTED
A no connect symbol is not connected to anything.
@ ERCE_FOUR_WAY_JUNCTION
A four-way junction was found.
@ ERCE_PIN_NOT_CONNECTED
Pin not connected and not no connect symbol.
@ ERCE_UNDEFINED_NETCLASS
A netclass was referenced but not defined.
@ ERCE_UNRESOLVED_VARIABLE
A text variable could not be resolved.
@ ERCE_SIMULATION_MODEL
An error was found in the simulation model.
@ ERCE_LIB_SYMBOL_MISMATCH
Symbol doesn't match copy in library.
@ ERCE_GENERIC_ERROR
@ ERCE_DIFFERENT_UNIT_FP
Different units of the same symbol have different footprints assigned.
@ ERCE_NOCONNECT_CONNECTED
A no connect symbol is connected to more than 1 pin.
@ ERCE_DUPLICATE_SHEET_NAME
Duplicate sheet names within a given sheet.
@ ERCE_LIB_SYMBOL_ISSUES
Symbol not found in active libraries.
@ ERCE_FOOTPRINT_FILTERS
The assigned footprint doesn't match the footprint filters.
@ ERCE_HIERACHICAL_LABEL
Mismatch between hierarchical labels and pins sheets.
@ ERCE_WIRE_DANGLING
Some wires are not connected to anything else.
@ ERCE_GENERIC_WARNING
@ ERCE_VARIANT_SYMBOL_INVALID
Variant symbol override LIB_ID cannot be resolved.
@ ERCE_PIN_MAP_UNMAPPED_PIN
A connected pin resolves to no footprint pad.
@ ERCE_LABEL_MULTIPLE_WIRES
A label is connected to more than one wire.
bool m_ConnectivityEngine
Use the schematic connectivity engine instead of CONNECTION_GRAPH.
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.
std::string source
void ignore_unused(const T &)
Definition ignore.h:20
KIID niluuid(0)
@ LAYER_WIRE
Definition layer_ids.h:480
@ LAYER_BUS
Definition layer_ids.h:481
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)
Macro to generate unique identifier for a type.
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:75
@ SYMBOL_FILTER_NON_POWER
SCH_CLEANUP_FLAGS
Definition schematic.h:92
@ LOCAL_CLEANUP
Definition schematic.h:94
@ GLOBAL_CLEANUP
Definition schematic.h:95
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:109
Pins of type no-connect that touch other items at one position.
A wire end, a bus entry end or a symbol pin that is not on the connection grid.
A simple container for schematic symbol instance information.
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
@ 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
VECTOR2I location
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_LABEL_T
Definition typeinfo.h:163
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_MARKER_T
Definition typeinfo.h:154
@ SCH_RULE_AREA_T
Definition typeinfo.h:166
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCHEMATIC_T
Definition typeinfo.h:200
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_JUNCTION_T
Definition typeinfo.h:155
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708