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