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