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