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