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