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