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