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
601
602
603void SCHEMATIC::GetContextualTextVars( wxArrayString* aVars ) const
604{
605 auto add = [&]( const wxString& aVar )
606 {
607 if( !alg::contains( *aVars, aVar ) )
608 aVars->push_back( aVar );
609 };
610
611 add( wxT( "#" ) );
612 add( wxT( "##" ) );
613 add( wxT( "SHEETPATH" ) );
614 add( wxT( "SHEETNAME" ) );
615 add( wxT( "FILENAME" ) );
616 add( wxT( "FILEPATH" ) );
617 add( wxT( "PROJECTNAME" ) );
618 add( wxT( "VARIANT" ) );
619 add( wxT( "VARIANT_DESC" ) );
620
621 if( !CurrentSheet().empty() )
623
624 for( std::pair<wxString, wxString> entry : m_project->GetTextVars() )
625 add( entry.first );
626}
627
628
629bool SCHEMATIC::ResolveTextVar( const SCH_SHEET_PATH* aSheetPath, wxString* token, int aDepth ) const
630{
631 wxCHECK( aSheetPath, false );
632
633 if( token->IsSameAs( wxT( "#" ) ) )
634 {
635 *token = aSheetPath->GetPageNumber();
636 return true;
637 }
638 else if( token->IsSameAs( wxT( "##" ) ) )
639 {
640 int i = 0;
641
642 for( SCH_SHEET* sheet : GetTopLevelSheets() )
643 i += sheet->CountSheets();
644
645 *token = wxString::Format( "%i", i );
646 return true;
647 }
648 else if( token->IsSameAs( wxT( "SHEETPATH" ) ) )
649 {
650 *token = aSheetPath->PathHumanReadable();
651 return true;
652 }
653 else if( token->IsSameAs( wxT( "SHEETNAME" ) ) )
654 {
655 *token = aSheetPath->Last()->GetName();
656 return true;
657 }
658 else if( token->IsSameAs( wxT( "FILENAME" ) ) )
659 {
660 wxFileName fn( GetFileName() );
661 *token = fn.GetFullName();
662 return true;
663 }
664 else if( token->IsSameAs( wxT( "FILEPATH" ) ) )
665 {
666 wxFileName fn( GetFileName() );
667 *token = fn.GetFullPath();
668 return true;
669 }
670 else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) )
671 {
672 *token = m_project->GetProjectName();
673 return true;
674 }
675 else if( token->IsSameAs( wxT( "VARIANTNAME" ) ) || token->IsSameAs( wxT( "VARIANT" ) ) )
676 {
677 *token = m_currentVariant;
678 return true;
679 }
680 else if( token->IsSameAs( wxT( "VARIANT_DESC" ) ) )
681 {
683 return true;
684 }
685
686 // aSheetPath->LastScreen() can be null during schematic loading
687 if( aSheetPath->LastScreen()
688 && aSheetPath->LastScreen()->GetTitleBlock().TextVarResolver( token, m_project, INTERNAL ) )
689 {
690 return true;
691 }
692
693 if( m_project->TextVarResolver( token ) )
694 return true;
695
696 return false;
697}
698
699
701{
702 // With virtual root pattern, m_rootSheet is the virtual root with no file
703 // Return filename from first top-level sheet if available
704 if( !IsValid() )
705 return wxString( wxEmptyString );
706
707 if( !m_topLevelSheets.empty() && m_topLevelSheets[0]->GetScreen() )
708 return m_topLevelSheets[0]->GetScreen()->GetFileName();
709
710 return wxString( wxEmptyString );
711}
712
713
715{
716 if( !m_project )
717 {
718 static SCHEMATIC_SETTINGS defaultSettings( nullptr, "schematic" );
719 return defaultSettings;
720 }
721 wxASSERT( m_project );
722 return *m_project->GetProjectFile().m_SchematicSettings;
723}
724
725
727{
728 wxASSERT( m_project );
729 return *m_project->GetProjectFile().m_ErcSettings;
730}
731
732
733std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
734{
735 SCH_SHEET_LIST sheetList = Hierarchy();
736 ERC_SETTINGS& settings = ErcSettings();
737
740
741 // Child exclusions need the loaded hierarchy to recover nonpersistent item IDs.
742 for( const auto& [markerData, comment] : settings.m_ErcExclusionsLegacy )
743 {
744 ERC_EXCLUSION exclusion = ERC_EXCLUSION::FromLegacyStrings( sheetList, markerData, comment );
745
746 if( !exclusion.GetSortKey().empty() )
747 {
748 // Legacy format can sometimes have the same exclusion multiple times,
749 // without and with a comment. If this happens, replace the existing one
750 // if we can go from no comment to comment
751 auto [it, inserted] = settings.m_ErcExclusions.insert( exclusion );
752
753 if( !inserted && !comment.empty() && it->GetComment().empty() )
754 {
755 ERC_EXCLUSION updated = *it;
756 updated.SetComment( comment );
757 settings.m_ErcExclusions.erase( it );
758 settings.m_ErcExclusions.insert( updated );
759 }
760 }
761 }
762
763 settings.m_ErcExclusionsLegacy.clear();
764
765 using GROUP_ANCHOR = std::variant<KIID, std::pair<int, int>>;
766 using GROUP_KEY = std::tuple<int, KIID_PATH, GROUP_ANCHOR>;
767 std::map<GROUP_KEY, SCH_MARKER*> groupMarkers;
768 using NC_GROUPS = std::map<std::pair<KIID_PATH, VECTOR2I>, SCH_CONNECTIVITY::NO_CONNECT_PIN_CONFLICT,
770 std::optional<NC_GROUPS> ncGroups;
771 std::optional<std::map<GROUP_KEY, KIID>> ncFlagGroups;
772 std::optional<std::map<GROUP_KEY, KIID>> hierarchyGroups;
773 std::optional<std::vector<SCH_CONNECTIVITY::LABEL_WIRE_CONFLICT>> labelWireGroups;
774 std::optional<std::vector<SCH_CONNECTIVITY::FOUR_WAY_JUNCTION>> fourWayGroups;
775 const auto groupKey = [&]( const SCH_MARKER& aMarker ) -> std::optional<GROUP_KEY>
776 {
777 const auto error = std::static_pointer_cast<ERC_ITEM>( aMarker.GetRCItem() );
778
779 const bool ncPin = error->GetErrorCode() == ERCE_NOCONNECT_CONNECTED
780 && !error->MainItemHasSheetPath() && !error->AuxItemHasSheetPath();
781
782 static const std::set<int> groupedCodes = { ERCE_WIRE_DANGLING, ERCE_BUS_TO_NET_CONFLICT,
787
788 if( !ADVANCED_CFG::GetCfg().m_ConnectivityEngine || !groupedCodes.contains( error->GetErrorCode() )
789 || !error->IsSheetSpecific() )
790 {
791 return std::nullopt;
792 }
793
794 if( error->GetErrorCode() == ERCE_FOUR_WAY_JUNCTION )
795 {
796 if( error->MainItemHasSheetPath() || error->AuxItemHasSheetPath() )
797 return std::nullopt;
798
799 if( !fourWayGroups )
800 fourWayGroups = m_connectivity->Engine().FourWayJunctions();
801
802 const KIID_PATH& path = error->GetSpecificSheetPath().PathRef();
803 const VECTOR2I position = aMarker.GetPosition();
804 const auto group = std::lower_bound( fourWayGroups->begin(), fourWayGroups->end(),
805 std::tie( path, position.x, position.y ),
806 []( const auto& junction, const auto& location )
807 {
808 return std::tie( junction.sheet, junction.position.x, junction.position.y ) < location;
809 } );
810
811 if( group == fourWayGroups->end() || group->sheet != path || group->position != position )
812 return std::nullopt;
813
814 for( const KIID& id : error->GetIDs() )
815 {
816 if( !std::binary_search( group->equivalentItems.begin(), group->equivalentItems.end(), id ) )
817 return std::nullopt;
818 }
819
820 return GROUP_KEY{ ERCE_FOUR_WAY_JUNCTION, path, std::pair{ position.x, position.y } };
821 }
822
823 const SCH_ITEM* main = ResolveItem( error->GetMainItemID(), nullptr, true );
824
825 if( !main || main->GetPosition() != aMarker.GetPosition() )
826 return std::nullopt;
827
828 if( error->GetErrorCode() == ERCE_PIN_NOT_CONNECTED )
829 {
830 if( main->Type() != SCH_PIN_T )
831 return std::nullopt;
832
833 // Power-symbol errors identify individual pins, not a canonical witness for an island
834 if( static_cast<const SCH_PIN*>( main )->GetParentSymbol()->IsPower() )
835 return std::nullopt;
836 }
837
838 const KIID_PATH& path = error->GetSpecificSheetPath().PathRef();
839 const auto instance = m_connectivity->Keys().FindInstance( path );
840
841 if( !instance )
842 return std::nullopt;
843
844 if( error->GetErrorCode() == ERCE_LABEL_MULTIPLE_WIRES )
845 {
846 if( ( main->Type() != SCH_LABEL_T && main->Type() != SCH_GLOBAL_LABEL_T
847 && main->Type() != SCH_HIER_LABEL_T )
848 || error->MainItemHasSheetPath() || error->AuxItemHasSheetPath() )
849 return std::nullopt;
850
851 if( !labelWireGroups )
852 labelWireGroups = m_connectivity->Engine().LabelWireConflicts();
853
854 const auto group = std::find_if( labelWireGroups->begin(), labelWireGroups->end(),
855 [&]( const auto& conflict )
856 {
857 return conflict.sheet == path && conflict.position == aMarker.GetPosition();
858 } );
859
860 if( group == labelWireGroups->end() )
861 return std::nullopt;
862
863 for( const KIID& id : error->GetIDs() )
864 {
865 if( id != main->m_Uuid && !std::binary_search( group->wires.begin(), group->wires.end(), id ) )
866 return std::nullopt;
867 }
868
869 return GROUP_KEY{ ERCE_LABEL_MULTIPLE_WIRES, path, group->label };
870 }
871
872 if( error->GetErrorCode() == ERCE_HIERACHICAL_LABEL )
873 {
874 if( !error->MainItemHasSheetPath() || error->AuxItemHasSheetPath()
875 || error->GetMainItemSheetPath().PathRef() != path )
876 return std::nullopt;
877
878 if( !hierarchyGroups )
879 {
880 hierarchyGroups.emplace();
881
882 for( const auto& diagnostic : m_connectivity->Engine().HierarchyErrors() )
883 {
884 for( const KIID& id : diagnostic.equivalentItems )
885 hierarchyGroups->emplace( GROUP_KEY{ ERCE_HIERACHICAL_LABEL, diagnostic.sheet, id },
886 diagnostic.item );
887 }
888 }
889
890 const auto found = hierarchyGroups->find( { ERCE_HIERACHICAL_LABEL, path, main->m_Uuid } );
891
892 if( found == hierarchyGroups->end() )
893 return std::nullopt;
894
895 return GROUP_KEY{ ERCE_HIERACHICAL_LABEL, path, found->second };
896 }
897
898 if( ncPin )
899 {
900 if( !ncGroups )
901 {
902 ncGroups.emplace();
903
904 for( auto& group : m_connectivity->Engine().NoConnectPinConflicts() )
905 ncGroups->emplace( std::make_pair( group.sheet, group.position ), std::move( group ) );
906 }
907
908 const auto found = ncGroups->find( { path, aMarker.GetPosition() } );
909
910 if( found == ncGroups->end() )
911 return std::nullopt;
912
913 const auto& group = found->second;
914 const auto isPin = [&]( const KIID& id )
915 {
916 return std::binary_search( group.pins.begin(), group.pins.end(), id );
917 };
918
919 if( !isPin( error->GetMainItemID() ) )
920 return std::nullopt;
921
922 for( const KIID& id : error->GetIDs() )
923 {
924 if( id == niluuid )
925 continue;
926
927 if( !m_connectivity->GetSubgraphForItem( id, path )
928 || ( !isPin( id ) && !std::binary_search( group.others.begin(), group.others.end(), id ) ) )
929 return std::nullopt;
930 }
931
932 return GROUP_KEY{ error->GetErrorCode(), path, group.pins.front() };
933 }
934
935 if( error->GetErrorCode() == ERCE_NOCONNECT_CONNECTED
936 || error->GetErrorCode() == ERCE_NOCONNECT_NOT_CONNECTED )
937 {
938 if( !error->MainItemHasSheetPath() || error->AuxItemHasSheetPath()
939 || error->GetMainItemSheetPath().PathRef() != path )
940 return std::nullopt;
941
942 KIID flag = niluuid;
943
944 for( const KIID& id : error->GetIDs() )
945 {
946 const SCH_ITEM* item = ResolveItem( id, nullptr, true );
947
948 if( item && item->Type() == SCH_NO_CONNECT_T )
949 flag = id;
950 }
951
952 const auto& islandOf = m_connectivity->Published().Auxiliary().IslandOf();
953 const auto flagIsland = islandOf.find( { flag, *instance } );
954
955 if( flagIsland == islandOf.end() )
956 return std::nullopt;
957
958 if( !ncFlagGroups )
959 {
960 ncFlagGroups.emplace();
961
962 for( const auto& diagnostic : m_connectivity->Engine().NoConnectFlagErrors() )
963 {
964 const auto inst = m_connectivity->Keys().FindInstance( diagnostic.sheet );
965 const auto record = inst ? islandOf.find( { diagnostic.flag, *inst } ) : islandOf.end();
966
967 if( record == islandOf.end() )
968 continue;
969
970 const int code = diagnostic.connected ? ERCE_NOCONNECT_CONNECTED : ERCE_NOCONNECT_NOT_CONNECTED;
971 ncFlagGroups->emplace( GROUP_KEY{ code, diagnostic.sheet, record->second.anchor },
972 diagnostic.flag );
973 }
974 }
975
976 const auto group = ncFlagGroups->find( { error->GetErrorCode(), path, flagIsland->second.anchor } );
977
978 if( group == ncFlagGroups->end() )
979 return std::nullopt;
980
981 const auto& rows = m_connectivity->Published().Rows();
982 const auto flagRow = rows.find( { flag, *instance } );
983
984 if( flagRow == rows.end() )
985 return std::nullopt;
986
987 for( const KIID& id : error->GetIDs() )
988 {
989 if( id == niluuid || id == flag )
990 continue;
991
992 const SCH_ITEM* item = ResolveItem( id, nullptr, true );
993 const auto row = rows.find( { id, *instance } );
994
995 if( !item || item->Type() != SCH_PIN_T || row == rows.end()
996 || row->second.component != flagRow->second.component
997 || !m_connectivity->GetSubgraphForItem( id, path ) )
998 return std::nullopt;
999 }
1000
1001 return GROUP_KEY{ error->GetErrorCode(), path, group->second };
1002 }
1003
1004 const auto& islands = m_connectivity->Published().Auxiliary().IslandOf();
1005 std::optional<GROUP_KEY> key;
1006
1007 for( const KIID& id : error->GetIDs() )
1008 {
1009 if( id == niluuid )
1010 continue;
1011
1012 const auto island = islands.find( { id, *instance } );
1013
1014 if( !m_connectivity->GetSubgraphForItem( id, path ) || island == islands.end()
1015 || ( key && std::get<2>( *key ) != GROUP_ANCHOR{ island->second.anchor } ) )
1016 return std::nullopt;
1017
1018 key = GROUP_KEY{ error->GetErrorCode(), path, island->second.anchor };
1019 }
1020
1021 return key;
1022 };
1023
1024 std::set<ERC_EXCLUSION, ERC_EXCLUSION_COMPARE> legacyPathlessExclusions;
1025 std::optional<std::vector<SCH_CONNECTIVITY::OFF_GRID_ENDPOINT>> offGridEndpoints;
1026 const auto findPathless = [&]( const ERC_EXCLUSION& aLookup )
1027 {
1028 auto legacy = aLookup.ToProto();
1029 legacy.mutable_marker()->clear_sheet_specific_path();
1030 legacy.mutable_marker()->clear_main_item_sheet_path();
1031 legacy.mutable_marker()->clear_aux_item_sheet_path();
1032 auto found = settings.m_ErcExclusions.find( ERC_EXCLUSION::FromProto( legacy ) );
1033
1034 if( found != settings.m_ErcExclusions.end() )
1035 legacyPathlessExclusions.insert( *found );
1036
1037 return found;
1038 };
1039
1040 for( const SCH_SHEET_PATH& sheet : sheetList )
1041 {
1042 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
1043 {
1044 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
1045 ERC_EXCLUSION lookup = ERC_EXCLUSION::FromMarker( *marker );
1046 auto it = settings.m_ErcExclusions.find( lookup );
1047
1048 const auto error = std::static_pointer_cast<ERC_ITEM>( marker->GetRCItem() );
1049
1050 if( it == settings.m_ErcExclusions.end() && !settings.m_ErcExclusions.empty() && error->IsSheetSpecific()
1051 && error->GetSpecificSheetPath().PathRef() == sheet.PathRef() )
1052 {
1053 const bool engine = ADVANCED_CFG::GetCfg().m_ConnectivityEngine;
1054 const bool hasMain = error->MainItemHasSheetPath();
1055 const bool hasAux = error->AuxItemHasSheetPath();
1056 const bool mainHere = hasMain && error->GetMainItemSheetPath().PathRef() == sheet.PathRef();
1057 const bool auxHere = hasAux && error->GetAuxItemSheetPath().PathRef() == sheet.PathRef();
1058
1059 switch( error->GetErrorCode() )
1060 {
1062 if( engine && !hasMain && !hasAux && sheet.Last()->IsTopLevelSheet() )
1063 {
1064 const SCH_ITEM* source = ResolveItem( error->GetMainItemID(), nullptr, true );
1065
1066 // Root-label exclusions predating instance-specific ERC omitted the sheet path
1067 if( source && source->Type() == SCH_HIER_LABEL_T )
1068 it = findPathless( lookup );
1069 }
1070
1071 break;
1072
1074 case ERCE_GENERIC_ERROR:
1078 if( !marker->IsExcluded() && mainHere && !hasAux )
1079 {
1080 // Older sheet-specific exclusions omitted the main item's sheet path
1081 auto previous = lookup.ToProto();
1082 previous.mutable_marker()->clear_main_item_sheet_path();
1083 it = settings.m_ErcExclusions.find( ERC_EXCLUSION::FromProto( previous ) );
1084 }
1085
1086 break;
1087
1096 // Older source diagnostics excluded the item across all of its sheet instances
1097 if( !marker->IsExcluded() && mainHere && !hasAux )
1098 it = findPathless( lookup );
1099
1100 if( it == settings.m_ErcExclusions.end() && engine && mainHere && !hasAux
1101 && error->GetErrorCode() == ERCE_ENDPOINT_OFF_GRID )
1102 {
1103 if( !offGridEndpoints )
1104 {
1105 offGridEndpoints =
1106 m_connectivity->Engine().OffGridEndpoints( Settings().m_ConnectionGridSize );
1107 }
1108
1109 for( const auto& endpoint : *offGridEndpoints )
1110 {
1111 if( endpoint.sheet != sheet.PathRef() || endpoint.item != error->GetMainItemID()
1112 || endpoint.position != marker->GetPosition() )
1113 continue;
1114
1115 for( const auto& [pin, position] : endpoint.equivalentPins )
1116 {
1117 auto alternate = ERC_ITEM::Create( ERCE_ENDPOINT_OFF_GRID );
1118 alternate->SetItems( std::vector<KIID>{ pin } );
1119 alternate->SetSheetSpecificPath( sheet );
1120 alternate->SetItemsSheetPaths( sheet );
1121 SCH_MARKER witness( std::move( alternate ), position );
1122 const auto candidate = ERC_EXCLUSION::FromMarker( witness );
1123 it = settings.m_ErcExclusions.find( candidate );
1124
1125 if( it == settings.m_ErcExclusions.end() )
1126 it = findPathless( candidate );
1127
1128 if( it != settings.m_ErcExclusions.end() )
1129 break;
1130 }
1131
1132 break;
1133 }
1134 }
1135
1136 break;
1137
1139 // Older duplicate-sheet exclusions covered this pair in every parent instance
1140 if( !marker->IsExcluded() && mainHere && auxHere )
1141 it = findPathless( lookup );
1142
1143 break;
1144
1146 // Older exclusions identified the unit pair without either sheet instance
1147 if( hasMain && auxHere )
1148 it = findPathless( lookup );
1149
1150 break;
1151
1152 default:
1153 break;
1154 }
1155 }
1156
1157 if( it != settings.m_ErcExclusions.end() )
1158 {
1159 marker->SetExcluded( true, it->GetComment() );
1160
1161 if( !legacyPathlessExclusions.contains( *it ) )
1162 settings.m_ErcExclusions.erase( it );
1163 }
1164 else if( !marker->IsExcluded() )
1165 {
1166 if( const auto key = groupKey( *marker ) )
1167 groupMarkers.emplace( *key, marker );
1168 }
1169 }
1170 }
1171
1172 for( const ERC_EXCLUSION& exclusion : legacyPathlessExclusions )
1173 settings.m_ErcExclusions.erase( exclusion );
1174
1175 std::vector<SCH_MARKER*> newMarkers;
1176
1177 for( const ERC_EXCLUSION& exclusion : settings.m_ErcExclusions )
1178 {
1179 SCH_MARKER* marker = SCH_MARKER::FromProto( exclusion.ToProto().marker(), sheetList );
1180
1181 if( marker )
1182 {
1183 // Canonical connectivity witnesses can change the marker anchor and reported IDs
1184 if( const auto key = groupKey( *marker ) )
1185 {
1186 const auto current = groupMarkers.find( *key );
1187 const auto itemCount = []( const SCH_MARKER& aMarker )
1188 {
1189 const auto ids = aMarker.GetRCItem()->GetIDs();
1190 return std::count_if( ids.begin(), ids.end(), []( const KIID& id ) { return id != niluuid; } );
1191 };
1192
1193 if( current != groupMarkers.end()
1194 && itemCount( *current->second ) == itemCount( *marker ) )
1195 {
1196 current->second->SetExcluded( true, exclusion.GetComment() );
1197 groupMarkers.erase( current );
1198 delete marker;
1199 continue;
1200 }
1201 }
1202
1203 marker->SetExcluded( true, exclusion.GetComment() );
1204 newMarkers.push_back( marker );
1205 }
1206 else
1207 {
1208 m_unresolvedErcExclusions.push_back( exclusion );
1209 }
1210 }
1211
1212 settings.m_ErcExclusions.clear();
1213
1214 return newMarkers;
1215}
1216
1217
1218std::shared_ptr<BUS_ALIAS> SCHEMATIC::GetBusAlias( const wxString& aLabel ) const
1219{
1220 for( auto it = m_busAliases.rbegin(); it != m_busAliases.rend(); ++it )
1221 {
1222 const auto& alias = *it;
1223
1224 if( alias && alias->GetName() == aLabel )
1225 return alias;
1226 }
1227
1228 return nullptr;
1229}
1230
1231
1232void SCHEMATIC::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
1233{
1234 if( !aAlias )
1235 return;
1236
1237 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
1238 {
1239 return candidate && candidate->GetName() == aAlias->GetName() && candidate->Members() == aAlias->Members();
1240 };
1241
1242 auto it = std::find_if( m_busAliases.begin(), m_busAliases.end(), sameDefinition );
1243
1244 if( it != m_busAliases.end() )
1245 std::rotate( it, std::next( it ), m_busAliases.end() );
1246 else
1247 m_busAliases.push_back( aAlias->Clone() );
1248
1250}
1251
1252
1253void SCHEMATIC::SetBusAliases( const std::vector<std::shared_ptr<BUS_ALIAS>>& aAliases )
1254{
1255 std::vector<std::shared_ptr<BUS_ALIAS>> aliases;
1256
1257 for( const std::shared_ptr<BUS_ALIAS>& alias : aAliases )
1258 {
1259 if( !alias )
1260 continue;
1261
1262 std::shared_ptr<BUS_ALIAS> clone = alias->Clone();
1263
1264 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
1265 {
1266 return candidate && candidate->GetName() == clone->GetName() && candidate->Members() == clone->Members();
1267 };
1268
1269 auto it = std::find_if( aliases.begin(), aliases.end(), sameDefinition );
1270
1271 if( it != aliases.end() )
1272 std::rotate( it, std::next( it ), aliases.end() );
1273 else
1274 aliases.push_back( clone );
1275 }
1276
1277 m_busAliases.swap( aliases );
1279
1280 if( m_project )
1281 m_project->GetProjectFile().m_BusAliasesDefined = true;
1282}
1283
1284
1286{
1287 return m_project && m_project->GetProjectFile().m_BusAliasesDefined;
1288}
1289
1290
1292{
1293 m_busAliases.clear();
1294
1295 if( !m_project )
1296 return;
1297
1298 const auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
1299
1300 for( const auto& alias : projectAliases )
1301 {
1302 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
1303
1304 busAlias->SetName( alias.first );
1305 busAlias->SetMembers( alias.second );
1306
1307 m_busAliases.push_back( busAlias );
1308 }
1309}
1310
1311
1313{
1314 if( !m_project )
1315 return;
1316
1317 auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
1318
1319 projectAliases.clear();
1320
1321 for( const std::shared_ptr<BUS_ALIAS>& alias : m_busAliases )
1322 {
1323 if( !alias )
1324 continue;
1325
1326 projectAliases.insert_or_assign( alias->GetName(), alias->Members() );
1327 }
1328}
1329
1330
1332{
1333 std::set<wxString> names;
1334
1335 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
1336 {
1337 for( const auto& group : Connectivity().GetNetMap() )
1338 {
1339 // A net without items, such as a member of an unplaced bus, has nothing to assign a netclass to
1340 if( std::ranges::all_of( group.instances, []( const auto& net ) { return net.Items().empty(); } ) )
1341 continue;
1342
1343 const auto& net = group.instances.front();
1344
1345 if( net.IsNet() && CONNECTION_SUBGRAPH::GetDriverPriority( net.Driver() )
1347 {
1348 names.insert( group.name );
1349 }
1350 }
1351
1352 return names;
1353 }
1354
1355 for( const auto& [key, subgraphList] : m_connectionGraph->GetNetMap() )
1356 {
1357 CONNECTION_SUBGRAPH* firstSubgraph = subgraphList[0];
1358
1359 if( !firstSubgraph->GetDriverConnection()->IsBus()
1361 {
1362 names.insert( key.Name );
1363 }
1364 }
1365
1366 return names;
1367}
1368
1369
1370bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
1371{
1373
1374 if( !environment || !environment->IsCollectingSources() )
1375 return resolveCrossReference( token, aDepth );
1376
1377 const TEXT_EVAL::ENVIRONMENT::CROSS_REFERENCE_KEY key{ *token, aDepth };
1378 const bool resolved = resolveCrossReference( token, aDepth );
1379 environment->RecordCrossReference( key, { *token, resolved } );
1380 return resolved;
1381}
1382
1383
1384bool SCHEMATIC::resolveCrossReference( wxString* token, int aDepth ) const
1385{
1386 wxString remainder;
1387 wxString ref = token->BeforeFirst( ':', &remainder );
1388 KIID_PATH path( ref );
1389 KIID uuid( 0 );
1390 SCH_SHEET_PATH sheetPath;
1391 SCH_ITEM* refItem = ResolveItem( KIID( uuid ), &sheetPath, true );
1392
1393 if( path.size() > 1 )
1394 {
1395 uuid = path.back();
1396 path.pop_back();
1397 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
1398 }
1399
1400 // Parse optional variant name from syntax ${REF:FIELD:VARIANT}
1401 // remainder is "FIELD" or "FIELD:VARIANT"
1402 wxString variantName;
1403 wxString fieldName = remainder;
1404 int colonPos = remainder.Find( ':' );
1405
1406 if( colonPos != wxNOT_FOUND )
1407 {
1408 fieldName = remainder.Left( colonPos );
1409 variantName = remainder.Mid( colonPos + 1 );
1410 }
1411
1412 // Note: We don't expand nested variables or evaluate math expressions here.
1413 // The multi-pass loop in GetShownText handles all variable and expression resolution
1414 // before cross-references are resolved. This ensures table cell variables like ${ROW}
1415 // are expanded correctly.
1416
1417 if( refItem && refItem->Type() == SCH_SYMBOL_T )
1418 {
1419 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
1420
1421 if( refSymbol->ResolveTextVar( &sheetPath, &fieldName, variantName, aDepth + 1 ) )
1422 {
1423 *token = std::move( fieldName );
1424 }
1425 else
1426 {
1427 // Field/function not found on symbol
1428 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ),
1429 refSymbol->GetRef( &sheetPath, false ),
1430 fieldName );
1431 }
1432
1433 return true; // Stop looking
1434 }
1435 else if( refItem && refItem->Type() == SCH_SHEET_T )
1436 {
1437 SCH_SHEET* refSheet = static_cast<SCH_SHEET*>( refItem );
1438
1439 sheetPath.push_back( refSheet );
1440
1441 if( refSheet->ResolveTextVar( &sheetPath, &remainder, aDepth + 1 ) )
1442 {
1443 *token = std::move( remainder );
1444 }
1445 else
1446 {
1447 // Field/function not found on sheet
1448 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ),
1449 refSheet->GetName(),
1450 remainder );
1451 }
1452
1453 return true; // Stop looking
1454 }
1455
1456 // If UUID resolution failed, try to resolve by reference designator
1457 // This handles both exact matches (J601A) and parent references for multi-unit symbols (J601)
1458 if( !refItem )
1459 {
1460 SCH_REFERENCE_LIST refs;
1462
1463 SCH_SYMBOL* foundSymbol = nullptr;
1464 SCH_SHEET_PATH foundPath;
1465
1466 for( int ii = 0; ii < (int) refs.GetCount(); ii++ )
1467 {
1468 SCH_REFERENCE& reference = refs[ii];
1469 wxString symbolRef = reference.GetSymbol()->GetRef( &reference.GetSheetPath(), false );
1470
1471 // Try exact match first
1472 if( symbolRef == ref )
1473 {
1474 foundSymbol = reference.GetSymbol();
1475 foundPath = reference.GetSheetPath();
1476 break;
1477 }
1478
1479 // For multi-unit symbols, try matching parent reference (e.g., J601 matches J601A)
1480 if( symbolRef.StartsWith( ref ) && symbolRef.Length() == ref.Length() + 1 )
1481 {
1482 wxChar lastChar = symbolRef.Last();
1483 if( lastChar >= 'A' && lastChar <= 'Z' )
1484 {
1485 foundSymbol = reference.GetSymbol();
1486 foundPath = reference.GetSheetPath();
1487 // Don't break - continue looking for exact match
1488 }
1489 }
1490 }
1491
1492 if( foundSymbol )
1493 {
1494 if( foundSymbol->ResolveTextVar( &foundPath, &fieldName, variantName, aDepth + 1 ) )
1495 {
1496 *token = std::move( fieldName );
1497 }
1498 else
1499 {
1500 // Field/function not found on symbol
1501 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ),
1502 foundSymbol->GetRef( &foundPath, false ),
1503 fieldName );
1504 }
1505
1506 return true; // Stop looking
1507 }
1508
1509 // Symbol not found - set unresolved error
1510 *token = wxString::Format( wxT( "<Unresolved: %s>" ), ref );
1511 return true;
1512 }
1513
1514 // Reference not found - show error message
1515 *token = wxString::Format( wxT( "<Unknown reference: %s>" ), ref );
1516 return true;
1517}
1518
1519
1520std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetNamesMap() const
1521{
1522 std::map<int, wxString> namesMap;
1523
1524 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1525 {
1526 if( sheet.size() == 1 )
1527 namesMap[sheet.GetVirtualPageNumber()] = _( "<root sheet>" );
1528 else
1529 namesMap[sheet.GetVirtualPageNumber()] = sheet.Last()->GetName();
1530 }
1531
1532 return namesMap;
1533}
1534
1535
1536std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetPagesMap() const
1537{
1538 std::map<int, wxString> pagesMap;
1539
1540 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1541 pagesMap[sheet.GetVirtualPageNumber()] = sheet.GetPageNumber();
1542
1543 return pagesMap;
1544}
1545
1546
1547wxString SCHEMATIC::ConvertRefsToKIIDs( const wxString& aSource ) const
1548{
1549 wxString newbuf;
1550 size_t sourceLen = aSource.length();
1551
1552 for( size_t i = 0; i < sourceLen; ++i )
1553 {
1554 // Check for escaped expressions: \${ or \@{
1555 // These should be copied verbatim without any ref→KIID conversion
1556 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
1557 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
1558 {
1559 // Copy the escape sequence and the entire escaped expression
1560 newbuf.append( aSource[i] ); // backslash
1561 newbuf.append( aSource[i + 1] ); // $ or @
1562 newbuf.append( aSource[i + 2] ); // {
1563 i += 2;
1564
1565 // Find and copy everything until the matching closing brace
1566 int braceDepth = 1;
1567 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
1568 {
1569 if( aSource[i] == '{' )
1570 braceDepth++;
1571 else if( aSource[i] == '}' )
1572 braceDepth--;
1573
1574 newbuf.append( aSource[i] );
1575 }
1576 i--; // Back up one since the for loop will increment
1577 continue;
1578 }
1579
1580 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
1581 {
1582 wxString token;
1583 bool isCrossRef = false;
1584 int nesting = 0;
1585
1586 for( i = i + 2; i < sourceLen; ++i )
1587 {
1588 if( aSource[i] == '{' && ( aSource[i - 1] == '_' || aSource[i - 1] == '^' || aSource[i - 1] == '~' ) )
1589 {
1590 nesting++;
1591 }
1592
1593 if( aSource[i] == '}' )
1594 {
1595 nesting--;
1596
1597 if( nesting < 0 )
1598 break;
1599 }
1600
1601 if( aSource[i] == ':' )
1602 isCrossRef = true;
1603
1604 token.append( aSource[i] );
1605 }
1606
1607 if( isCrossRef )
1608 {
1609 wxString remainder;
1610 wxString ref = token.BeforeFirst( ':', &remainder );
1611 SCH_REFERENCE_LIST references;
1612
1614
1615 for( size_t jj = 0; jj < references.GetCount(); jj++ )
1616 {
1617 SCH_SYMBOL* refSymbol = references[jj].GetSymbol();
1618
1619 if( ref == refSymbol->GetRef( &references[jj].GetSheetPath(), true ) )
1620 {
1621 KIID_PATH path = references[jj].GetSheetPath().Path();
1622 path.push_back( refSymbol->m_Uuid );
1623
1624 token = path.AsString() + wxS( ":" ) + remainder;
1625 break;
1626 }
1627 }
1628 }
1629
1630 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1631 }
1632 else
1633 {
1634 newbuf.append( aSource[i] );
1635 }
1636 }
1637
1638 return newbuf;
1639}
1640
1641
1642wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
1643{
1644 wxString newbuf;
1645 size_t sourceLen = aSource.length();
1646
1647 for( size_t i = 0; i < sourceLen; ++i )
1648 {
1649 // Check for escaped expressions: \${ or \@{
1650 // These should be copied verbatim without any KIID→ref conversion
1651 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
1652 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
1653 {
1654 // Copy the escape sequence and the entire escaped expression
1655 newbuf.append( aSource[i] ); // backslash
1656 newbuf.append( aSource[i + 1] ); // $ or @
1657 newbuf.append( aSource[i + 2] ); // {
1658 i += 2;
1659
1660 // Find and copy everything until the matching closing brace
1661 int braceDepth = 1;
1662 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
1663 {
1664 if( aSource[i] == '{' )
1665 braceDepth++;
1666 else if( aSource[i] == '}' )
1667 braceDepth--;
1668
1669 newbuf.append( aSource[i] );
1670 }
1671 i--; // Back up one since the for loop will increment
1672 continue;
1673 }
1674
1675 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
1676 {
1677 wxString token;
1678 bool isCrossRef = false;
1679
1680 for( i = i + 2; i < sourceLen; ++i )
1681 {
1682 if( aSource[i] == '}' )
1683 break;
1684
1685 if( aSource[i] == ':' )
1686 isCrossRef = true;
1687
1688 token.append( aSource[i] );
1689 }
1690
1691 if( isCrossRef )
1692 {
1693 wxString remainder;
1694 wxString ref = token.BeforeFirst( ':', &remainder );
1695 KIID_PATH path( ref );
1696 KIID uuid = path.back();
1697 SCH_SHEET_PATH sheetPath;
1698 SCH_ITEM* refItem = ResolveItem( uuid, &sheetPath, true );
1699
1700 if( path.size() > 1 )
1701 {
1702 path.pop_back();
1703 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
1704 }
1705
1706 if( refItem && refItem->Type() == SCH_SYMBOL_T )
1707 {
1708 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
1709 token = refSymbol->GetRef( &sheetPath, true ) + wxS( ":" ) + remainder;
1710 }
1711 }
1712
1713 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1714 }
1715 else
1716 {
1717 newbuf.append( aSource[i] );
1718 }
1719 }
1720
1721 return newbuf;
1722}
1723
1724
1731
1732
1734{
1735 // Filename is rootSheetName-sheetName-...-sheetName
1736 // Note that we need to fetch the rootSheetName out of its filename, as the root SCH_SHEET's
1737 // name is just a timestamp.
1738
1739 // Skip virtual root if present
1740 size_t startIdx = 0;
1741 if( CurrentSheet().size() > 0 && CurrentSheet().at( 0 )->IsVirtualRootSheet() )
1742 startIdx = 1;
1743
1744 // Handle the case where we only have a virtual root (shouldn't happen in practice)
1745 if( startIdx >= CurrentSheet().size() )
1746 return wxEmptyString;
1747
1748 SCH_SHEET* topSheet = CurrentSheet().at( startIdx );
1749
1750 // A top-level sheet keeps its file on the screen, because the SHEET_FILENAME field is only
1751 // set on the sheet instances that a parent sheet owns
1752 wxString topFileName;
1753
1754 if( topSheet->GetScreen() )
1755 topFileName = topSheet->GetScreen()->GetFileName();
1756
1757 if( topFileName.IsEmpty() )
1758 topFileName = topSheet->GetFileName();
1759
1760 wxFileName rootFn( topFileName );
1761 wxString filename = rootFn.GetName();
1762
1763 for( unsigned i = startIdx + 1; i < CurrentSheet().size(); i++ )
1764 filename += wxT( "-" ) + CurrentSheet().at( i )->GetName();
1765
1766 return filename;
1767}
1768
1769
1771{
1772 SCH_SCREEN* screen;
1773 SCH_SCREENS s_list( Root() );
1774
1775 // Set the sheet count, and the sheet number (1 for root sheet)
1776 int sheet_count;
1777
1778 // Handle virtual root case
1779 if( Root().m_Uuid == niluuid )
1780 {
1781 // Virtual root: count all top-level sheets
1782 sheet_count = 0;
1783
1784 for( const SCH_SHEET* topSheet : m_topLevelSheets )
1785 {
1786 if( topSheet )
1787 sheet_count += topSheet->CountSheets();
1788 }
1789 }
1790 else
1791 {
1792 // Traditional single root
1793 sheet_count = Root().CountSheets();
1794 }
1795
1796 int sheet_number = 1;
1797
1798 if( m_hierarchy.empty() )
1799 {
1800 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1801 screen->SetPageCount( sheet_count );
1802
1803 CurrentSheet().SetVirtualPageNumber( sheet_number );
1804 screen = CurrentSheet().LastScreen();
1805
1806 if( screen )
1807 screen->SetVirtualPageNumber( sheet_number );
1808
1809 return;
1810 }
1811
1812 const KIID_PATH& current_sheetpath = CurrentSheet().Path();
1813
1814 // @todo Remove all pseudo page number system is left over from prior to real page number
1815 // implementation.
1816 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1817 {
1818 if( sheet.Path() == current_sheetpath ) // Current sheet path found
1819 break;
1820
1821 sheet_number++; // Not found, increment before this current path
1822 }
1823
1824 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1825 screen->SetPageCount( sheet_count );
1826
1827 CurrentSheet().SetVirtualPageNumber( sheet_number );
1828 CurrentSheet().LastScreen()->SetVirtualPageNumber( sheet_number );
1829 CurrentSheet().LastScreen()->SetPageNumber( CurrentSheet().GetPageNumber() );
1830}
1831
1832
1834{
1835 std::map<wxString, std::set<int>>& pageRefsMap = GetPageRefsMap();
1836
1837 pageRefsMap.clear();
1838
1839 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1840 {
1841 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1842 {
1843 SCH_GLOBALLABEL* global = static_cast<SCH_GLOBALLABEL*>( item );
1844 wxString resolvedLabel = global->GetShownText( &sheet, FOR_GUI );
1845
1846 pageRefsMap[resolvedLabel].insert( sheet.GetVirtualPageNumber() );
1847 }
1848 }
1849
1850 bool show = Settings().m_IntersheetRefsShow;
1851
1852 // Refresh all visible global labels. Note that we have to collect them first as the
1853 // SCH_SCREEN::Update() call is going to invalidate the RTree iterator.
1854
1855 std::vector<SCH_GLOBALLABEL*> currentSheetGlobalLabels;
1856
1857 for( EDA_ITEM* item : CurrentSheet().LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1858 currentSheetGlobalLabels.push_back( static_cast<SCH_GLOBALLABEL*>( item ) );
1859
1860 for( SCH_GLOBALLABEL* globalLabel : currentSheetGlobalLabels )
1861 {
1862 std::vector<SCH_FIELD>& fields = globalLabel->GetFields();
1863
1864 fields[0].SetVisible( show );
1865
1866 if( show )
1867 {
1868 if( fields.size() == 1 && fields[0].GetTextPos() == globalLabel->GetPosition() )
1869 globalLabel->AutoplaceFields( CurrentSheet().LastScreen(), AUTOPLACE_AUTO );
1870
1871 CurrentSheet().LastScreen()->Update( globalLabel );
1872
1873 for( SCH_FIELD& field : globalLabel->GetFields() )
1874 field.ClearBoundingBoxCache();
1875
1876 globalLabel->ClearBoundingBoxCache();
1877
1878 if( m_schematicHolder )
1879 m_schematicHolder->IntersheetRefUpdate( globalLabel );
1880 }
1881 }
1882}
1883
1884
1885void SCHEMATIC::SyncLibSymbolPinMaps( const wxString& aSchLibSymbolName, const LIB_SYMBOL& aSource,
1886 SCH_COMMIT* aCommit )
1887{
1888 SCH_SCREENS screens( Root() );
1889
1890 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1891 {
1892 auto it = screen->GetLibSymbols().find( aSchLibSymbolName );
1893
1894 if( it != screen->GetLibSymbols().end() && it->second && it->second != &aSource )
1895 {
1896 it->second->SetPinMaps( aSource.GetPinMaps() );
1897 it->second->SetAssociatedFootprints( aSource.GetAssociatedFootprints() );
1898 }
1899
1900 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1901 {
1902 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1903
1904 if( symbol->GetSchSymbolLibraryName() != aSchLibSymbolName )
1905 continue;
1906
1907 LIB_SYMBOL* copy = symbol->GetLibSymbolRef().get();
1908
1909 if( !copy || copy == &aSource )
1910 continue;
1911
1912 if( copy->GetPinMaps() == aSource.GetPinMaps()
1913 && copy->GetAssociatedFootprints() == aSource.GetAssociatedFootprints() )
1914 {
1915 continue;
1916 }
1917
1918 if( aCommit )
1919 aCommit->Modify( symbol, screen );
1920
1921 copy->SetPinMaps( aSource.GetPinMaps() );
1922 copy->SetAssociatedFootprints( aSource.GetAssociatedFootprints() );
1923 }
1924 }
1925}
1926
1927
1928wxString SCHEMATIC::GetOperatingPoint( const wxString& aNetName, int aPrecision, const wxString& aRange )
1929{
1930 wxString spiceNetName( aNetName );
1932 spiceNetName.MakeLower();
1933
1934 if( spiceNetName == wxS( "gnd" ) || spiceNetName == wxS( "0" ) )
1935 return wxEmptyString;
1936
1937 auto it = m_operatingPoints.find( spiceNetName );
1938
1939 if( it != m_operatingPoints.end() )
1940 return SPICE_VALUE( it->second ).ToString( { aPrecision, aRange } );
1941 else if( m_operatingPoints.empty() )
1942 return wxS( "--" );
1943 else
1944 return wxS( "?" );
1945}
1946
1947
1948int SCHEMATIC::FixupJunctionsAfterImport( const std::function<void( SCH_LINE*, SCH_LINE* )>& aOnSplit )
1949{
1950 SCH_SCREENS screens( Root() );
1951 int count = 0;
1952
1953 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1954 {
1955 std::deque<EDA_ITEM*> allItems;
1956
1957 for( SCH_ITEM* item : screen->Items() )
1958 allItems.push_back( item );
1959
1960 // Add missing junctions and breakup wires as needed
1961 for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
1962 {
1963 count++;
1964
1965 SCH_JUNCTION* junction = new SCH_JUNCTION( point );
1966 screen->Append( junction );
1967
1968 // Breakup wires
1969 for( SCH_LINE* wire : screen->GetBusesAndWires( point, true ) )
1970 {
1971 SCH_LINE* newSegment = wire->NonGroupAware_BreakAt( point );
1972 screen->Append( newSegment );
1973
1974 if( aOnSplit )
1975 aOnSplit( wire, newSegment );
1976 }
1977 }
1978 }
1979
1980 return count;
1981}
1982
1983
1984void SCHEMATIC::OnItemsAdded( std::vector<SCH_ITEM*>& aNewItems )
1985{
1987}
1988
1989
1990void SCHEMATIC::OnItemsRemoved( std::vector<SCH_ITEM*>& aRemovedItems )
1991{
1993}
1994
1995
1996void SCHEMATIC::OnItemsChanged( std::vector<SCH_ITEM*>& aItems )
1997{
1999}
2000
2001
2006
2007
2012
2013
2015{
2016 if( !alg::contains( m_listeners, aListener ) )
2017 m_listeners.push_back( aListener );
2018}
2019
2020
2022{
2023 auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
2024
2025 if( i != m_listeners.end() )
2026 {
2027 std::iter_swap( i, m_listeners.end() - 1 );
2028 m_listeners.pop_back();
2029 }
2030}
2031
2032
2034{
2035 m_listeners.clear();
2036}
2037
2038
2040{
2041 std::erase_if( m_unresolvedErcExclusions,
2042 [&]( const ERC_EXCLUSION& exclusion )
2043 {
2044 if( aErrorCode >= 0
2046 exclusion.ToProto().marker().error_type() ) != aErrorCode )
2047 return false;
2048
2049 ErcSettings().m_ErcExclusions.erase( exclusion );
2050 return true;
2051 } );
2052}
2053
2054
2056{
2057 // Use a sorted sheetList to reduce file churn
2058 SCH_SHEET_LIST sheetList = Hierarchy();
2059 ERC_SETTINGS& ercSettings = ErcSettings();
2060
2061 ercSettings.m_ErcExclusions.clear();
2062 ercSettings.m_ErcExclusions.insert( m_unresolvedErcExclusions.begin(), m_unresolvedErcExclusions.end() );
2063
2064 for( unsigned i = 0; i < sheetList.size(); i++ )
2065 {
2066 for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) )
2067 {
2068 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
2069
2070 if( marker->IsExcluded() )
2071 {
2072 ercSettings.m_ErcExclusions.insert( ERC_EXCLUSION::FromMarker( *marker ) );
2073 }
2074 }
2075 }
2076}
2077
2078
2080{
2081 SCH_SHEET_LIST sheetList = Hierarchy();
2082
2083 for( SCH_MARKER* marker : ResolveERCExclusions() )
2084 {
2085 SCH_SHEET_PATH errorPath;
2086 ignore_unused( sheetList.ResolveItem( marker->GetRCItem()->GetMainItemID(), &errorPath ) );
2087
2088 if( errorPath.LastScreen() )
2089 errorPath.LastScreen()->Append( marker );
2090 else
2091 RootScreen()->Append( marker );
2092 }
2093
2094 // Once we have the ERC Exclusions, record them in the project file so that
2095 // they are retained even before the schematic is saved (PCB Editor can also save the project)
2097}
2098
2099
2101{
2102 return static_cast<EMBEDDED_FILES*>( this );
2103}
2104
2105
2107{
2108 return static_cast<const EMBEDDED_FILES*>( this );
2109}
2110
2111
2112void SCHEMATIC::RunOnNestedEmbeddedFiles( const std::function<void( EMBEDDED_FILES* )>& aFunction )
2113{
2114 SCH_SCREENS screens( Root() );
2115
2116 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2117 {
2118 for( auto& [name, libSym] : screen->GetLibSymbols() )
2119 aFunction( libSym->GetEmbeddedFiles() );
2120 }
2121}
2122
2123
2124std::set<KIFONT::OUTLINE_FONT*> SCHEMATIC::GetFonts() const
2125{
2126 std::set<KIFONT::OUTLINE_FONT*> fonts;
2127
2128 SCH_SHEET_LIST sheetList = Hierarchy();
2129
2130 for( const SCH_SHEET_PATH& sheet : sheetList )
2131 {
2132 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
2133 {
2134 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
2135 {
2136 KIFONT::FONT* font = text->GetFont();
2137
2138 if( !font || font->IsStroke() )
2139 continue;
2140
2141 using EMBEDDING_PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
2142 auto* outline = static_cast<KIFONT::OUTLINE_FONT*>( font );
2143
2144 if( outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::EDITABLE
2145 || outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::INSTALLABLE )
2146 {
2147 fonts.insert( outline );
2148 }
2149 }
2150 }
2151 }
2152
2153 return fonts;
2154}
2155
2156
2158{
2159 std::set<KIFONT::OUTLINE_FONT*> fonts = GetFonts();
2160
2161 for( KIFONT::OUTLINE_FONT* font : fonts )
2162 {
2163 auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
2164
2165 if( !file )
2166 {
2167 wxLogTrace( "EMBED", "Failed to add font file: %s", font->GetFileName() );
2168 continue;
2169 }
2170
2172 }
2173}
2174
2175
2176std::set<const SCH_SCREEN*> SCHEMATIC::GetSchematicsSharedByMultipleProjects() const
2177{
2178 std::set<const SCH_SCREEN*> retv;
2179
2180 wxCHECK( m_rootSheet, retv );
2181
2182 SCH_SHEET_LIST hierarchy( m_rootSheet );
2183 SCH_SCREENS screens( m_rootSheet );
2184
2185 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2186 {
2187 for( const SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
2188 {
2189 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
2190
2191 const std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = symbol->GetInstances();
2192
2193 for( const SCH_SYMBOL_INSTANCE& instance : symbolInstances )
2194 {
2195 if( !hierarchy.HasPath( instance.m_Path ) )
2196 {
2197 retv.insert( screen );
2198 break;
2199 }
2200 }
2201
2202 if( retv.count( screen ) )
2203 break;
2204 }
2205 }
2206
2207 return retv;
2208}
2209
2210
2212{
2213 wxCHECK( m_rootSheet, false );
2214
2215 SCH_SCREENS screens( m_rootSheet );
2216
2217 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
2218 {
2219 wxCHECK2( screen, continue );
2220
2221 if( screen->GetRefCount() > 1 )
2222 return true;
2223 }
2224
2225 return false;
2226}
2227
2228
2229void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
2230{
2231 SCH_SELECTION_TOOL* selectionTool = m_schematicHolder ? m_schematicHolder->GetSelectionTool() : nullptr;
2232 std::vector<SCH_LINE*> lines;
2233 std::vector<SCH_JUNCTION*> junctions;
2234 std::vector<SCH_NO_CONNECT*> ncs;
2235 std::vector<SCH_ITEM*> items_to_remove;
2236 std::unordered_set<SCH_LINE*> generatedLines;
2237 std::vector<std::unique_ptr<SCH_LINE>> retiredLines;
2238 bool changed = true;
2239
2240 if( aScreen == nullptr )
2241 aScreen = GetCurrentScreen();
2242
2243 auto remove_item = [&]( SCH_ITEM* aItem ) -> void
2244 {
2245 changed = true;
2246
2247 if( !( aItem->GetFlags() & STRUCT_DELETED ) )
2248 {
2249 aItem->SetFlags( STRUCT_DELETED );
2250
2251 if( aItem->IsSelected() && selectionTool )
2252 selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ );
2253
2254 if( m_schematicHolder )
2255 {
2256 m_schematicHolder->RemoveFromScreen( aItem, aScreen );
2257 }
2258 else
2259 {
2260 aScreen->Remove( aItem );
2261 }
2262
2263 aCommit->RemovedForCleanup( aItem, aScreen );
2264
2265 if( aItem->Type() == SCH_LINE_T && generatedLines.erase( static_cast<SCH_LINE*>( aItem ) )
2266 && !aCommit->GetStatus( aItem, aScreen ) )
2267 {
2268 // An intermediate merge has no undo owner when its Add/Remove entries cancel.
2269 retiredLines.emplace_back( static_cast<SCH_LINE*>( aItem ) );
2270 }
2271 }
2272 };
2273
2274
2275 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
2276 {
2277 if( !aScreen->IsExplicitJunctionAllowed( item->GetPosition() ) )
2278 {
2279 if( item->IsSelected() || item->HasFlag( SELECTED_BY_DRAG ) )
2280 continue;
2281
2282 items_to_remove.push_back( item );
2283 }
2284 else
2285 junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
2286 }
2287
2288 for( SCH_ITEM* item : items_to_remove )
2289 remove_item( item );
2290
2291 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
2292 ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
2293
2294 alg::for_all_pairs( junctions.begin(), junctions.end(),
2295 [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
2296 {
2297 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
2298 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
2299 {
2300 return;
2301 }
2302
2303 if( aFirst->GetPosition() == aSecond->GetPosition() )
2304 remove_item( aSecond );
2305 } );
2306
2307 alg::for_all_pairs( ncs.begin(), ncs.end(),
2308 [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
2309 {
2310 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
2311 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
2312 {
2313 return;
2314 }
2315
2316 if( aFirst->GetPosition() == aSecond->GetPosition() )
2317 remove_item( aSecond );
2318 } );
2319
2320
2321 auto minX = []( const SCH_LINE* l )
2322 {
2323 return std::min( l->GetStartPoint().x, l->GetEndPoint().x );
2324 };
2325
2326 auto maxX = []( const SCH_LINE* l )
2327 {
2328 return std::max( l->GetStartPoint().x, l->GetEndPoint().x );
2329 };
2330
2331 auto minY = []( const SCH_LINE* l )
2332 {
2333 return std::min( l->GetStartPoint().y, l->GetEndPoint().y );
2334 };
2335
2336 auto maxY = []( const SCH_LINE* l )
2337 {
2338 return std::max( l->GetStartPoint().y, l->GetEndPoint().y );
2339 };
2340
2341 // Would be nice to put lines in a canonical form here by swapping
2342 // start <-> end as needed but I don't know what swapping breaks.
2343 while( changed )
2344 {
2345 changed = false;
2346 lines.clear();
2347
2348 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
2349 {
2350 if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
2351 lines.push_back( static_cast<SCH_LINE*>( item ) );
2352 }
2353
2354 // Sort by minimum X position
2355 std::sort( lines.begin(), lines.end(),
2356 [&]( const SCH_LINE* a, const SCH_LINE* b )
2357 {
2358 return minX( a ) < minX( b );
2359 } );
2360
2361 for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
2362 {
2363 SCH_LINE* firstLine = *it1;
2364
2365 if( firstLine->GetEditFlags() & STRUCT_DELETED )
2366 continue;
2367
2368 if( firstLine->IsNull() )
2369 {
2370 remove_item( firstLine );
2371 continue;
2372 }
2373
2374 int firstRightXEdge = maxX( firstLine );
2375 auto it2 = it1;
2376
2377 for( ++it2; it2 != lines.end(); ++it2 )
2378 {
2379 SCH_LINE* secondLine = *it2;
2380 int secondLeftXEdge = minX( secondLine );
2381
2382 // impossible to overlap remaining lines
2383 if( secondLeftXEdge > firstRightXEdge )
2384 break;
2385
2386 // No Y axis overlap
2387 if( !( std::max( minY( firstLine ), minY( secondLine ) )
2388 <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) )
2389 {
2390 continue;
2391 }
2392
2393 if( secondLine->GetFlags() & STRUCT_DELETED )
2394 continue;
2395
2396 if( !secondLine->IsParallel( firstLine ) || !secondLine->IsStrokeEquivalent( firstLine )
2397 || secondLine->GetLayer() != firstLine->GetLayer() )
2398 {
2399 continue;
2400 }
2401
2402 // Remove identical lines
2403 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
2404 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
2405 {
2406 remove_item( secondLine );
2407 continue;
2408 }
2409
2410 // See if we can merge an overlap (or two colinear touching segments with
2411 // no junction where they meet).
2412 SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
2413
2414 if( mergedLine != nullptr )
2415 {
2416 remove_item( firstLine );
2417 remove_item( secondLine );
2418
2419 if( m_schematicHolder )
2420 {
2421 m_schematicHolder->AddToScreen( mergedLine, aScreen );
2422 }
2423 else
2424 {
2425 aScreen->Append( mergedLine );
2426 }
2427
2428 generatedLines.insert( mergedLine );
2429 aCommit->Added( mergedLine, aScreen );
2430
2431 if( selectionTool && ( firstLine->IsSelected() || secondLine->IsSelected() ) )
2432 selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
2433
2434 break;
2435 }
2436 }
2437 }
2438 }
2439}
2440
2441
2443 const std::set<SCH_SCREEN*>& aLocalScreens )
2444{
2446 PROF_TIMER timer;
2447
2448 if( aCleanupFlags == LOCAL_CLEANUP )
2449 {
2450 if( aLocalScreens.empty() )
2451 CleanUp( aCommit, GetCurrentScreen() );
2452 else
2453 {
2454 for( SCH_SCREEN* screen : aLocalScreens )
2455 CleanUp( aCommit, screen );
2456 }
2457 }
2458 else if( aCleanupFlags == GLOBAL_CLEANUP )
2459 {
2460 std::unordered_set<SCH_SCREEN*> cleanedScreens;
2461
2462 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
2463 {
2464 SCH_SCREEN* screen = sheet.LastScreen();
2465
2466 if( cleanedScreens.insert( screen ).second )
2467 {
2468 screen->BumpConnectivityRevision();
2469 CleanUp( aCommit, screen );
2470 }
2471 }
2472 }
2473
2474 timer.Stop();
2475 wxLogTrace( "CONN_PROFILE", "SchematicCleanUp() %0.4f ms", timer.msecs() );
2476
2477 if( Settings().m_IntersheetRefsShow )
2479}
2480
2481
2482void SCHEMATIC::RebuildConnectivity( std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2483 PROGRESS_REPORTER* aProgressReporter,
2484 KIGFX::SCH_VIEW* aSchView )
2485{
2486 // Some importers populate screens after installing the hierarchy
2487 if( m_textVarAdapter )
2488 m_textVarAdapter->MarkHierarchyChanged();
2489
2491 m_project->GetProjectFile().NetSettings()->ClearAllCaches();
2492 std::unordered_set<SCH_SCREEN*> screens;
2493
2494 for( const SCH_SHEET_PATH& path : Hierarchy() )
2495 {
2496 if( SCH_SCREEN* screen = path.LastScreen() )
2497 screens.insert( screen );
2498 }
2499
2500 SCH_RULE_AREA::UpdateRuleAreasInScreens( screens, aSchView );
2501
2502 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
2503 {
2504 m_connectivity->Recalculate( *this, true,
2505 aChangedItemHandler ? *aChangedItemHandler
2506 : std::function<void( SCH_ITEM* )>() );
2507 }
2508 else
2509 {
2510 ConnectionGraph()->Recalculate( Hierarchy(), true, aChangedItemHandler, aProgressReporter );
2511 }
2512}
2513
2514
2516 TOOL_MANAGER* aToolManager, PROGRESS_REPORTER* aProgressReporter,
2517 KIGFX::SCH_VIEW* aSchView,
2518 std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2519 PICKED_ITEMS_LIST* aLastChangeList, bool aCleanupDone )
2520{
2521 SCH_COMMIT localCommit( aToolManager );
2522
2523 if( !aCommit )
2524 aCommit = &localCommit;
2525
2526 if( !aCleanupDone )
2527 CleanUpConnections( aCommit, aCleanupFlags );
2528
2529 SCH_SHEET_LIST list = Hierarchy();
2530
2531 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
2532 {
2533 if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP )
2534 {
2535 if( !localCommit.Empty() )
2536 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2537
2538 RebuildConnectivity( aChangedItemHandler, aProgressReporter, aSchView );
2539 return;
2540 }
2541
2542 std::unordered_set<SCH_SCREEN*> screens;
2543
2544 for( const SCH_SHEET_PATH& path : list )
2545 screens.insert( path.LastScreen() );
2546
2547 SCH_RULE_AREA::UpdateRuleAreasInScreens( screens, aSchView );
2548
2549 // Commit cleanup before callbacks, which may close or replace the schematic
2550 if( !localCommit.Empty() )
2551 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2552
2553 m_connectivity->Recalculate( *this, false, aChangedItemHandler ? *aChangedItemHandler
2554 : std::function<void( SCH_ITEM* )>() );
2555 return;
2556 }
2557
2558 if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP
2559 || aLastChangeList == nullptr || ConnectionGraph()->IsMinor() )
2560 {
2561 if( !localCommit.Empty() )
2562 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2563
2564 RebuildConnectivity( aChangedItemHandler, aProgressReporter, aSchView );
2565 return;
2566 }
2567 else
2568 {
2569 struct CHANGED_ITEM
2570 {
2571 SCH_ITEM* item;
2572 SCH_ITEM* linked_item;
2573 SCH_SCREEN* screen;
2574 };
2575
2576 // Final change sets
2577 std::set<SCH_ITEM*> changed_items;
2578 std::set<VECTOR2I> pts;
2579 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> item_paths;
2580
2581 // Working change sets
2582 std::unordered_set<SCH_SCREEN*> changed_screens;
2583 std::set<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> changed_rule_areas;
2584 std::vector<CHANGED_ITEM> changed_connectable_items;
2585
2586 // Lambda to add an item to the connectivity update sets
2587 auto addItemToChangeSet =
2588 [&changed_items, &pts, &item_paths]( CHANGED_ITEM itemData )
2589 {
2590 std::vector<SCH_SHEET_PATH>& paths = itemData.screen->GetClientSheetPaths();
2591
2592 std::vector<VECTOR2I> tmp_pts = itemData.item->GetConnectionPoints();
2593 pts.insert( tmp_pts.begin(), tmp_pts.end() );
2594 changed_items.insert( itemData.item );
2595
2596 for( SCH_SHEET_PATH& path : paths )
2597 item_paths.insert( std::make_pair( path, itemData.item ) );
2598
2599 if( !itemData.linked_item || !itemData.linked_item->IsConnectable() )
2600 return;
2601
2602 tmp_pts = itemData.linked_item->GetConnectionPoints();
2603 pts.insert( tmp_pts.begin(), tmp_pts.end() );
2604 changed_items.insert( itemData.linked_item );
2605
2606 // We have to directly add the pins here because the link may not exist on the schematic
2607 // anymore and so won't be picked up by GetScreen()->Items().Overlapping() below.
2608 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( itemData.linked_item ) )
2609 {
2610 std::vector<SCH_PIN*> pins = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
2611 changed_items.insert( pins.begin(), pins.end() );
2612 }
2613
2614 for( SCH_SHEET_PATH& path : paths )
2615 item_paths.insert( std::make_pair( path, itemData.linked_item ) );
2616 };
2617
2618 // Get all changed connectable items and determine all changed screens
2619 for( unsigned ii = 0; ii < aLastChangeList->GetCount(); ++ii )
2620 {
2621 switch( aLastChangeList->GetPickedItemStatus( ii ) )
2622 {
2623 // Only care about changed, new, and deleted items, the other
2624 // cases are not connectivity-related
2625 case UNDO_REDO::CHANGED:
2626 case UNDO_REDO::NEWITEM:
2627 case UNDO_REDO::DELETED: break;
2628
2629 default: continue;
2630 }
2631
2632 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItem( ii ) );
2633
2634 if( item )
2635 {
2636 SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( aLastChangeList->GetScreenForItem( ii ) );
2637 changed_screens.insert( screen );
2638
2639 if( item->Type() == SCH_RULE_AREA_T )
2640 {
2641 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( item );
2642 changed_rule_areas.insert( { ruleArea, screen } );
2643 }
2644 else if( item->IsConnectable() )
2645 {
2646 SCH_ITEM* linked_item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItemLink( ii ) );
2647 changed_connectable_items.push_back( { item, linked_item, screen } );
2648 }
2649 }
2650 }
2651
2652 // Update rule areas in changed screens to propagate any directive connectivity changes
2653 std::vector<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> forceUpdateRuleAreas =
2654 SCH_RULE_AREA::UpdateRuleAreasInScreens( changed_screens, aSchView );
2655
2656 std::for_each( forceUpdateRuleAreas.begin(), forceUpdateRuleAreas.end(),
2657 [&]( std::pair<SCH_RULE_AREA*, SCH_SCREEN*>& updatedRuleArea )
2658 {
2659 changed_rule_areas.insert( updatedRuleArea );
2660 } );
2661
2662 // If a SCH_RULE_AREA was changed, we need to add all past and present contained items to
2663 // update their connectivity
2664 std::map<KIID, EDA_ITEM*> itemMap;
2665 list.FillItemMap( itemMap );
2666
2667 auto addPastAndPresentContainedItems =
2668 [&]( SCH_RULE_AREA* changedRuleArea, SCH_SCREEN* screen )
2669 {
2670 for( const KIID& pastItem : changedRuleArea->GetPastContainedItems() )
2671 {
2672 if( itemMap.contains( pastItem ) )
2673 addItemToChangeSet( { static_cast<SCH_ITEM*>( itemMap[pastItem] ), nullptr, screen } );
2674 }
2675
2676 for( SCH_ITEM* containedItem : changedRuleArea->GetContainedItems() )
2677 addItemToChangeSet( { containedItem, nullptr, screen } );
2678 };
2679
2680 for( const auto& [changedRuleArea, screen] : changed_rule_areas )
2681 addPastAndPresentContainedItems( changedRuleArea, screen );
2682
2683 // Add all changed items, and associated items, to the change set
2684 for( CHANGED_ITEM& changed_item : changed_connectable_items )
2685 {
2686 addItemToChangeSet( changed_item );
2687
2688 // If a SCH_DIRECTIVE_LABEL was changed which is attached to a SCH_RULE_AREA, we need
2689 // to add the contained items to the change set to force update of their connectivity
2690 if( changed_item.item->Type() == SCH_DIRECTIVE_LABEL_T )
2691 {
2692 const std::vector<VECTOR2I> labelConnectionPoints = changed_item.item->GetConnectionPoints();
2693
2694 auto candidateRuleAreas = changed_item.screen->Items()
2695 .Overlapping( SCH_RULE_AREA_T, changed_item.item->GetBoundingBox() );
2696
2697 for( SCH_ITEM* candidateRuleArea : candidateRuleAreas )
2698 {
2699 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( candidateRuleArea );
2700
2701 if( ruleArea->GetPolyShape().CollideEdge( labelConnectionPoints[0], nullptr, 5 ) )
2702 addPastAndPresentContainedItems( ruleArea, changed_item.screen );
2703 }
2704 }
2705 }
2706
2707 for( const VECTOR2I& pt : pts )
2708 {
2709 for( SCH_ITEM* item : GetCurrentScreen()->Items().Overlapping( pt ) )
2710 {
2711 // Leave this check in place. Overlapping items are not necessarily connectable.
2712 if( !item->IsConnectable() )
2713 continue;
2714
2715 SCH_SCREEN* screen = GetCurrentScreen();
2716 std::vector<SCH_SHEET_PATH>& paths = screen->GetClientSheetPaths();
2717
2718 if( item->Type() == SCH_LINE_T )
2719 {
2720 if( item->HitTest( pt ) )
2721 {
2722 changed_items.insert( item );
2723
2724 for( SCH_SHEET_PATH& path : paths )
2725 item_paths.insert( std::make_pair( path, item ) );
2726 }
2727 }
2728 else if( item->Type() == SCH_SYMBOL_T && item->IsConnected( pt ) )
2729 {
2730 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2731 std::vector<SCH_PIN*> pins = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
2732
2733 changed_items.insert( pins.begin(), pins.end() );
2734
2735 for( SCH_PIN* pin : pins )
2736 {
2737 for( SCH_SHEET_PATH& path : paths )
2738 item_paths.insert( std::make_pair( path, pin ) );
2739 }
2740 }
2741 else if( item->Type() == SCH_SHEET_T )
2742 {
2743 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
2744
2745 wxCHECK2( sheet, continue );
2746
2747 std::vector<SCH_SHEET_PIN*> sheetPins = sheet->GetPins();
2748 changed_items.insert( sheetPins.begin(), sheetPins.end() );
2749
2750 for( SCH_SHEET_PIN* pin : sheetPins )
2751 {
2752 for( SCH_SHEET_PATH& path : paths )
2753 item_paths.insert( std::make_pair( path, pin ) );
2754 }
2755 }
2756 else
2757 {
2758 if( item->IsConnected( pt ) )
2759 {
2760 changed_items.insert( item );
2761
2762 for( SCH_SHEET_PATH& path : paths )
2763 item_paths.insert( std::make_pair( path, item ) );
2764 }
2765 }
2766 }
2767 }
2768
2769 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> all_items =
2770 ConnectionGraph()->ExtractAffectedItems( changed_items );
2771
2772 all_items.insert( item_paths.begin(), item_paths.end() );
2773
2774 CONNECTION_GRAPH new_graph( this );
2775
2776 new_graph.SetLastCodes( ConnectionGraph() );
2777
2778 std::shared_ptr<NET_SETTINGS> netSettings = m_project->GetProjectFile().NetSettings();
2779
2780 std::set<wxString> affectedNets;
2781
2782 for( auto& [path, item] : all_items )
2783 {
2784 wxCHECK2( item, continue );
2785 item->SetConnectivityDirty();
2786 SCH_CONNECTION* conn = item->Connection();
2787
2788 if( conn )
2789 affectedNets.insert( conn->Name() );
2790 }
2791
2792 // For label items, also capture the old net name from the linked item (original state).
2793 // This ensures cache is cleared for both old and new net names when a label is renamed.
2794 for( const CHANGED_ITEM& changedItem : changed_connectable_items )
2795 {
2796 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( changedItem.item ) )
2797 {
2798 const wxString& driverName = label->GetCachedDriverName();
2799
2800 if( !driverName.IsEmpty() )
2801 affectedNets.insert( driverName );
2802 }
2803
2804 if( SCH_LABEL_BASE* linkedLabel = dynamic_cast<SCH_LABEL_BASE*>( changedItem.linked_item ) )
2805 {
2806 const wxString& driverName = linkedLabel->GetCachedDriverName();
2807
2808 if( !driverName.IsEmpty() )
2809 affectedNets.insert( driverName );
2810 }
2811 }
2812
2813 // Reset resolved netclass cache for this connection
2814 for( const wxString& netName : affectedNets )
2815 netSettings->ClearCacheForNet( netName );
2816
2817 new_graph.Recalculate( list, false, aChangedItemHandler, aProgressReporter );
2818 ConnectionGraph()->Merge( new_graph );
2819 }
2820
2821 if( !localCommit.Empty() )
2822 localCommit.Push( _( "Schematic Cleanup" ), SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
2823
2824}
2825
2826
2828{
2829 Reset();
2830
2832
2833 // Create the actual first top-level sheet
2834 SCH_SHEET* rootSheet = new SCH_SHEET( this );
2835 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
2836
2837 rootSheet->SetScreen( rootScreen );
2838 rootSheet->SyncUuidToScreen();
2839 rootScreen->SetFileName( "untitled.kicad_sch" ); // Set default filename to avoid conflicts
2840 rootScreen->SetPageNumber( wxT( "1" ) );
2841
2842 // Don't leave root page number empty
2843 SCH_SHEET_PATH rootSheetPath;
2844 rootSheetPath.push_back( m_rootSheet );
2845 rootSheetPath.push_back( rootSheet );
2846 rootSheetPath.SetPageNumber( wxT( "1" ) );
2847
2848 SetTopLevelSheets( { rootSheet } );
2849}
2850
2851
2852std::vector<SCH_SHEET*> SCHEMATIC::GetTopLevelSheets() const
2853{
2854 return m_topLevelSheets;
2855}
2856
2858{
2859 if( aIndex < 0 )
2860 return nullptr;
2861
2862 size_t index = static_cast<size_t>( aIndex );
2863
2864 if( index >= m_topLevelSheets.size() )
2865 return nullptr;
2866
2867 return m_topLevelSheets[index];
2868}
2869
2870
2871bool SCHEMATIC::IsTopLevelSheetUuid( const KIID& aUuid ) const
2872{
2873 if( !m_rootSheet )
2874 return false;
2875
2876 // A non-virtual root is the only sheet above the hierarchy
2877 if( m_rootSheet->m_Uuid != niluuid )
2878 return aUuid == m_rootSheet->m_Uuid;
2879
2880 for( const SCH_SHEET* sheet : m_topLevelSheets )
2881 {
2882 if( sheet && sheet->m_Uuid == aUuid )
2883 return true;
2884 }
2885
2886 return false;
2887}
2888
2889
2891{
2892 KIID_PATH path = aPath;
2893
2894 if( m_rootSheet && m_rootSheet->m_Uuid == niluuid && !path.empty() && path.front() == niluuid )
2895 path.erase( path.begin() );
2896
2897 return path;
2898}
2899
2900
2902{
2903 const KIID_PATH path = NormalizeInstancePath( aPath );
2904
2905 return !path.empty() && IsTopLevelSheetUuid( path.front() );
2906}
2907
2908
2910{
2911 wxCHECK_RET( aSheet, wxS( "Cannot add null sheet!" ) );
2912 wxCHECK_RET( aSheet->GetScreen(), wxS( "Cannot add virtual root as top-level sheet!" ) );
2913
2915
2916 // Set parent to virtual root
2917 aSheet->SetParent( m_rootSheet );
2918
2919 // Add to the virtual root's screen if it exists
2920 if( m_rootSheet->GetScreen() )
2921 {
2922 m_rootSheet->GetScreen()->Append( aSheet );
2923 }
2924
2925 // Add to our list
2926 m_topLevelSheets.push_back( aSheet );
2927
2929 rebuildHierarchyState( true );
2930}
2931
2933{
2934 auto it = std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet );
2935
2936 if( it == m_topLevelSheets.end() )
2937 return false;
2938
2939 if( m_topLevelSheets.size() == 1 )
2940 return false;
2941
2942 m_topLevelSheets.erase( it );
2943
2944 if( m_rootSheet && m_rootSheet->GetScreen() )
2945 m_rootSheet->GetScreen()->Remove( aSheet, false );
2946
2947 // If we're removing the current sheet, switch to another one
2948 if( !m_currentSheet->empty() && m_currentSheet->at( 0 ) == aSheet )
2949 {
2950 m_currentSheet->clear();
2951 if( !m_topLevelSheets.empty() )
2952 {
2953 m_currentSheet->push_back( m_topLevelSheets[0] );
2954 }
2955 }
2956
2957 rebuildHierarchyState( true );
2958 return true;
2959}
2960
2961
2962bool SCHEMATIC::IsTopLevelSheet( const SCH_SHEET* aSheet ) const
2963{
2964 return std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet ) != m_topLevelSheets.end();
2965}
2966
2967
2969{
2970 SCH_SHEET_LIST hierarchy;
2971
2972 wxLogTrace( traceSchSheetPaths, "BuildSheetListSortedByPageNumbers: %zu top-level sheets",
2973 m_topLevelSheets.size() );
2974
2975 // Can't build hierarchy without top-level sheets
2976 if( m_topLevelSheets.empty() )
2977 return hierarchy;
2978
2979 // For each top-level sheet, build its hierarchy
2980 for( SCH_SHEET* sheet : m_topLevelSheets )
2981 {
2982 if( sheet )
2983 {
2984 wxLogTrace( traceSchSheetPaths, " Top-level sheet: '%s' (UUID=%s, isVirtualRoot=%d)", sheet->GetName(),
2985 sheet->m_Uuid.AsString(), sheet->m_Uuid == niluuid ? 1 : 0 );
2986
2987 // Build the sheet list for this top-level sheet
2988 SCH_SHEET_LIST sheetList;
2989 sheetList.BuildSheetList( sheet, false );
2990
2991 // Add all sheets from this top-level sheet's hierarchy
2992 for( const SCH_SHEET_PATH& path : sheetList )
2993 {
2994 hierarchy.push_back( path );
2995 }
2996 }
2997 }
2998
2999 hierarchy.SortByPageNumbers();
3000
3001 return hierarchy;
3002}
3003
3004
3006{
3007 SCH_SHEET_LIST sheets;
3008
3009 // For each top-level sheet, build its hierarchy
3010 for( SCH_SHEET* sheet : m_topLevelSheets )
3011 {
3012 if( sheet )
3013 {
3014 SCH_SHEET_LIST sheetList;
3015 sheetList.BuildSheetList( sheet, false );
3016
3017 // Add all sheets from this top-level sheet's hierarchy
3018 for( const SCH_SHEET_PATH& path : sheetList )
3019 {
3020 sheets.push_back( path );
3021 }
3022 }
3023 }
3024
3025 return sheets;
3026}
3027
3028
3030{
3031 wxArrayString variantNames;
3032
3033 // There is no default variant name. This is just a place holder for UI controls.
3034 variantNames.Add( GetDefaultVariantName() );
3035
3036 for( const wxString& name : m_variantNames )
3037 variantNames.Add( name );
3038
3039 variantNames.Sort( SortVariantNames ); // SortVariantNames ensures the default is always first
3040
3041 return variantNames;
3042}
3043
3044
3046{
3047 if( m_currentVariant.IsEmpty() || ( m_currentVariant == GetDefaultVariantName() ) )
3048 return wxEmptyString;
3049
3050 return m_currentVariant;
3051}
3052
3053
3054bool SCHEMATIC::HasVariant( const wxString& aVariantName ) const
3055{
3056 for( const wxString& name : m_variantNames )
3057 {
3058 if( name.CmpNoCase( aVariantName ) == 0 )
3059 return true;
3060 }
3061
3062 return false;
3063}
3064
3065
3066void SCHEMATIC::SetCurrentVariant( const wxString& aVariantName )
3067{
3068 wxString newVariant;
3069
3070 // Internally an empty string is the default variant. Set to default if the variant name doesn't exist.
3071 if( ( aVariantName != GetDefaultVariantName() ) && m_variantNames.contains( aVariantName ) )
3072 newVariant = aVariantName;
3073
3074 if( m_currentVariant.CmpNoCase( newVariant ) == 0 )
3075 return;
3076
3077 m_currentVariant = newVariant;
3078
3079 // Variant-specific field values affect text geometry, so bounding box caches computed
3080 // with the previous variant's text are now stale.
3081 if( m_rootSheet )
3082 {
3083 SCH_SCREENS allScreens( m_rootSheet );
3084
3085 for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
3086 {
3087 for( SCH_ITEM* item : screen->Items() )
3088 item->ClearCaches();
3089 }
3090 }
3091
3092 // Variant-driven cross-ref / local-field value changes require a
3093 // reactive fan-out so dependent text items repaint without waiting for
3094 // an unrelated edit to nudge the view.
3095 if( m_textVarAdapter )
3096 m_textVarAdapter->Tracker().InvalidateVariantScoped();
3097}
3098
3099
3100void SCHEMATIC::AddVariant( const wxString& aVariantName )
3101{
3102 m_variantNames.emplace( aVariantName );
3103
3104 // Ensure the variant is registered in the project file
3105 auto& descriptions = Settings().m_VariantDescriptions;
3106
3107 if( descriptions.find( aVariantName ) == descriptions.end() )
3108 descriptions[aVariantName] = wxEmptyString;
3109}
3110
3111
3112void SCHEMATIC::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit )
3113{
3114 if( aCommit )
3115 {
3116 wxCHECK( m_rootSheet, /* void */ );
3117
3118 SCH_SCREENS allScreens( m_rootSheet );
3119
3120 allScreens.DeleteVariant( aVariantName, aCommit );
3121 }
3122
3123 if( m_currentVariant == aVariantName )
3124 SetCurrentVariant( wxEmptyString );
3125
3126 m_variantNames.erase( aVariantName );
3127 Settings().m_VariantDescriptions.erase( aVariantName );
3128}
3129
3130
3131void SCHEMATIC::RenameVariant( const wxString& aOldName, const wxString& aNewName, SCH_COMMIT* aCommit )
3132{
3133 wxCHECK( m_rootSheet, /* void */ );
3134 wxCHECK( !aOldName.IsEmpty() && !aNewName.IsEmpty(), /* void */ );
3135 wxCHECK( m_variantNames.contains( aOldName ), /* void */ );
3136
3137 m_variantNames.erase( aOldName );
3138 m_variantNames.insert( aNewName );
3139
3140 auto& descriptions = Settings().m_VariantDescriptions;
3141
3142 if( descriptions.count( aOldName ) )
3143 {
3144 descriptions[aNewName] = descriptions[aOldName];
3145 descriptions.erase( aOldName );
3146 }
3147
3148 // Retarget through SetCurrentVariant so the whole-schematic cache/text-var invalidation runs;
3149 // otherwise ${VARIANT} text on off-sheet items keeps its stale render cache after the rename.
3150 if( m_currentVariant == aOldName )
3151 SetCurrentVariant( aNewName );
3152
3153 SCH_SCREENS allScreens( m_rootSheet );
3154 allScreens.RenameVariant( aOldName, aNewName, aCommit );
3155}
3156
3157
3158void SCHEMATIC::CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
3159 SCH_COMMIT* aCommit )
3160{
3161 wxCHECK( m_rootSheet, /* void */ );
3162 wxCHECK( !aSourceVariant.IsEmpty() && !aNewVariant.IsEmpty(), /* void */ );
3163 wxCHECK( m_variantNames.contains( aSourceVariant ), /* void */ );
3164 wxCHECK( !m_variantNames.contains( aNewVariant ), /* void */ );
3165
3166 AddVariant( aNewVariant );
3167
3168 auto& descriptions = Settings().m_VariantDescriptions;
3169
3170 if( descriptions.count( aSourceVariant ) )
3171 descriptions[aNewVariant] = descriptions[aSourceVariant];
3172
3173 SCH_SCREENS allScreens( m_rootSheet );
3174 allScreens.CopyVariant( aSourceVariant, aNewVariant, aCommit );
3175}
3176
3177
3178wxString SCHEMATIC::GetVariantDescription( const wxString& aVariantName ) const
3179{
3180 const auto& descriptions = Settings().m_VariantDescriptions;
3181 auto it = descriptions.find( aVariantName );
3182
3183 if( it != descriptions.end() )
3184 return it->second;
3185
3186 return wxEmptyString;
3187}
3188
3189
3190void SCHEMATIC::SetVariantDescription( const wxString& aVariantName, const wxString& aDescription )
3191{
3192 auto& descriptions = Settings().m_VariantDescriptions;
3193
3194 if( aDescription.IsEmpty() )
3195 descriptions.erase( aVariantName );
3196 else
3197 descriptions[aVariantName] = aDescription;
3198}
3199
3200
3202{
3203 if( m_rootSheet && m_rootSheet->GetScreen() )
3204 {
3205 SCH_SCREENS screens( m_rootSheet );
3206 std::set<wxString> variantNames = screens.GetVariantNames();
3207 m_variantNames.insert( variantNames.begin(), variantNames.end() );
3208
3209 // Register any unknown variants to the project file with empty descriptions
3210 auto& descriptions = Settings().m_VariantDescriptions;
3211
3212 for( const wxString& name : variantNames )
3213 {
3214 if( descriptions.find( name ) == descriptions.end() )
3215 descriptions[name] = wxEmptyString;
3216 }
3217
3218 // Also include variants from the project file that may not have any diffs yet.
3219 // This ensures newly created variants with no symbol changes are preserved.
3220 for( const auto& [name, description] : descriptions )
3221 m_variantNames.insert( name );
3222 }
3223}
3224
3225
3226void SCHEMATIC::SaveToHistory( const wxString& aProjectPath, std::vector<HISTORY_FILE_DATA>& aFileData )
3227{
3228 if( !IsValid() )
3229 return;
3230
3231 wxString projPath = m_project->GetProjectPath();
3232
3233 if( projPath.IsEmpty() )
3234 return; // no project yet
3235
3236 // Verify we're saving for the correct project
3237 if( !projPath.IsSameAs( aProjectPath ) )
3238 {
3239 wxLogTrace( traceAutoSave, wxS( "[history] sch saver skipping - project path mismatch: %s vs %s" ), projPath,
3240 aProjectPath );
3241 return;
3242 }
3243
3244 if( !projPath.EndsWith( wxFILE_SEP_PATH ) )
3245 projPath += wxFILE_SEP_PATH;
3246
3247 SCH_SHEET_LIST sheetList = Hierarchy();
3248
3250
3251 KICAD_FORMAT::FORMAT_MODE mode = KICAD_FORMAT::FORMAT_MODE::NORMAL;
3252
3253 if( ADVANCED_CFG::GetCfg().m_CompactSave )
3254 mode = KICAD_FORMAT::FORMAT_MODE::COMPACT_TEXT_PROPERTIES;
3255
3256 // In ZIP mode the only caller is the autosave timer, so skipping clean sheets
3257 // avoids spurious _autosave-* files. In INCREMENTAL mode the manual-save flow
3258 // clears dirty flags before calling here, so filtering would skip the whole
3259 // snapshot. Git's diff-against-HEAD check rejects no-op commits there anyway.
3260 bool filterClean = !Pgm().GetCommonSettings()->AutosaveUsesLocalHistory();
3261
3262 for( const SCH_SHEET_PATH& path : sheetList )
3263 {
3264 SCH_SHEET* sheet = path.Last();
3265 SCH_SCREEN* screen = path.LastScreen();
3266
3267 if( !sheet || !screen )
3268 continue;
3269
3270 if( filterClean && !screen->IsContentModified() )
3271 continue;
3272
3273 wxFileName abs = m_project->AbsolutePath( screen->GetFileName() );
3274
3275 if( !abs.IsOk() )
3276 continue;
3277
3278 wxString absPath = abs.GetFullPath();
3279
3280 if( absPath.IsEmpty() || !absPath.StartsWith( projPath ) )
3281 continue;
3282
3283 wxString rel = absPath.Mid( projPath.length() );
3284
3285 try
3286 {
3287 STRING_FORMATTER formatter;
3288 pi.FormatSchematicToFormatter( &formatter, sheet, this );
3289
3290 HISTORY_FILE_DATA entry;
3291 entry.relativePath = rel;
3292 entry.content = std::move( formatter.MutableString() );
3293 entry.prettify = true;
3294 entry.formatMode = mode;
3295 aFileData.push_back( std::move( entry ) );
3296
3297 wxLogTrace( traceAutoSave,
3298 wxS( "[history] sch saver serialized %zu bytes for '%s' -> '%s'" ),
3299 aFileData.back().content.size(), absPath, rel );
3300 }
3301 catch( const IO_ERROR& ioe )
3302 {
3303 wxLogTrace( traceAutoSave, wxS( "[history] sch saver serialize failed for '%s': %s" ),
3304 absPath, wxString::FromUTF8( ioe.What() ) );
3305 }
3306 }
3307}
3308
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:309
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.
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:546
A holder to handle information on schematic or board items.
UNDO_REDO GetPickedItemStatus(unsigned int aIdx) const
EDA_ITEM * GetPickedItemLink(unsigned int aIdx) const
unsigned GetCount() const
BASE_SCREEN * GetScreenForItem(unsigned int aIdx) const
EDA_ITEM * GetPickedItem(unsigned int aIdx) const
A small class to help profiling.
Definition profile.h:46
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:86
double msecs(bool aSinceLast=false)
Definition profile.h: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:802
std::unique_ptr< SCH_CONNECTIVITY::NETCHAIN_MANAGER > m_netChains
Definition schematic.h:751
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:749
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:775
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:746
std::vector< SCH_SHEET * > m_topLevelSheets
List of top-level sheets (direct children of virtual root)
Definition schematic.h:737
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:314
wxString ConvertKIIDsToRefs(const wxString &aSource) const
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Definition schematic.h:194
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:796
std::map< wxString, std::set< int > > & GetPageRefsMap()
Definition schematic.h:370
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:760
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:728
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:778
SCH_CONNECTIVITY::FACADE & Connectivity() const
Definition schematic.h:316
wxString ConvertRefsToKIIDs(const wxString &aSource) const
void SetProject(PROJECT *aPrj)
wxString GetCurrentVariant() const
Return the current variant being edited.
void AddListener(SCHEMATIC_LISTENER *aListener)
Add a listener to the schematic to receive calls whenever something on the schematic has been modifie...
void CleanUpConnections(SCH_COMMIT *aCommit, SCH_CLEANUP_FLAGS aCleanupFlags, const std::set< SCH_SCREEN * > &aLocalScreens={})
std::map< int, wxString > GetVirtualPageToSheetPagesMap() const
EMBEDDED_FILES * GetEmbeddedFiles() override
PROJECT * m_project
Definition schematic.h:727
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:319
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:794
void InvokeListeners(Func &&aFunc, Args &&... args)
Definition schematic.h:716
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:289
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:806
bool HasProjectBusAliases() const
PROPERTY_LISTENER_SUBSCRIPTION m_fieldListenerSubscription
PROPERTY_MANAGER listener subscription installed in the ctor.
Definition schematic.h:811
void GetContextualTextVars(wxArrayString *aVars) const
void ensureDefaultTopLevelSheet()
SCH_SHEET & Root() const
Definition schematic.h:200
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:750
SCHEMATIC_HOLDER * m_schematicHolder
What currently "Holds" the schematic, i.e.
Definition schematic.h:788
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:783
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:304
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:770
ERC_SETTINGS & ErcSettings() const
std::set< wxString > m_variantNames
Definition schematic.h:798
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:734
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
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:545
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:682
bool IsParallel(const SCH_LINE *aLine) const
Definition sch_line.cpp:534
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
SCH_LINE * MergeOverlap(SCH_SCREEN *aScreen, SCH_LINE *aLine, bool aCheckJunctions)
Check line against aLine to see if it overlaps and merge if it does.
Definition sch_line.cpp:547
bool IsNull() const
Definition sch_line.h:134
bool IsStrokeEquivalent(const SCH_LINE *aLine)
Definition sch_line.h:228
bool IsEndPoint(const VECTOR2I &aPoint) const override
Test if aPt is an end point of this schematic object.
Definition sch_line.h:88
static SCH_MARKER * FromProto(const kiapi::schematic::ErcMarker &aMsg, const SCH_SHEET_LIST &aSheetList)
VECTOR2I GetPosition() const override
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:818
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:386
void SyncUuidToScreen()
Take the identity of the screen this sheet owns.
wxString GetName() const
Definition sch_sheet.h:142
SCH_SCREEN * m_screen
Definition sch_sheet.h:694
int CountSheets() const
Count the number of sheets found in "this" sheet including all of the subsheets.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:243
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:73
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:133
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:163
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit, int aBodyStyle) const override
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
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:91
@ INTERNAL
Definition common.h:94
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:474
@ LAYER_BUS
Definition layer_ids.h:475
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition kicad_algo.h:80
#define _HKI(x)
Definition page_info.cpp:40
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define TYPE_HASH(x)
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 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