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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <advanced_config.h>
21#include <algorithm>
22#include <common.h>
23#include <inspectable_impl.h>
24#include <set>
25#include <bus_alias.h>
26#include <commit.h>
27#include <connection_graph.h>
28#include <core/ignore.h>
29#include <core/kicad_algo.h>
30#include <core/profile.h>
31#include <sch_collectors.h>
32#include <erc/erc_settings.h>
33#include <font/outline_font.h>
35#include <progress_reporter.h>
36#include <project.h>
39#include <refdes_tracker.h>
40#include <schematic.h>
41#include <sch_bus_entry.h>
42#include <sch_commit.h>
43#include <sch_junction.h>
44#include <sch_label.h>
45#include <sch_line.h>
46#include <sch_marker.h>
47#include <sch_no_connect.h>
48#include <sch_rule_area.h>
49#include <sch_screen.h>
50#include <sch_sheet_pin.h>
51#include <sch_selection_tool.h>
52#include <sim/spice_settings.h>
53#include <sim/spice_value.h>
54#include <trace_helpers.h>
55#include <string_utils.h>
57#include <tool/tool_manager.h>
58#include <undo_redo_container.h>
59#include <local_history.h>
60#include <sch_io/sch_io_mgr.h>
61#include <sch_io/sch_io.h>
62
63#include <wx/log.h>
64
66
68 EDA_ITEM( nullptr, SCHEMATIC_T ),
69 m_project( nullptr ),
70 m_rootSheet( nullptr ),
71 m_schematicHolder( nullptr )
72{
76
77 SetProject( aPrj );
78
81 [&]( INSPECTABLE* aItem, PROPERTY_BASE* aProperty, COMMIT* aCommit )
82 {
83 // Special case: propagate value, footprint, and datasheet fields to other units
84 // of a given symbol if they aren't in the selection
85
86 SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( aItem );
87
88 if( !field || !IsValid() )
89 return;
90
91 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( field->GetParent() );
92
93 if( !symbol || aProperty->Name() != _HKI( "Text" ) )
94 return;
95
96 // TODO(JE) This will need to get smarter to enable API access
97 SCH_SHEET_PATH sheetPath = CurrentSheet();
98
99 wxString newValue = aItem->Get<wxString>( aProperty );
100
101 if( field->GetId() == FIELD_T::REFERENCE )
102 {
103 symbol->SetRef( &sheetPath, newValue );
104
105 // The user might want to change all the units to the new ref. Or they
106 // might not. Since we have no way of knowing, we default to the most
107 // concrete action (change only the selected reference).
108 return;
109 }
110
111 wxString ref = symbol->GetRef( &sheetPath );
112 int unit = symbol->GetUnit();
113 LIB_ID libId = symbol->GetLibId();
114
115 for( SCH_SHEET_PATH& sheet : Hierarchy() )
116 {
117 std::vector<SCH_SYMBOL*> otherUnits;
118
119 CollectOtherUnits( ref, unit, libId, sheet, &otherUnits );
120
121 for( SCH_SYMBOL* otherUnit : otherUnits )
122 {
123 switch( field->GetId() )
124 {
125 case FIELD_T::VALUE:
128 {
129 if( aCommit )
130 aCommit->Modify( otherUnit, sheet.LastScreen() );
131
132 otherUnit->GetField( field->GetId() )->SetText( newValue );
133 break;
134 }
135
136 default: break;
137 }
138 }
139 }
140 } );
141
142 Reset();
143}
144
145
155
156
158{
159 delete m_rootSheet;
160
161 m_rootSheet = nullptr;
162 m_topLevelSheets.clear();
163 m_hierarchy.clear();
164
165 m_connectionGraph->Reset();
166 m_currentSheet->clear();
167
168 m_busAliases.clear();
169
173}
174
175
177{
178 if( m_project )
179 {
180 PROJECT_FILE& project = m_project->GetProjectFile();
181
182 // d'tor will save settings to file
183 delete project.m_ErcSettings;
184 project.m_ErcSettings = nullptr;
185
186 // d'tor will save settings to file
187 delete project.m_SchematicSettings;
188 project.m_SchematicSettings = nullptr;
189 }
190
191 m_project = aPrj;
192
193 if( m_project )
194 {
195 PROJECT_FILE& project = m_project->GetProjectFile();
196 project.m_ErcSettings = new ERC_SETTINGS( &project, "erc" );
197 project.m_SchematicSettings = new SCHEMATIC_SETTINGS( &project, "schematic" );
198
199 project.m_SchematicSettings->LoadFromFile();
200 project.m_SchematicSettings->m_NgspiceSettings->LoadFromFile();
201 project.m_ErcSettings->LoadFromFile();
202
204 }
205}
206
207
209{
210 wxASSERT( m_project );
211
212 // Cache all existing annotations in the REFDES_TRACKER
213 std::shared_ptr<REFDES_TRACKER> refdesTracker = m_project->GetProjectFile().m_SchematicSettings->m_refDesTracker;
214
215 SCH_SHEET_LIST sheets = Hierarchy();
216 SCH_REFERENCE_LIST references;
217
218 sheets.GetSymbols( references );
219
220 for( const SCH_REFERENCE& ref : references )
221 {
222 refdesTracker->Insert( ref.GetFullRef( false ).ToStdString() );
223 }
224}
225
226
227bool SCHEMATIC::Contains( const SCH_REFERENCE& aRef ) const
228{
229 SCH_SHEET_LIST sheets = Hierarchy();
230 SCH_REFERENCE_LIST references;
231
236 sheets.GetSymbols( references );
237
238 return std::any_of( references.begin(), references.end(),
239 [&]( const SCH_REFERENCE& ref )
240 {
241 return ref.GetFullRef( true ) == aRef.GetFullRef( true );
242 } );
243}
244
245
247{
248 if( m_rootSheet && m_rootSheet->m_Uuid == niluuid )
249 {
250 if( !m_rootSheet->GetScreen() )
251 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
252
253 return;
254 }
255
256 SCH_SHEET* previousRoot = m_rootSheet;
257
258 m_rootSheet = new SCH_SHEET( this );
259 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
260 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
261
262 if( previousRoot )
263 {
264 previousRoot->SetParent( m_rootSheet );
265
266 if( m_rootSheet->GetScreen() )
267 m_rootSheet->GetScreen()->Append( previousRoot );
268
269 m_topLevelSheets.clear();
270 m_topLevelSheets.push_back( previousRoot );
271 }
272}
273
274
276{
277 // Early exit if we're already in the process of setting top-level sheets to avoid recursion
279 return;
280
282
283 if( !m_topLevelSheets.empty() )
284 return;
285
286 SCH_SHEET* rootSheet = new SCH_SHEET( this );
287 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
288
289 const_cast<KIID&>( rootSheet->m_Uuid ) = rootScreen->GetUuid();
290 rootSheet->SetScreen( rootScreen );
291
292 SetTopLevelSheets( { rootSheet } );
293
294 SCH_SHEET_PATH rootSheetPath;
295 rootSheetPath.push_back( m_rootSheet );
296 rootSheetPath.push_back( rootSheet );
297 rootSheetPath.SetPageNumber( wxT( "1" ) );
298}
299
300
302{
303 if( m_topLevelSheets.empty() )
304 return;
305
306 if( m_currentSheet->empty() || !IsTopLevelSheet( m_currentSheet->at( 0 ) ) )
307 {
308 m_currentSheet->clear();
309 m_currentSheet->push_back( m_topLevelSheets[0] );
310 }
311}
312
313
314void SCHEMATIC::rebuildHierarchyState( bool aResetConnectionGraph )
315{
317
318 if( aResetConnectionGraph && m_project )
319 m_connectionGraph->Reset();
320
321 m_variantNames.clear();
322
323 if( m_rootSheet && m_rootSheet->GetScreen() )
324 {
325 SCH_SCREENS screens( m_rootSheet );
326 std::set<wxString> variantNames = screens.GetVariantNames();
327 m_variantNames.insert( variantNames.begin(), variantNames.end() );
328 }
329
330 // Also include variants from the project file that may not have any diffs yet.
331 // This ensures newly created variants with no symbol changes are preserved.
332 if( m_project )
333 {
334 for( const auto& [name, description] : Settings().m_VariantDescriptions )
335 m_variantNames.insert( name );
336 }
337}
338
339
340void SCHEMATIC::SetTopLevelSheets( const std::vector<SCH_SHEET*>& aSheets )
341{
342 wxCHECK_RET( !aSheets.empty(), wxS( "Cannot set empty top-level sheets!" ) );
343
344 // Set the recursion guard early before any calls to ensureDefaultTopLevelSheet()
345 bool wasAlreadySetting = m_settingTopLevelSheets;
347
348 std::vector<SCH_SHEET*> validSheets;
349 validSheets.reserve( aSheets.size() );
350
351 for( SCH_SHEET* sheet : aSheets )
352 {
353 // Skip null sheets and virtual roots (which have niluuid)
354 if( sheet && sheet->m_Uuid != niluuid )
355 validSheets.push_back( sheet );
356 }
357
358 if( validSheets.empty() )
359 {
360 // Guard against re-entry to prevent infinite recursion
361 if( !wasAlreadySetting )
363
364 m_settingTopLevelSheets = wasAlreadySetting;
365 return;
366 }
367
369
370 std::set<SCH_SHEET*> desiredSheets( validSheets.begin(), validSheets.end() );
371
372 if( m_rootSheet->GetScreen() )
373 {
374 for( SCH_ITEM* item : m_rootSheet->GetScreen()->Items() )
375 {
376 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
377
378 if( sheet && !desiredSheets.contains( sheet ) )
379 delete sheet;
380 }
381
382 m_rootSheet->GetScreen()->Clear( false );
383 }
384
385 m_currentSheet->clear();
386 m_topLevelSheets.clear();
387
388 for( SCH_SHEET* sheet : validSheets )
389 {
390 sheet->SetParent( m_rootSheet );
391
392 if( m_rootSheet->GetScreen() )
393 m_rootSheet->GetScreen()->Append( sheet );
394
395 m_topLevelSheets.push_back( sheet );
396 }
397
399 rebuildHierarchyState( true );
400
401 m_settingTopLevelSheets = wasAlreadySetting;
402}
403
404
406{
407 // Virtual root's screen is just a container - return the first top-level sheet's screen
408 // which is what callers actually want
409 if( !m_topLevelSheets.empty() && m_topLevelSheets[0] )
410 return m_topLevelSheets[0]->GetScreen();
411
412 return nullptr;
413}
414
415
417{
418 wxCHECK( !m_hierarchy.empty(), m_hierarchy );
419
420 return m_hierarchy;
421}
422
423
429
430
431void SCHEMATIC::GetContextualTextVars( wxArrayString* aVars ) const
432{
433 auto add = [&]( const wxString& aVar )
434 {
435 if( !alg::contains( *aVars, aVar ) )
436 aVars->push_back( aVar );
437 };
438
439 add( wxT( "#" ) );
440 add( wxT( "##" ) );
441 add( wxT( "SHEETPATH" ) );
442 add( wxT( "SHEETNAME" ) );
443 add( wxT( "FILENAME" ) );
444 add( wxT( "FILEPATH" ) );
445 add( wxT( "PROJECTNAME" ) );
446 add( wxT( "VARIANT" ) );
447 add( wxT( "VARIANT_DESC" ) );
448
449 if( !CurrentSheet().empty() )
451
452 for( std::pair<wxString, wxString> entry : m_project->GetTextVars() )
453 add( entry.first );
454}
455
456
457bool SCHEMATIC::ResolveTextVar( const SCH_SHEET_PATH* aSheetPath, wxString* token, int aDepth ) const
458{
459 wxCHECK( aSheetPath, false );
460
461 if( token->IsSameAs( wxT( "#" ) ) )
462 {
463 *token = aSheetPath->GetPageNumber();
464 return true;
465 }
466 else if( token->IsSameAs( wxT( "##" ) ) )
467 {
468 *token = wxString::Format( "%i", Root().CountSheets() );
469 return true;
470 }
471 else if( token->IsSameAs( wxT( "SHEETPATH" ) ) )
472 {
473 *token = aSheetPath->PathHumanReadable();
474 return true;
475 }
476 else if( token->IsSameAs( wxT( "SHEETNAME" ) ) )
477 {
478 *token = aSheetPath->Last()->GetName();
479 return true;
480 }
481 else if( token->IsSameAs( wxT( "FILENAME" ) ) )
482 {
483 wxFileName fn( GetFileName() );
484 *token = fn.GetFullName();
485 return true;
486 }
487 else if( token->IsSameAs( wxT( "FILEPATH" ) ) )
488 {
489 wxFileName fn( GetFileName() );
490 *token = fn.GetFullPath();
491 return true;
492 }
493 else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) )
494 {
495 *token = m_project->GetProjectName();
496 return true;
497 }
498 else if( token->IsSameAs( wxT( "VARIANTNAME" ) ) || token->IsSameAs( wxT( "VARIANT" ) ) )
499 {
500 *token = m_currentVariant;
501 return true;
502 }
503 else if( token->IsSameAs( wxT( "VARIANT_DESC" ) ) )
504 {
506 return true;
507 }
508
509 // aSheetPath->LastScreen() can be null during schematic loading
510 if( aSheetPath->LastScreen() && aSheetPath->LastScreen()->GetTitleBlock().TextVarResolver( token, m_project ) )
511 return true;
512
513 if( m_project->TextVarResolver( token ) )
514 return true;
515
516 return false;
517}
518
519
521{
522 // With virtual root pattern, m_rootSheet is the virtual root with no file
523 // Return filename from first top-level sheet if available
524 if( !IsValid() )
525 return wxString( wxEmptyString );
526
527 if( !m_topLevelSheets.empty() && m_topLevelSheets[0]->GetScreen() )
528 return m_topLevelSheets[0]->GetScreen()->GetFileName();
529
530 return wxString( wxEmptyString );
531}
532
533
535{
536 if( !m_project )
537 {
538 static SCHEMATIC_SETTINGS defaultSettings( nullptr, "schematic" );
539 return defaultSettings;
540 }
541 wxASSERT( m_project );
542 return *m_project->GetProjectFile().m_SchematicSettings;
543}
544
545
547{
548 wxASSERT( m_project );
549 return *m_project->GetProjectFile().m_ErcSettings;
550}
551
552
553std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
554{
555 SCH_SHEET_LIST sheetList = Hierarchy();
556 ERC_SETTINGS& settings = ErcSettings();
557
558 // Migrate legacy marker exclusions to new format to ensure exclusion matching functions across
559 // file versions. Silently drops any legacy exclusions which can not be mapped to the new format
560 // without risking an incorrect exclusion - this is preferable to silently dropping
561 // new ERC errors / warnings due to an incorrect match between a legacy and new
562 // marker serialization format
563 std::set<wxString> migratedExclusions;
564
565 for( auto it = settings.m_ErcExclusions.begin(); it != settings.m_ErcExclusions.end(); )
566 {
567 SCH_MARKER* testMarker = SCH_MARKER::DeserializeFromString( sheetList, *it );
568
569 if( !testMarker )
570 {
571 it = settings.m_ErcExclusions.erase( it );
572 continue;
573 }
574
575 if( testMarker->IsLegacyMarker() )
576 {
577 const wxString settingsKey = testMarker->GetRCItem()->GetSettingsKey();
578
579 if( settingsKey != wxT( "pin_to_pin" ) && settingsKey != wxT( "hier_label_mismatch" )
580 && settingsKey != wxT( "different_unit_net" ) )
581 {
582 migratedExclusions.insert( testMarker->SerializeToString() );
583 }
584
585 it = settings.m_ErcExclusions.erase( it );
586 }
587 else
588 {
589 ++it;
590 }
591
592 delete testMarker;
593 }
594
595 settings.m_ErcExclusions.insert( migratedExclusions.begin(), migratedExclusions.end() );
596
597 // End of legacy exclusion removal / migrations
598
599 for( const SCH_SHEET_PATH& sheet : sheetList )
600 {
601 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
602 {
603 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
604 wxString serialized = marker->SerializeToString();
605 std::set<wxString>::iterator it = settings.m_ErcExclusions.find( serialized );
606
607 if( it != settings.m_ErcExclusions.end() )
608 {
609 marker->SetExcluded( true, settings.m_ErcExclusionComments[serialized] );
610 settings.m_ErcExclusions.erase( it );
611 }
612 }
613 }
614
615 std::vector<SCH_MARKER*> newMarkers;
616
617 for( const wxString& serialized : settings.m_ErcExclusions )
618 {
619 SCH_MARKER* marker = SCH_MARKER::DeserializeFromString( sheetList, serialized );
620
621 if( marker )
622 {
623 marker->SetExcluded( true, settings.m_ErcExclusionComments[serialized] );
624 newMarkers.push_back( marker );
625 }
626 }
627
628 settings.m_ErcExclusions.clear();
629
630 return newMarkers;
631}
632
633
634std::shared_ptr<BUS_ALIAS> SCHEMATIC::GetBusAlias( const wxString& aLabel ) const
635{
636 for( const std::shared_ptr<BUS_ALIAS>& alias : m_busAliases )
637 {
638 if( alias && alias->GetName() == aLabel )
639 return alias;
640 }
641
642 return nullptr;
643}
644
645
646void SCHEMATIC::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
647{
648 if( !aAlias )
649 return;
650
651 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
652 {
653 return candidate && candidate->GetName() == aAlias->GetName() && candidate->Members() == aAlias->Members();
654 };
655
656 auto it = std::find_if( m_busAliases.begin(), m_busAliases.end(), sameDefinition );
657
658 if( it != m_busAliases.end() )
659 return;
660
661 m_busAliases.push_back( aAlias );
662
664}
665
666
667void SCHEMATIC::SetBusAliases( const std::vector<std::shared_ptr<BUS_ALIAS>>& aAliases )
668{
669 m_busAliases.clear();
670
671 for( const std::shared_ptr<BUS_ALIAS>& alias : aAliases )
672 {
673 if( !alias )
674 continue;
675
676 std::shared_ptr<BUS_ALIAS> clone = alias->Clone();
677
678 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
679 {
680 return candidate && candidate->GetName() == clone->GetName() && candidate->Members() == clone->Members();
681 };
682
683 if( std::find_if( m_busAliases.begin(), m_busAliases.end(), sameDefinition ) != m_busAliases.end() )
684 continue;
685
686 m_busAliases.push_back( clone );
687 }
688
690}
691
692
694{
695 m_busAliases.clear();
696
697 if( !m_project )
698 return;
699
700 const auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
701
702 for( const auto& alias : projectAliases )
703 {
704 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
705
706 busAlias->SetName( alias.first );
707 busAlias->SetMembers( alias.second );
708
709 m_busAliases.push_back( busAlias );
710 }
711}
712
713
715{
716 if( !m_project )
717 return;
718
719 auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
720
721 projectAliases.clear();
722
723 std::set<wxString> seen;
724
725 for( const std::shared_ptr<BUS_ALIAS>& alias : m_busAliases )
726 {
727 if( !alias )
728 continue;
729
730 if( !seen.insert( alias->GetName() ).second )
731 continue;
732
733 projectAliases.emplace( alias->GetName(), alias->Members() );
734 }
735}
736
737
739{
740 std::set<wxString> names;
741
742 for( const auto& [key, subgraphList] : m_connectionGraph->GetNetMap() )
743 {
744 CONNECTION_SUBGRAPH* firstSubgraph = subgraphList[0];
745
746 if( !firstSubgraph->GetDriverConnection()->IsBus()
748 {
749 names.insert( key.Name );
750 }
751 }
752
753 return names;
754}
755
756
757bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
758{
759 wxString remainder;
760 wxString ref = token->BeforeFirst( ':', &remainder );
761 KIID_PATH path( ref );
762 KIID uuid = path.back();
763 SCH_SHEET_PATH sheetPath;
764 SCH_ITEM* refItem = ResolveItem( KIID( uuid ), &sheetPath, true );
765
766 if( path.size() > 1 )
767 {
768 path.pop_back();
769 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
770 }
771
772 // Parse optional variant name from syntax ${REF:FIELD:VARIANT}
773 // remainder is "FIELD" or "FIELD:VARIANT"
774 wxString variantName;
775 wxString fieldName = remainder;
776 int colonPos = remainder.Find( ':' );
777
778 if( colonPos != wxNOT_FOUND )
779 {
780 fieldName = remainder.Left( colonPos );
781 variantName = remainder.Mid( colonPos + 1 );
782 }
783
784 // Note: We don't expand nested variables or evaluate math expressions here.
785 // The multi-pass loop in GetShownText handles all variable and expression resolution
786 // before cross-references are resolved. This ensures table cell variables like ${ROW}
787 // are expanded correctly.
788
789 if( refItem && refItem->Type() == SCH_SYMBOL_T )
790 {
791 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
792
793 bool resolved = refSymbol->ResolveTextVar( &sheetPath, &fieldName, variantName, aDepth + 1 );
794
795 if( resolved )
796 {
797 *token = std::move( fieldName );
798 }
799 else
800 {
801 // Field/function not found on symbol
802 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ), refSymbol->GetRef( &sheetPath, false ), fieldName );
803 }
804
805 return true;
806 }
807 else if( refItem && refItem->Type() == SCH_SHEET_T )
808 {
809 SCH_SHEET* refSheet = static_cast<SCH_SHEET*>( refItem );
810
811 sheetPath.push_back( refSheet );
812
813 wxString remainderBefore = remainder;
814
815 if( refSheet->ResolveTextVar( &sheetPath, &remainder, aDepth + 1 ) )
816 *token = std::move( remainder );
817
818 // If the remainder still contains unresolved variables or expressions,
819 // return false so ExpandTextVars keeps the ${...} wrapper
820 if( remainderBefore.Contains( wxT( "${" ) ) || remainderBefore.Contains( wxT( "@{" ) ) )
821 return false;
822
823 return true; // Cross-reference is resolved
824 }
825
826 // If UUID resolution failed, try to resolve by reference designator
827 // This handles both exact matches (J601A) and parent references for multi-unit symbols (J601)
828 if( !refItem )
829 {
831 Hierarchy().GetSymbols( refs );
832
833 SCH_SYMBOL* foundSymbol = nullptr;
834 SCH_SHEET_PATH foundPath;
835
836 for( int ii = 0; ii < (int) refs.GetCount(); ii++ )
837 {
838 SCH_REFERENCE& reference = refs[ii];
839 wxString symbolRef = reference.GetSymbol()->GetRef( &reference.GetSheetPath(), false );
840
841 // Try exact match first
842 if( symbolRef == ref )
843 {
844 foundSymbol = reference.GetSymbol();
845 foundPath = reference.GetSheetPath();
846 break;
847 }
848
849 // For multi-unit symbols, try matching parent reference (e.g., J601 matches J601A)
850 if( symbolRef.StartsWith( ref ) && symbolRef.Length() == ref.Length() + 1 )
851 {
852 wxChar lastChar = symbolRef.Last();
853 if( lastChar >= 'A' && lastChar <= 'Z' )
854 {
855 foundSymbol = reference.GetSymbol();
856 foundPath = reference.GetSheetPath();
857 // Don't break - continue looking for exact match
858 }
859 }
860 }
861
862 if( foundSymbol )
863 {
864 bool resolved = foundSymbol->ResolveTextVar( &foundPath, &fieldName, variantName, aDepth + 1 );
865
866 if( resolved )
867 {
868 *token = std::move( fieldName );
869 }
870 else
871 {
872 // Field/function not found on symbol
873 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ), foundSymbol->GetRef( &foundPath, false ),
874 fieldName );
875 }
876
877 return true;
878 }
879
880 // Symbol not found - set unresolved error
881 *token = wxString::Format( wxT( "<Unresolved: %s>" ), ref );
882 return true;
883 }
884
885 // Reference not found - show error message
886 *token = wxString::Format( wxT( "<Unknown reference: %s>" ), ref );
887 return true;
888}
889
890
891std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetNamesMap() const
892{
893 std::map<int, wxString> namesMap;
894
895 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
896 {
897 if( sheet.size() == 1 )
898 namesMap[sheet.GetVirtualPageNumber()] = _( "<root sheet>" );
899 else
900 namesMap[sheet.GetVirtualPageNumber()] = sheet.Last()->GetName();
901 }
902
903 return namesMap;
904}
905
906
907std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetPagesMap() const
908{
909 std::map<int, wxString> pagesMap;
910
911 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
912 pagesMap[sheet.GetVirtualPageNumber()] = sheet.GetPageNumber();
913
914 return pagesMap;
915}
916
917
918wxString SCHEMATIC::ConvertRefsToKIIDs( const wxString& aSource ) const
919{
920 wxString newbuf;
921 size_t sourceLen = aSource.length();
922
923 for( size_t i = 0; i < sourceLen; ++i )
924 {
925 // Check for escaped expressions: \${ or \@{
926 // These should be copied verbatim without any ref→KIID conversion
927 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
928 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
929 {
930 // Copy the escape sequence and the entire escaped expression
931 newbuf.append( aSource[i] ); // backslash
932 newbuf.append( aSource[i + 1] ); // $ or @
933 newbuf.append( aSource[i + 2] ); // {
934 i += 2;
935
936 // Find and copy everything until the matching closing brace
937 int braceDepth = 1;
938 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
939 {
940 if( aSource[i] == '{' )
941 braceDepth++;
942 else if( aSource[i] == '}' )
943 braceDepth--;
944
945 newbuf.append( aSource[i] );
946 }
947 i--; // Back up one since the for loop will increment
948 continue;
949 }
950
951 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
952 {
953 wxString token;
954 bool isCrossRef = false;
955 int nesting = 0;
956
957 for( i = i + 2; i < sourceLen; ++i )
958 {
959 if( aSource[i] == '{' && ( aSource[i - 1] == '_' || aSource[i - 1] == '^' || aSource[i - 1] == '~' ) )
960 {
961 nesting++;
962 }
963
964 if( aSource[i] == '}' )
965 {
966 nesting--;
967
968 if( nesting < 0 )
969 break;
970 }
971
972 if( aSource[i] == ':' )
973 isCrossRef = true;
974
975 token.append( aSource[i] );
976 }
977
978 if( isCrossRef )
979 {
980 wxString remainder;
981 wxString ref = token.BeforeFirst( ':', &remainder );
982 SCH_REFERENCE_LIST references;
983
984 Hierarchy().GetSymbols( references );
985
986 for( size_t jj = 0; jj < references.GetCount(); jj++ )
987 {
988 SCH_SYMBOL* refSymbol = references[jj].GetSymbol();
989
990 if( ref == refSymbol->GetRef( &references[jj].GetSheetPath(), true ) )
991 {
992 KIID_PATH path = references[jj].GetSheetPath().Path();
993 path.push_back( refSymbol->m_Uuid );
994
995 token = path.AsString() + wxS( ":" ) + remainder;
996 break;
997 }
998 }
999 }
1000
1001 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1002 }
1003 else
1004 {
1005 newbuf.append( aSource[i] );
1006 }
1007 }
1008
1009 return newbuf;
1010}
1011
1012
1013wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
1014{
1015 wxString newbuf;
1016 size_t sourceLen = aSource.length();
1017
1018 for( size_t i = 0; i < sourceLen; ++i )
1019 {
1020 // Check for escaped expressions: \${ or \@{
1021 // These should be copied verbatim without any KIID→ref conversion
1022 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
1023 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
1024 {
1025 // Copy the escape sequence and the entire escaped expression
1026 newbuf.append( aSource[i] ); // backslash
1027 newbuf.append( aSource[i + 1] ); // $ or @
1028 newbuf.append( aSource[i + 2] ); // {
1029 i += 2;
1030
1031 // Find and copy everything until the matching closing brace
1032 int braceDepth = 1;
1033 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
1034 {
1035 if( aSource[i] == '{' )
1036 braceDepth++;
1037 else if( aSource[i] == '}' )
1038 braceDepth--;
1039
1040 newbuf.append( aSource[i] );
1041 }
1042 i--; // Back up one since the for loop will increment
1043 continue;
1044 }
1045
1046 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
1047 {
1048 wxString token;
1049 bool isCrossRef = false;
1050
1051 for( i = i + 2; i < sourceLen; ++i )
1052 {
1053 if( aSource[i] == '}' )
1054 break;
1055
1056 if( aSource[i] == ':' )
1057 isCrossRef = true;
1058
1059 token.append( aSource[i] );
1060 }
1061
1062 if( isCrossRef )
1063 {
1064 wxString remainder;
1065 wxString ref = token.BeforeFirst( ':', &remainder );
1066 KIID_PATH path( ref );
1067 KIID uuid = path.back();
1068 SCH_SHEET_PATH sheetPath;
1069 SCH_ITEM* refItem = ResolveItem( uuid, &sheetPath, true );
1070
1071 if( path.size() > 1 )
1072 {
1073 path.pop_back();
1074 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
1075 }
1076
1077 if( refItem && refItem->Type() == SCH_SYMBOL_T )
1078 {
1079 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
1080 token = refSymbol->GetRef( &sheetPath, true ) + wxS( ":" ) + remainder;
1081 }
1082 }
1083
1084 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1085 }
1086 else
1087 {
1088 newbuf.append( aSource[i] );
1089 }
1090 }
1091
1092 return newbuf;
1093}
1094
1095
1102
1103
1105{
1106 // Filename is rootSheetName-sheetName-...-sheetName
1107 // Note that we need to fetch the rootSheetName out of its filename, as the root SCH_SHEET's
1108 // name is just a timestamp.
1109
1110 // Skip virtual root if present
1111 size_t startIdx = 0;
1112 if( CurrentSheet().size() > 0 && CurrentSheet().at( 0 )->IsVirtualRootSheet() )
1113 startIdx = 1;
1114
1115 // Handle the case where we only have a virtual root (shouldn't happen in practice)
1116 if( startIdx >= CurrentSheet().size() )
1117 return wxEmptyString;
1118
1119 wxFileName rootFn( CurrentSheet().at( startIdx )->GetFileName() );
1120 wxString filename = rootFn.GetName();
1121
1122 for( unsigned i = startIdx + 1; i < CurrentSheet().size(); i++ )
1123 filename += wxT( "-" ) + CurrentSheet().at( i )->GetName();
1124
1125 return filename;
1126}
1127
1128
1130{
1131 SCH_SCREEN* screen;
1132 SCH_SCREENS s_list( Root() );
1133
1134 // Set the sheet count, and the sheet number (1 for root sheet)
1135 int sheet_count;
1136
1137 // Handle virtual root case
1138 if( Root().m_Uuid == niluuid )
1139 {
1140 // Virtual root: count all top-level sheets
1141 sheet_count = 0;
1142
1143 for( const SCH_SHEET* topSheet : m_topLevelSheets )
1144 {
1145 if( topSheet )
1146 sheet_count += topSheet->CountSheets();
1147 }
1148 }
1149 else
1150 {
1151 // Traditional single root
1152 sheet_count = Root().CountSheets();
1153 }
1154
1155 int sheet_number = 1;
1156
1157 if( m_hierarchy.empty() )
1158 {
1159 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1160 screen->SetPageCount( sheet_count );
1161
1162 CurrentSheet().SetVirtualPageNumber( sheet_number );
1163 screen = CurrentSheet().LastScreen();
1164
1165 if( screen )
1166 screen->SetVirtualPageNumber( sheet_number );
1167
1168 return;
1169 }
1170
1171 const KIID_PATH& current_sheetpath = CurrentSheet().Path();
1172
1173 // @todo Remove all pseudo page number system is left over from prior to real page number
1174 // implementation.
1175 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1176 {
1177 if( sheet.Path() == current_sheetpath ) // Current sheet path found
1178 break;
1179
1180 sheet_number++; // Not found, increment before this current path
1181 }
1182
1183 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1184 screen->SetPageCount( sheet_count );
1185
1186 CurrentSheet().SetVirtualPageNumber( sheet_number );
1187 CurrentSheet().LastScreen()->SetVirtualPageNumber( sheet_number );
1188 CurrentSheet().LastScreen()->SetPageNumber( CurrentSheet().GetPageNumber() );
1189}
1190
1191
1193{
1194 std::map<wxString, std::set<int>>& pageRefsMap = GetPageRefsMap();
1195
1196 pageRefsMap.clear();
1197
1198 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1199 {
1200 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1201 {
1202 SCH_GLOBALLABEL* global = static_cast<SCH_GLOBALLABEL*>( item );
1203 wxString resolvedLabel = global->GetShownText( &sheet, false );
1204
1205 pageRefsMap[resolvedLabel].insert( sheet.GetVirtualPageNumber() );
1206 }
1207 }
1208
1209 bool show = Settings().m_IntersheetRefsShow;
1210
1211 // Refresh all visible global labels. Note that we have to collect them first as the
1212 // SCH_SCREEN::Update() call is going to invalidate the RTree iterator.
1213
1214 std::vector<SCH_GLOBALLABEL*> currentSheetGlobalLabels;
1215
1216 for( EDA_ITEM* item : CurrentSheet().LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1217 currentSheetGlobalLabels.push_back( static_cast<SCH_GLOBALLABEL*>( item ) );
1218
1219 for( SCH_GLOBALLABEL* globalLabel : currentSheetGlobalLabels )
1220 {
1221 std::vector<SCH_FIELD>& fields = globalLabel->GetFields();
1222
1223 fields[0].SetVisible( show );
1224
1225 if( show )
1226 {
1227 if( fields.size() == 1 && fields[0].GetTextPos() == globalLabel->GetPosition() )
1228 globalLabel->AutoplaceFields( CurrentSheet().LastScreen(), AUTOPLACE_AUTO );
1229
1230 CurrentSheet().LastScreen()->Update( globalLabel );
1231
1232 for( SCH_FIELD& field : globalLabel->GetFields() )
1233 field.ClearBoundingBoxCache();
1234
1235 globalLabel->ClearBoundingBoxCache();
1236
1237 if( m_schematicHolder )
1238 m_schematicHolder->IntersheetRefUpdate( globalLabel );
1239 }
1240 }
1241}
1242
1243
1244wxString SCHEMATIC::GetOperatingPoint( const wxString& aNetName, int aPrecision, const wxString& aRange )
1245{
1246 wxString spiceNetName( aNetName.Lower() );
1248
1249 if( spiceNetName == wxS( "gnd" ) || spiceNetName == wxS( "0" ) )
1250 return wxEmptyString;
1251
1252 auto it = m_operatingPoints.find( spiceNetName );
1253
1254 if( it != m_operatingPoints.end() )
1255 return SPICE_VALUE( it->second ).ToString( { aPrecision, aRange } );
1256 else if( m_operatingPoints.empty() )
1257 return wxS( "--" );
1258 else
1259 return wxS( "?" );
1260}
1261
1262
1264{
1265 SCH_SCREENS screens( Root() );
1266 int count = 0;
1267
1268 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1269 {
1270 std::deque<EDA_ITEM*> allItems;
1271
1272 for( SCH_ITEM* item : screen->Items() )
1273 allItems.push_back( item );
1274
1275 // Add missing junctions and breakup wires as needed
1276 for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
1277 {
1278 count++;
1279
1280 SCH_JUNCTION* junction = new SCH_JUNCTION( point );
1281 screen->Append( junction );
1282
1283 // Breakup wires
1284 for( SCH_LINE* wire : screen->GetBusesAndWires( point, true ) )
1285 {
1286 SCH_LINE* newSegment = wire->NonGroupAware_BreakAt( point );
1287 screen->Append( newSegment );
1288 }
1289 }
1290 }
1291
1292 return count;
1293}
1294
1295
1296void SCHEMATIC::OnItemsAdded( std::vector<SCH_ITEM*>& aNewItems )
1297{
1299}
1300
1301
1302void SCHEMATIC::OnItemsRemoved( std::vector<SCH_ITEM*>& aRemovedItems )
1303{
1305}
1306
1307
1308void SCHEMATIC::OnItemsChanged( std::vector<SCH_ITEM*>& aItems )
1309{
1311}
1312
1313
1318
1319
1321{
1322 if( !alg::contains( m_listeners, aListener ) )
1323 m_listeners.push_back( aListener );
1324}
1325
1326
1328{
1329 auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
1330
1331 if( i != m_listeners.end() )
1332 {
1333 std::iter_swap( i, m_listeners.end() - 1 );
1334 m_listeners.pop_back();
1335 }
1336}
1337
1338
1340{
1341 m_listeners.clear();
1342}
1343
1344
1346{
1347 // Use a sorted sheetList to reduce file churn
1348 SCH_SHEET_LIST sheetList = Hierarchy();
1349 ERC_SETTINGS& ercSettings = ErcSettings();
1350
1351 ercSettings.m_ErcExclusions.clear();
1352 ercSettings.m_ErcExclusionComments.clear();
1353
1354 for( unsigned i = 0; i < sheetList.size(); i++ )
1355 {
1356 for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) )
1357 {
1358 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
1359
1360 if( marker->IsExcluded() )
1361 {
1362 wxString serialized = marker->SerializeToString();
1363 ercSettings.m_ErcExclusions.insert( serialized );
1364 ercSettings.m_ErcExclusionComments[serialized] = marker->GetComment();
1365 }
1366 }
1367 }
1368}
1369
1370
1372{
1373 SCH_SHEET_LIST sheetList = Hierarchy();
1374
1375 for( SCH_MARKER* marker : ResolveERCExclusions() )
1376 {
1377 SCH_SHEET_PATH errorPath;
1378 ignore_unused( sheetList.ResolveItem( marker->GetRCItem()->GetMainItemID(), &errorPath ) );
1379
1380 if( errorPath.LastScreen() )
1381 errorPath.LastScreen()->Append( marker );
1382 else
1383 RootScreen()->Append( marker );
1384 }
1385
1386 // Once we have the ERC Exclusions, record them in the project file so that
1387 // they are retained even before the schematic is saved (PCB Editor can also save the project)
1389}
1390
1391
1393{
1394 return static_cast<EMBEDDED_FILES*>( this );
1395}
1396
1397
1399{
1400 return static_cast<const EMBEDDED_FILES*>( this );
1401}
1402
1403
1404void SCHEMATIC::RunOnNestedEmbeddedFiles( const std::function<void( EMBEDDED_FILES* )>& aFunction )
1405{
1406 SCH_SCREENS screens( Root() );
1407
1408 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1409 {
1410 for( auto& [name, libSym] : screen->GetLibSymbols() )
1411 aFunction( libSym->GetEmbeddedFiles() );
1412 }
1413}
1414
1415
1416std::set<KIFONT::OUTLINE_FONT*> SCHEMATIC::GetFonts() const
1417{
1418 std::set<KIFONT::OUTLINE_FONT*> fonts;
1419
1420 SCH_SHEET_LIST sheetList = Hierarchy();
1421
1422 for( const SCH_SHEET_PATH& sheet : sheetList )
1423 {
1424 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
1425 {
1426 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
1427 {
1428 KIFONT::FONT* font = text->GetFont();
1429
1430 if( !font || font->IsStroke() )
1431 continue;
1432
1433 using EMBEDDING_PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
1434 auto* outline = static_cast<KIFONT::OUTLINE_FONT*>( font );
1435
1436 if( outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::EDITABLE
1437 || outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::INSTALLABLE )
1438 {
1439 fonts.insert( outline );
1440 }
1441 }
1442 }
1443 }
1444
1445 return fonts;
1446}
1447
1448
1450{
1451 std::set<KIFONT::OUTLINE_FONT*> fonts = GetFonts();
1452
1453 for( KIFONT::OUTLINE_FONT* font : fonts )
1454 {
1455 auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
1456
1457 if( !file )
1458 {
1459 wxLogTrace( "EMBED", "Failed to add font file: %s", font->GetFileName() );
1460 continue;
1461 }
1462
1464 }
1465}
1466
1467
1468std::set<const SCH_SCREEN*> SCHEMATIC::GetSchematicsSharedByMultipleProjects() const
1469{
1470 std::set<const SCH_SCREEN*> retv;
1471
1472 wxCHECK( m_rootSheet, retv );
1473
1474 SCH_SHEET_LIST hierarchy( m_rootSheet );
1475 SCH_SCREENS screens( m_rootSheet );
1476
1477 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1478 {
1479 for( const SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1480 {
1481 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
1482
1483 const std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = symbol->GetInstances();
1484
1485 for( const SCH_SYMBOL_INSTANCE& instance : symbolInstances )
1486 {
1487 if( !hierarchy.HasPath( instance.m_Path ) )
1488 {
1489 retv.insert( screen );
1490 break;
1491 }
1492 }
1493
1494 if( retv.count( screen ) )
1495 break;
1496 }
1497 }
1498
1499 return retv;
1500}
1501
1502
1504{
1505 wxCHECK( m_rootSheet, false );
1506
1507 SCH_SCREENS screens( m_rootSheet );
1508
1509 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1510 {
1511 wxCHECK2( screen, continue );
1512
1513 if( screen->GetRefCount() > 1 )
1514 return true;
1515 }
1516
1517 return false;
1518}
1519
1520
1521void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
1522{
1523 SCH_SELECTION_TOOL* selectionTool = m_schematicHolder ? m_schematicHolder->GetSelectionTool() : nullptr;
1524 std::vector<SCH_LINE*> lines;
1525 std::vector<SCH_JUNCTION*> junctions;
1526 std::vector<SCH_NO_CONNECT*> ncs;
1527 std::vector<SCH_ITEM*> items_to_remove;
1528 bool changed = true;
1529
1530 if( aScreen == nullptr )
1531 aScreen = GetCurrentScreen();
1532
1533 auto remove_item = [&]( SCH_ITEM* aItem ) -> void
1534 {
1535 changed = true;
1536
1537 if( !( aItem->GetFlags() & STRUCT_DELETED ) )
1538 {
1539 aItem->SetFlags( STRUCT_DELETED );
1540
1541 if( aItem->IsSelected() && selectionTool )
1542 selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ );
1543
1544 if( m_schematicHolder )
1545 {
1546 m_schematicHolder->RemoveFromScreen( aItem, aScreen );
1547 }
1548 aCommit->Removed( aItem, aScreen );
1549 }
1550 };
1551
1552
1553 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
1554 {
1555 if( !aScreen->IsExplicitJunction( item->GetPosition() ) )
1556 {
1557 if( item->IsSelected() || item->HasFlag( SELECTED_BY_DRAG ) )
1558 continue;
1559
1560 items_to_remove.push_back( item );
1561 }
1562 else
1563 junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
1564 }
1565
1566 for( SCH_ITEM* item : items_to_remove )
1567 remove_item( item );
1568
1569 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
1570 ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
1571
1572 alg::for_all_pairs( junctions.begin(), junctions.end(),
1573 [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
1574 {
1575 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
1576 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
1577 {
1578 return;
1579 }
1580
1581 if( aFirst->GetPosition() == aSecond->GetPosition() )
1582 remove_item( aSecond );
1583 } );
1584
1585 alg::for_all_pairs( ncs.begin(), ncs.end(),
1586 [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
1587 {
1588 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
1589 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
1590 {
1591 return;
1592 }
1593
1594 if( aFirst->GetPosition() == aSecond->GetPosition() )
1595 remove_item( aSecond );
1596 } );
1597
1598
1599 auto minX = []( const SCH_LINE* l )
1600 {
1601 return std::min( l->GetStartPoint().x, l->GetEndPoint().x );
1602 };
1603
1604 auto maxX = []( const SCH_LINE* l )
1605 {
1606 return std::max( l->GetStartPoint().x, l->GetEndPoint().x );
1607 };
1608
1609 auto minY = []( const SCH_LINE* l )
1610 {
1611 return std::min( l->GetStartPoint().y, l->GetEndPoint().y );
1612 };
1613
1614 auto maxY = []( const SCH_LINE* l )
1615 {
1616 return std::max( l->GetStartPoint().y, l->GetEndPoint().y );
1617 };
1618
1619 // Would be nice to put lines in a canonical form here by swapping
1620 // start <-> end as needed but I don't know what swapping breaks.
1621 while( changed )
1622 {
1623 changed = false;
1624 lines.clear();
1625
1626 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
1627 {
1628 if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
1629 lines.push_back( static_cast<SCH_LINE*>( item ) );
1630 }
1631
1632 // Sort by minimum X position
1633 std::sort( lines.begin(), lines.end(),
1634 [&]( const SCH_LINE* a, const SCH_LINE* b )
1635 {
1636 return minX( a ) < minX( b );
1637 } );
1638
1639 for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
1640 {
1641 SCH_LINE* firstLine = *it1;
1642
1643 if( firstLine->GetEditFlags() & STRUCT_DELETED )
1644 continue;
1645
1646 if( firstLine->IsNull() )
1647 {
1648 remove_item( firstLine );
1649 continue;
1650 }
1651
1652 int firstRightXEdge = maxX( firstLine );
1653 auto it2 = it1;
1654
1655 for( ++it2; it2 != lines.end(); ++it2 )
1656 {
1657 SCH_LINE* secondLine = *it2;
1658 int secondLeftXEdge = minX( secondLine );
1659
1660 // impossible to overlap remaining lines
1661 if( secondLeftXEdge > firstRightXEdge )
1662 break;
1663
1664 // No Y axis overlap
1665 if( !( std::max( minY( firstLine ), minY( secondLine ) )
1666 <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) )
1667 {
1668 continue;
1669 }
1670
1671 if( secondLine->GetFlags() & STRUCT_DELETED )
1672 continue;
1673
1674 if( !secondLine->IsParallel( firstLine ) || !secondLine->IsStrokeEquivalent( firstLine )
1675 || secondLine->GetLayer() != firstLine->GetLayer() )
1676 {
1677 continue;
1678 }
1679
1680 // Remove identical lines
1681 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
1682 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
1683 {
1684 remove_item( secondLine );
1685 continue;
1686 }
1687
1688 // See if we can merge an overlap (or two colinear touching segments with
1689 // no junction where they meet).
1690 SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
1691
1692 if( mergedLine != nullptr )
1693 {
1694 remove_item( firstLine );
1695 remove_item( secondLine );
1696
1697 if( m_schematicHolder )
1698 {
1699 m_schematicHolder->AddToScreen( mergedLine, aScreen );
1700 }
1701
1702 aCommit->Added( mergedLine, aScreen );
1703
1704 if( selectionTool && ( firstLine->IsSelected() || secondLine->IsSelected() ) )
1705 selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
1706
1707 break;
1708 }
1709 }
1710 }
1711 }
1712}
1713
1714
1716 TOOL_MANAGER* aToolManager, PROGRESS_REPORTER* aProgressReporter,
1717 KIGFX::SCH_VIEW* aSchView,
1718 std::function<void( SCH_ITEM* )>* aChangedItemHandler,
1719 PICKED_ITEMS_LIST* aLastChangeList )
1720{
1721 SCHEMATIC_SETTINGS& settings = Settings();
1723 SCH_SHEET_LIST list = Hierarchy();
1724 SCH_COMMIT localCommit( aToolManager );
1725
1726 if( !aCommit )
1727 aCommit = &localCommit;
1728
1729 PROF_TIMER timer;
1730
1731 // Ensure schematic graph is accurate
1732 if( aCleanupFlags == LOCAL_CLEANUP )
1733 {
1734 CleanUp( aCommit, GetCurrentScreen() );
1735 }
1736 else if( aCleanupFlags == GLOBAL_CLEANUP )
1737 {
1738 for( const SCH_SHEET_PATH& sheet : list )
1739 CleanUp( aCommit, sheet.LastScreen() );
1740 }
1741
1742 timer.Stop();
1743 wxLogTrace( "CONN_PROFILE", "SchematicCleanUp() %0.4f ms", timer.msecs() );
1744
1745 if( settings.m_IntersheetRefsShow )
1747
1748 if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP
1749 || aLastChangeList == nullptr || ConnectionGraph()->IsMinor() )
1750 {
1751 // Clear all resolved netclass caches in case labels have changed
1752 m_project->GetProjectFile().NetSettings()->ClearAllCaches();
1753
1754 // Update all rule areas so we can cascade implied connectivity changes
1755 std::unordered_set<SCH_SCREEN*> all_screens;
1756
1757 for( const SCH_SHEET_PATH& path : list )
1758 all_screens.insert( path.LastScreen() );
1759
1760 SCH_RULE_AREA::UpdateRuleAreasInScreens( all_screens, aSchView );
1761
1762 // Recalculate all connectivity
1763 ConnectionGraph()->Recalculate( list, true, aChangedItemHandler, aProgressReporter );
1764 }
1765 else
1766 {
1767 struct CHANGED_ITEM
1768 {
1769 SCH_ITEM* item;
1770 SCH_ITEM* linked_item;
1771 SCH_SCREEN* screen;
1772 };
1773
1774 // Final change sets
1775 std::set<SCH_ITEM*> changed_items;
1776 std::set<VECTOR2I> pts;
1777 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> item_paths;
1778
1779 // Working change sets
1780 std::unordered_set<SCH_SCREEN*> changed_screens;
1781 std::set<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> changed_rule_areas;
1782 std::vector<CHANGED_ITEM> changed_connectable_items;
1783
1784 // Lambda to add an item to the connectivity update sets
1785 auto addItemToChangeSet = [&changed_items, &pts, &item_paths]( CHANGED_ITEM itemData )
1786 {
1787 std::vector<SCH_SHEET_PATH>& paths = itemData.screen->GetClientSheetPaths();
1788
1789 std::vector<VECTOR2I> tmp_pts = itemData.item->GetConnectionPoints();
1790 pts.insert( tmp_pts.begin(), tmp_pts.end() );
1791 changed_items.insert( itemData.item );
1792
1793 for( SCH_SHEET_PATH& path : paths )
1794 item_paths.insert( std::make_pair( path, itemData.item ) );
1795
1796 if( !itemData.linked_item || !itemData.linked_item->IsConnectable() )
1797 return;
1798
1799 tmp_pts = itemData.linked_item->GetConnectionPoints();
1800 pts.insert( tmp_pts.begin(), tmp_pts.end() );
1801 changed_items.insert( itemData.linked_item );
1802
1803 // We have to directly add the pins here because the link may not exist on the schematic
1804 // anymore and so won't be picked up by GetScreen()->Items().Overlapping() below.
1805 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( itemData.linked_item ) )
1806 {
1807 std::vector<SCH_PIN*> pins = symbol->GetPins();
1808 changed_items.insert( pins.begin(), pins.end() );
1809 }
1810
1811 for( SCH_SHEET_PATH& path : paths )
1812 item_paths.insert( std::make_pair( path, itemData.linked_item ) );
1813 };
1814
1815 // Get all changed connectable items and determine all changed screens
1816 for( unsigned ii = 0; ii < aLastChangeList->GetCount(); ++ii )
1817 {
1818 switch( aLastChangeList->GetPickedItemStatus( ii ) )
1819 {
1820 // Only care about changed, new, and deleted items, the other
1821 // cases are not connectivity-related
1822 case UNDO_REDO::CHANGED:
1823 case UNDO_REDO::NEWITEM:
1824 case UNDO_REDO::DELETED: break;
1825
1826 default: continue;
1827 }
1828
1829 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItem( ii ) );
1830
1831 if( item )
1832 {
1833 SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( aLastChangeList->GetScreenForItem( ii ) );
1834 changed_screens.insert( screen );
1835
1836 if( item->Type() == SCH_RULE_AREA_T )
1837 {
1838 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( item );
1839 changed_rule_areas.insert( { ruleArea, screen } );
1840 }
1841 else if( item->IsConnectable() )
1842 {
1843 SCH_ITEM* linked_item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItemLink( ii ) );
1844 changed_connectable_items.push_back( { item, linked_item, screen } );
1845 }
1846 }
1847 }
1848
1849 // Update rule areas in changed screens to propagate any directive connectivity changes
1850 std::vector<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> forceUpdateRuleAreas =
1851 SCH_RULE_AREA::UpdateRuleAreasInScreens( changed_screens, aSchView );
1852
1853 std::for_each( forceUpdateRuleAreas.begin(), forceUpdateRuleAreas.end(),
1854 [&]( std::pair<SCH_RULE_AREA*, SCH_SCREEN*>& updatedRuleArea )
1855 {
1856 changed_rule_areas.insert( updatedRuleArea );
1857 } );
1858
1859 // If a SCH_RULE_AREA was changed, we need to add all past and present contained items to
1860 // update their connectivity
1861 std::map<KIID, EDA_ITEM*> itemMap;
1862 list.FillItemMap( itemMap );
1863
1864 auto addPastAndPresentContainedItems = [&]( SCH_RULE_AREA* changedRuleArea, SCH_SCREEN* screen )
1865 {
1866 for( const KIID& pastItem : changedRuleArea->GetPastContainedItems() )
1867 {
1868 if( itemMap.contains( pastItem ) )
1869 addItemToChangeSet( { static_cast<SCH_ITEM*>( itemMap[pastItem] ), nullptr, screen } );
1870 }
1871
1872 for( SCH_ITEM* containedItem : changedRuleArea->GetContainedItems() )
1873 addItemToChangeSet( { containedItem, nullptr, screen } );
1874 };
1875
1876 for( const auto& [changedRuleArea, screen] : changed_rule_areas )
1877 addPastAndPresentContainedItems( changedRuleArea, screen );
1878
1879 // Add all changed items, and associated items, to the change set
1880 for( CHANGED_ITEM& changed_item_data : changed_connectable_items )
1881 {
1882 addItemToChangeSet( changed_item_data );
1883
1884 // If a SCH_DIRECTIVE_LABEL was changed which is attached to a SCH_RULE_AREA, we need
1885 // to add the contained items to the change set to force update of their connectivity
1886 if( changed_item_data.item->Type() == SCH_DIRECTIVE_LABEL_T )
1887 {
1888 const std::vector<VECTOR2I> labelConnectionPoints = changed_item_data.item->GetConnectionPoints();
1889
1890 auto candidateRuleAreas = changed_item_data.screen->Items().Overlapping(
1891 SCH_RULE_AREA_T, changed_item_data.item->GetBoundingBox() );
1892
1893 for( SCH_ITEM* candidateRuleArea : candidateRuleAreas )
1894 {
1895 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( candidateRuleArea );
1896 std::vector<SHAPE*> borderShapes = ruleArea->MakeEffectiveShapes( true );
1897
1898 if( ruleArea->GetPolyShape().CollideEdge( labelConnectionPoints[0], nullptr, 5 ) )
1899 addPastAndPresentContainedItems( ruleArea, changed_item_data.screen );
1900 }
1901 }
1902 }
1903
1904 for( const VECTOR2I& pt : pts )
1905 {
1906 for( SCH_ITEM* item : GetCurrentScreen()->Items().Overlapping( pt ) )
1907 {
1908 // Leave this check in place. Overlapping items are not necessarily connectable.
1909 if( !item->IsConnectable() )
1910 continue;
1911
1912 SCH_SCREEN* screen = GetCurrentScreen();
1913 std::vector<SCH_SHEET_PATH>& paths = screen->GetClientSheetPaths();
1914
1915 if( item->Type() == SCH_LINE_T )
1916 {
1917 if( item->HitTest( pt ) )
1918 {
1919 changed_items.insert( item );
1920
1921 for( SCH_SHEET_PATH& path : paths )
1922 item_paths.insert( std::make_pair( path, item ) );
1923 }
1924 }
1925 else if( item->Type() == SCH_SYMBOL_T && item->IsConnected( pt ) )
1926 {
1927 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1928 std::vector<SCH_PIN*> pins = symbol->GetPins();
1929
1930 changed_items.insert( pins.begin(), pins.end() );
1931
1932 for( SCH_PIN* pin : pins )
1933 {
1934 for( SCH_SHEET_PATH& path : paths )
1935 item_paths.insert( std::make_pair( path, pin ) );
1936 }
1937 }
1938 else if( item->Type() == SCH_SHEET_T )
1939 {
1940 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1941
1942 wxCHECK2( sheet, continue );
1943
1944 std::vector<SCH_SHEET_PIN*> sheetPins = sheet->GetPins();
1945 changed_items.insert( sheetPins.begin(), sheetPins.end() );
1946
1947 for( SCH_SHEET_PIN* pin : sheetPins )
1948 {
1949 for( SCH_SHEET_PATH& path : paths )
1950 item_paths.insert( std::make_pair( path, pin ) );
1951 }
1952 }
1953 else
1954 {
1955 if( item->IsConnected( pt ) )
1956 {
1957 changed_items.insert( item );
1958
1959 for( SCH_SHEET_PATH& path : paths )
1960 item_paths.insert( std::make_pair( path, item ) );
1961 }
1962 }
1963 }
1964 }
1965
1966 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> all_items =
1967 ConnectionGraph()->ExtractAffectedItems( changed_items );
1968
1969 all_items.insert( item_paths.begin(), item_paths.end() );
1970
1971 CONNECTION_GRAPH new_graph( this );
1972
1973 new_graph.SetLastCodes( ConnectionGraph() );
1974
1975 std::shared_ptr<NET_SETTINGS> netSettings = m_project->GetProjectFile().NetSettings();
1976
1977 std::set<wxString> affectedNets;
1978
1979 for( auto& [path, item] : all_items )
1980 {
1981 wxCHECK2( item, continue );
1982 item->SetConnectivityDirty();
1983 SCH_CONNECTION* conn = item->Connection();
1984
1985 if( conn )
1986 affectedNets.insert( conn->Name() );
1987 }
1988
1989 // For label items, also capture the old net name from the linked item (original state).
1990 // This ensures cache is cleared for both old and new net names when a label is renamed.
1991 for( const CHANGED_ITEM& changedItem : changed_connectable_items )
1992 {
1993 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( changedItem.item ) )
1994 {
1995 const wxString& driverName = label->GetCachedDriverName();
1996
1997 if( !driverName.IsEmpty() )
1998 affectedNets.insert( driverName );
1999 }
2000
2001 if( SCH_LABEL_BASE* linkedLabel = dynamic_cast<SCH_LABEL_BASE*>( changedItem.linked_item ) )
2002 {
2003 const wxString& driverName = linkedLabel->GetCachedDriverName();
2004
2005 if( !driverName.IsEmpty() )
2006 affectedNets.insert( driverName );
2007 }
2008 }
2009
2010 // Reset resolved netclass cache for this connection
2011 for( const wxString& netName : affectedNets )
2012 netSettings->ClearCacheForNet( netName );
2013
2014 new_graph.Recalculate( list, false, aChangedItemHandler, aProgressReporter );
2015 ConnectionGraph()->Merge( new_graph );
2016 }
2017
2018 if( !localCommit.Empty() )
2019 localCommit.Push( _( "Schematic Cleanup" ) );
2020}
2021
2022
2024{
2025 Reset();
2026
2028
2029 // Create the actual first top-level sheet
2030 SCH_SHEET* rootSheet = new SCH_SHEET( this );
2031 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
2032
2033 const_cast<KIID&>( rootSheet->m_Uuid ) = rootScreen->GetUuid();
2034 rootSheet->SetScreen( rootScreen );
2035 rootScreen->SetFileName( "untitled.kicad_sch" ); // Set default filename to avoid conflicts
2036 rootScreen->SetPageNumber( wxT( "1" ) );
2037
2038 // Don't leave root page number empty
2039 SCH_SHEET_PATH rootSheetPath;
2040 rootSheetPath.push_back( m_rootSheet );
2041 rootSheetPath.push_back( rootSheet );
2042 rootSheetPath.SetPageNumber( wxT( "1" ) );
2043
2044 SetTopLevelSheets( { rootSheet } );
2045}
2046
2047
2048std::vector<SCH_SHEET*> SCHEMATIC::GetTopLevelSheets() const
2049{
2050 return m_topLevelSheets;
2051}
2052
2054{
2055 if( aIndex < 0 )
2056 return nullptr;
2057
2058 size_t index = static_cast<size_t>( aIndex );
2059
2060 if( index >= m_topLevelSheets.size() )
2061 return nullptr;
2062
2063 return m_topLevelSheets[index];
2064}
2065
2067{
2068 wxCHECK_RET( aSheet, wxS( "Cannot add null sheet!" ) );
2069 wxCHECK_RET( aSheet->GetScreen(), wxS( "Cannot add virtual root as top-level sheet!" ) );
2070
2072
2073 // Set parent to virtual root
2074 aSheet->SetParent( m_rootSheet );
2075
2076 // Add to the virtual root's screen if it exists
2077 if( m_rootSheet->GetScreen() )
2078 {
2079 m_rootSheet->GetScreen()->Append( aSheet );
2080 }
2081
2082 // Add to our list
2083 m_topLevelSheets.push_back( aSheet );
2084
2086 rebuildHierarchyState( true );
2087}
2088
2090{
2091 auto it = std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet );
2092
2093 if( it == m_topLevelSheets.end() )
2094 return false;
2095
2096 if( m_topLevelSheets.size() == 1 )
2097 return false;
2098
2099 m_topLevelSheets.erase( it );
2100
2101 if( m_rootSheet && m_rootSheet->GetScreen() )
2102 m_rootSheet->GetScreen()->Items().remove( aSheet );
2103
2104 // If we're removing the current sheet, switch to another one
2105 if( !m_currentSheet->empty() && m_currentSheet->at( 0 ) == aSheet )
2106 {
2107 m_currentSheet->clear();
2108 if( !m_topLevelSheets.empty() )
2109 {
2110 m_currentSheet->push_back( m_topLevelSheets[0] );
2111 }
2112 }
2113
2114 rebuildHierarchyState( true );
2115 return true;
2116}
2117
2118
2119bool SCHEMATIC::IsTopLevelSheet( const SCH_SHEET* aSheet ) const
2120{
2121 return std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet ) != m_topLevelSheets.end();
2122}
2123
2124
2126{
2127 SCH_SHEET_LIST hierarchy;
2128
2129 wxLogTrace( traceSchSheetPaths, "BuildSheetListSortedByPageNumbers: %zu top-level sheets",
2130 m_topLevelSheets.size() );
2131
2132 // Can't build hierarchy without top-level sheets
2133 if( m_topLevelSheets.empty() )
2134 return hierarchy;
2135
2136 // For each top-level sheet, build its hierarchy
2137 for( SCH_SHEET* sheet : m_topLevelSheets )
2138 {
2139 if( sheet )
2140 {
2141 wxLogTrace( traceSchSheetPaths, " Top-level sheet: '%s' (UUID=%s, isVirtualRoot=%d)", sheet->GetName(),
2142 sheet->m_Uuid.AsString(), sheet->m_Uuid == niluuid ? 1 : 0 );
2143
2144 // Build the sheet list for this top-level sheet
2145 SCH_SHEET_LIST sheetList;
2146 sheetList.BuildSheetList( sheet, false );
2147
2148 // Add all sheets from this top-level sheet's hierarchy
2149 for( const SCH_SHEET_PATH& path : sheetList )
2150 {
2151 hierarchy.push_back( path );
2152 }
2153 }
2154 }
2155
2156 hierarchy.SortByPageNumbers();
2157
2158 return hierarchy;
2159}
2160
2161
2163{
2164 SCH_SHEET_LIST sheets;
2165
2166 // For each top-level sheet, build its hierarchy
2167 for( SCH_SHEET* sheet : m_topLevelSheets )
2168 {
2169 if( sheet )
2170 {
2171 SCH_SHEET_LIST sheetList;
2172 sheetList.BuildSheetList( sheet, false );
2173
2174 // Add all sheets from this top-level sheet's hierarchy
2175 for( const SCH_SHEET_PATH& path : sheetList )
2176 {
2177 sheets.push_back( path );
2178 }
2179 }
2180 }
2181
2182 return sheets;
2183}
2184
2185
2187{
2188 wxArrayString variantNames;
2189
2190 // There is no default variant name. This is just a place holder for UI controls.
2191 variantNames.Add( GetDefaultVariantName() );
2192
2193 for( const wxString& name : m_variantNames )
2194 variantNames.Add( name );
2195
2196 variantNames.Sort( SortVariantNames );
2197
2198 return variantNames;
2199}
2200
2201
2203{
2204 if( m_currentVariant.IsEmpty() || ( m_currentVariant == GetDefaultVariantName() ) )
2205 return wxEmptyString;
2206
2207 return m_currentVariant;
2208}
2209
2210
2211void SCHEMATIC::SetCurrentVariant( const wxString& aVariantName )
2212{
2213 wxString newVariant;
2214
2215 // Internally an empty string is the default variant. Set to default if the variant name doesn't exist.
2216 if( ( aVariantName != GetDefaultVariantName() ) && m_variantNames.contains( aVariantName ) )
2217 newVariant = aVariantName;
2218
2219 if( m_currentVariant == newVariant )
2220 return;
2221
2222 m_currentVariant = newVariant;
2223
2224 // Variant-specific field values affect text geometry, so bounding box caches computed
2225 // with the previous variant's text are now stale.
2226 if( m_rootSheet )
2227 {
2228 SCH_SCREENS allScreens( m_rootSheet );
2229
2230 for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
2231 {
2232 for( SCH_ITEM* item : screen->Items() )
2233 item->ClearCaches();
2234 }
2235 }
2236}
2237
2238
2239void SCHEMATIC::AddVariant( const wxString& aVariantName )
2240{
2241 m_variantNames.emplace( aVariantName );
2242
2243 // Ensure the variant is registered in the project file
2244 auto& descriptions = Settings().m_VariantDescriptions;
2245
2246 if( descriptions.find( aVariantName ) == descriptions.end() )
2247 descriptions[aVariantName] = wxEmptyString;
2248}
2249
2250
2251void SCHEMATIC::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit )
2252{
2253 wxCHECK( m_rootSheet, /* void */ );
2254
2255 SCH_SCREENS allScreens( m_rootSheet );
2256
2257 allScreens.DeleteVariant( aVariantName, aCommit );
2258
2259 m_variantNames.erase( aVariantName );
2260 Settings().m_VariantDescriptions.erase( aVariantName );
2261}
2262
2263
2264void SCHEMATIC::RenameVariant( const wxString& aOldName, const wxString& aNewName,
2265 SCH_COMMIT* aCommit )
2266{
2267 wxCHECK( m_rootSheet, /* void */ );
2268 wxCHECK( !aOldName.IsEmpty() && !aNewName.IsEmpty(), /* void */ );
2269 wxCHECK( m_variantNames.contains( aOldName ), /* void */ );
2270
2271 m_variantNames.erase( aOldName );
2272 m_variantNames.insert( aNewName );
2273
2274 auto& descriptions = Settings().m_VariantDescriptions;
2275
2276 if( descriptions.count( aOldName ) )
2277 {
2278 descriptions[aNewName] = descriptions[aOldName];
2279 descriptions.erase( aOldName );
2280 }
2281
2282 if( m_currentVariant == aOldName )
2283 m_currentVariant = aNewName;
2284
2285 SCH_SCREENS allScreens( m_rootSheet );
2286 allScreens.RenameVariant( aOldName, aNewName, aCommit );
2287}
2288
2289
2290void SCHEMATIC::CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
2291 SCH_COMMIT* aCommit )
2292{
2293 wxCHECK( m_rootSheet, /* void */ );
2294 wxCHECK( !aSourceVariant.IsEmpty() && !aNewVariant.IsEmpty(), /* void */ );
2295 wxCHECK( m_variantNames.contains( aSourceVariant ), /* void */ );
2296 wxCHECK( !m_variantNames.contains( aNewVariant ), /* void */ );
2297
2298 AddVariant( aNewVariant );
2299
2300 auto& descriptions = Settings().m_VariantDescriptions;
2301
2302 if( descriptions.count( aSourceVariant ) )
2303 descriptions[aNewVariant] = descriptions[aSourceVariant];
2304
2305 SCH_SCREENS allScreens( m_rootSheet );
2306 allScreens.CopyVariant( aSourceVariant, aNewVariant, aCommit );
2307}
2308
2309
2310wxString SCHEMATIC::GetVariantDescription( const wxString& aVariantName ) const
2311{
2312 const auto& descriptions = Settings().m_VariantDescriptions;
2313 auto it = descriptions.find( aVariantName );
2314
2315 if( it != descriptions.end() )
2316 return it->second;
2317
2318 return wxEmptyString;
2319}
2320
2321
2322void SCHEMATIC::SetVariantDescription( const wxString& aVariantName, const wxString& aDescription )
2323{
2324 auto& descriptions = Settings().m_VariantDescriptions;
2325
2326 if( aDescription.IsEmpty() )
2327 descriptions.erase( aVariantName );
2328 else
2329 descriptions[aVariantName] = aDescription;
2330}
2331
2332
2334{
2335 if( m_rootSheet && m_rootSheet->GetScreen() )
2336 {
2337 SCH_SCREENS screens( m_rootSheet );
2338 std::set<wxString> variantNames = screens.GetVariantNames();
2339 m_variantNames.insert( variantNames.begin(), variantNames.end() );
2340
2341 // Register any unknown variants to the project file with empty descriptions
2342 auto& descriptions = Settings().m_VariantDescriptions;
2343
2344 for( const wxString& name : variantNames )
2345 {
2346 if( descriptions.find( name ) == descriptions.end() )
2347 descriptions[name] = wxEmptyString;
2348 }
2349
2350 // Also include variants from the project file that may not have any diffs yet.
2351 // This ensures newly created variants with no symbol changes are preserved.
2352 for( const auto& [name, description] : descriptions )
2353 m_variantNames.insert( name );
2354 }
2355}
2356
2357
2358void SCHEMATIC::SaveToHistory( const wxString& aProjectPath, std::vector<wxString>& aFiles )
2359{
2360 if( !IsValid() )
2361 return;
2362
2363 wxString projPath = m_project->GetProjectPath();
2364
2365 if( projPath.IsEmpty() )
2366 return; // no project yet
2367
2368 // Verify we're saving for the correct project
2369 if( !projPath.IsSameAs( aProjectPath ) )
2370 {
2371 wxLogTrace( traceAutoSave, wxS( "[history] sch saver skipping - project path mismatch: %s vs %s" ), projPath,
2372 aProjectPath );
2373 return;
2374 }
2375
2376 // Ensure project path has trailing separator for StartsWith tests & Mid calculations.
2377 if( !projPath.EndsWith( wxFILE_SEP_PATH ) )
2378 projPath += wxFILE_SEP_PATH;
2379
2380 wxFileName historyRoot( projPath, wxEmptyString );
2381 historyRoot.AppendDir( wxS( ".history" ) );
2382 wxString historyRootPath = historyRoot.GetPath();
2383
2384 // Iterate full schematic hierarchy (all sheets & their screens).
2385 SCH_SHEET_LIST sheetList = Hierarchy();
2386
2387 // Acquire plugin once.
2388 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
2389
2390 for( const SCH_SHEET_PATH& path : sheetList )
2391 {
2392 SCH_SHEET* sheet = path.Last();
2393 SCH_SCREEN* screen = path.LastScreen();
2394
2395 if( !sheet || !screen )
2396 continue;
2397
2398 wxFileName abs = m_project->AbsolutePath( screen->GetFileName() );
2399
2400 if( !abs.IsOk() )
2401 continue; // no filename
2402
2403 wxString absPath = abs.GetFullPath();
2404
2405 if( absPath.IsEmpty() || !absPath.StartsWith( projPath ) )
2406 continue; // external / unsaved subsheet
2407
2408 wxString rel = absPath.Mid( projPath.length() );
2409
2410 // Destination mirrors project-relative path under .history
2411 wxFileName dst( rel );
2412
2413 if( dst.IsRelative() )
2414 dst.MakeAbsolute( historyRootPath );
2415 else
2416 dst.SetPath( historyRootPath );
2417
2418 // Ensure destination directory exists
2419 wxFileName dstDir( dst );
2420 dstDir.SetFullName( wxEmptyString );
2421
2422 if( !dstDir.DirExists() )
2423 wxFileName::Mkdir( dstDir.GetPath(), 0777, wxPATH_MKDIR_FULL );
2424
2425 try
2426 {
2427 pi->SaveSchematicFile( dst.GetFullPath(), sheet, this );
2428 aFiles.push_back( dst.GetFullPath() );
2429 wxLogTrace( traceAutoSave, wxS( "[history] sch saver exported sheet '%s' -> '%s'" ), absPath,
2430 dst.GetFullPath() );
2431 }
2432 catch( const IO_ERROR& ioe )
2433 {
2434 wxLogTrace( traceAutoSave, wxS( "[history] sch saver export failed for '%s': %s" ), absPath,
2435 wxString::FromUTF8( ioe.What() ) );
2436 }
2437 }
2438}
int index
const char * name
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:79
void SetVirtualPageNumber(int aPageNumber)
Definition base_screen.h:76
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:72
bool Empty() const
Definition commit.h:137
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:106
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Definition commit.h:96
Calculate the connectivity of a schematic and generates 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:154
const KIID m_Uuid
Definition eda_item.h:527
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:111
bool IsSelected() const
Definition eda_item.h:128
EDA_ITEM * GetParent() const
Definition eda_item.h:113
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:151
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:41
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:337
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:80
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
EMBEDDED_FILES()=default
Container for ERC settings.
std::map< wxString, wxString > m_ErcExclusionComments
std::set< wxString > m_ErcExclusions
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:38
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:98
virtual bool IsStroke() const
Definition font.h:105
Class OUTLINE_FONT implements outline font drawing.
Definition kiid.h:49
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
bool IsExcluded() const
Definition marker_base.h:93
std::shared_ptr< RC_ITEM > GetRCItem() const
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:94
wxString GetComment() const
static void ConvertToSpiceMarkup(wxString *aNetName)
Remove formatting wrappers and replace illegal spice net name characters with underscores.
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:49
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:88
double msecs(bool aSinceLast=false)
Definition profile.h:149
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:65
const wxString & Name() const
Definition property.h:220
static PROPERTY_MANAGER & Instance()
void UnregisterListeners(TYPE_ID aType)
void RegisterListener(TYPE_ID aType, PROPERTY_LISTENER aListenerFunc)
Registers a listener for the given type.
wxString GetSettingsKey() const
Definition rc_item.h:179
virtual void OnSchItemsRemoved(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:65
virtual void OnSchItemsChanged(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:66
virtual void OnSchSheetChanged(SCHEMATIC &aSch)
Definition schematic.h:70
virtual void OnSchItemsAdded(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem)
Definition schematic.h:64
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:624
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:578
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:600
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 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:575
std::vector< SCH_SHEET * > m_topLevelSheets
List of top-level sheets (direct children of virtual root)
Definition schematic.h:566
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...
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)
int FixupJunctionsAfterImport()
Add junctions to this schematic where required.
virtual ~SCHEMATIC()
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 EmbedFonts() override
Embed fonts in the schematic.
SCHEMATIC_SETTINGS & Settings() const
SCH_SCREEN * GetCurrentScreen() const
Definition schematic.h:197
wxString ConvertKIIDsToRefs(const wxString &aSource) const
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Definition schematic.h:126
void ensureVirtualRoot()
void ensureCurrentSheetIsTopLevel()
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:618
std::map< wxString, std::set< int > > & GetPageRefsMap()
Definition schematic.h:244
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::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.
bool Contains(const SCH_REFERENCE &aRef) const
Check if the schematic contains the specified reference.
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...
std::map< int, wxString > GetVirtualPageToSheetPagesMap() const
EMBEDDED_FILES * GetEmbeddedFiles() override
PROJECT * m_project
Definition schematic.h:560
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:199
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
SCHEMATIC(PROJECT *aPrj)
Definition schematic.cpp:67
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:616
void InvokeListeners(Func &&aFunc, Args &&... args)
Definition schematic.h:549
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:172
void updateProjectBusAliases()
void SetVariantDescription(const wxString &aVariantName, const wxString &aDescription)
Set the description for a variant.
static bool m_IsSchematicExists
True if a SCHEMATIC exists, false if not.
Definition schematic.h:527
void RemoveAllListeners()
Remove all listeners.
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
void GetContextualTextVars(wxArrayString *aVars) const
void ensureDefaultTopLevelSheet()
SCH_SHEET & Root() const
Definition schematic.h:132
std::map< int, wxString > GetVirtualPageToSheetNamesMap() const
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
std::vector< SCH_SHEET * > GetTopLevelSheets() const
Get the list of top-level sheets.
wxArrayString GetVariantNamesForUI() const
Return an array of variant names for using in wxWidgets UI controls.
void RunOnNestedEmbeddedFiles(const std::function< void(EMBEDDED_FILES *)> &aFunction) override
Provide access to nested embedded files, such as symbols in schematics and footprints in boards.
SCHEMATIC_HOLDER * m_schematicHolder
What currently "Holds" the schematic, i.e.
Definition schematic.h:610
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:605
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:187
bool ResolveCrossReference(wxString *token, int aDepth) const
Resolves text vars that refer to other items.
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)
Generate the connection data for the entire schematic hierarchy.
void SaveToHistory(const wxString &aProjectPath, std::vector< wxString > &aFiles)
Save schematic files to the .history directory.
std::map< wxString, double > m_operatingPoints
Simulation operating points for text variable substitution.
Definition schematic.h:595
ERC_SETTINGS & ErcSettings() const
std::set< wxString > m_variantNames
Definition schematic.h:620
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:563
virtual void Push(const wxString &aMessage=wxT("A commit"), 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:120
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
virtual bool IsConnectable() const
Definition sch_item.h:527
int GetUnit() const
Definition sch_item.h:239
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:341
VECTOR2I GetPosition() const override
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const override
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
SCH_LINE * NonGroupAware_BreakAt(const VECTOR2I &aPoint)
This version should only be used when importing files.
Definition sch_line.cpp:590
bool IsParallel(const SCH_LINE *aLine) const
Definition sch_line.cpp:444
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
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:457
bool IsNull() const
Definition sch_line.h:137
bool IsStrokeEquivalent(const SCH_LINE *aLine)
Definition sch_line.h:204
bool IsEndPoint(const VECTOR2I &aPoint) const override
Test if aPt is an end point of this schematic object.
Definition sch_line.h:91
static SCH_MARKER * DeserializeFromString(const SCH_SHEET_LIST &aSheetList, const wxString &data)
wxString SerializeToString() const
bool IsLegacyMarker() const
Determine if this marker is legacy (i.e.
Definition sch_marker.h:121
VECTOR2I GetPosition() const override
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
A helper to define a symbol's reference designator in a schematic.
const SCH_SHEET_PATH & GetSheetPath() const
SCH_SYMBOL * GetSymbol() const
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const override
Make a set of SHAPE objects representing the EDA_SHAPE.
const std::unordered_set< SCH_ITEM * > & GetContainedItems() const
Return a set of all items contained within the rule area.
static std::vector< std::pair< SCH_RULE_AREA *, SCH_SCREEN * > > UpdateRuleAreasInScreens(std::unordered_set< SCH_SCREEN * > &screens, KIGFX::SCH_VIEW *view)
Update all rule area connectvity / caches in the given sheet paths.
const std::unordered_set< KIID > & GetPastContainedItems() const
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:749
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:188
bool IsExplicitJunction(const VECTOR2I &aPosition) const
Indicate that a junction dot is necessary at the given location.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
const wxString & GetFileName() const
Definition sch_screen.h:154
const KIID & GetUuid() const
Definition sch_screen.h:532
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
const TITLE_BLOCK & GetTitleBlock() const
Definition sch_screen.h:165
void Update(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Update aItem's bounding box in the tree.
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 GetSymbols(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
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
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 GetName() const
Definition sch_sheet.h:137
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:140
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:228
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:76
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:135
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:165
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX *aClosestVertex=nullptr, int aClearance=0) const
Check whether aPoint collides with any edge of any of the contours of the polygon.
Helper class to recognize Spice formatted values.
Definition spice_value.h:56
wxString ToString() const
Return string value as when converting double to string (e.g.
bool TextVarResolver(wxString *aToken, const PROJECT *aProject, int aFlags=0) const
static void GetContextualTextVars(wxArrayString *aVars)
Master controller class:
The common library.
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
#define SELECTED_BY_DRAG
Item was algorithmically selected as a dragged item.
#define STRUCT_DELETED
flag indication structures to be erased
const wxChar *const traceAutoSave
Flag to enable auto save feature debug tracing.
const wxChar *const traceSchSheetPaths
Flag to enable debug output of schematic symbol sheet path manipulation code.
void ignore_unused(const T &)
Definition ignore.h:24
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
KIID niluuid(0)
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_BUS
Definition layer_ids.h:453
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
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:84
#define _HKI(x)
Definition page_info.cpp:44
#define TYPE_HASH(x)
Definition property.h:74
void CollectOtherUnits(const wxString &aRef, int aUnit, const LIB_ID &aLibId, SCH_SHEET_PATH &aSheet, std::vector< SCH_SYMBOL * > *otherUnits)
@ AUTOPLACE_AUTO
Definition sch_item.h:71
SCH_CLEANUP_FLAGS
Definition schematic.h:74
@ LOCAL_CLEANUP
Definition schematic.h:76
@ GLOBAL_CLEANUP
Definition schematic.h:77
wxString GetDefaultVariantName()
int SortVariantNames(const wxString &aLhs, const wxString &aRhs)
A simple container for schematic symbol instance information.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS_PIN * pin
wxLogTrace helper definitions.
@ SCH_LINE_T
Definition typeinfo.h:167
@ SCH_NO_CONNECT_T
Definition typeinfo.h:164
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:175
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_MARKER_T
Definition typeinfo.h:162
@ SCH_RULE_AREA_T
Definition typeinfo.h:174
@ SCHEMATIC_T
Definition typeinfo.h:208
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:172
@ SCH_JUNCTION_T
Definition typeinfo.h:163
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695