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
142
152
153
155{
156 delete m_rootSheet;
157
158 m_rootSheet = nullptr;
159 m_topLevelSheets.clear();
160
161 m_connectionGraph->Reset();
162 m_currentSheet->clear();
163
164 m_busAliases.clear();
165}
166
167
169{
170 if( m_project )
171 {
172 PROJECT_FILE& project = m_project->GetProjectFile();
173
174 // d'tor will save settings to file
175 delete project.m_ErcSettings;
176 project.m_ErcSettings = nullptr;
177
178 // d'tor will save settings to file
179 delete project.m_SchematicSettings;
180 project.m_SchematicSettings = nullptr;
181 }
182
183 m_project = aPrj;
184
185 if( m_project )
186 {
187 PROJECT_FILE& project = m_project->GetProjectFile();
188 project.m_ErcSettings = new ERC_SETTINGS( &project, "erc" );
189 project.m_SchematicSettings = new SCHEMATIC_SETTINGS( &project, "schematic" );
190
191 project.m_SchematicSettings->LoadFromFile();
192 project.m_SchematicSettings->m_NgspiceSettings->LoadFromFile();
193 project.m_ErcSettings->LoadFromFile();
194
196 }
197}
198
199
201{
202 wxASSERT( m_project );
203
204 // Cache all existing annotations in the REFDES_TRACKER
205 std::shared_ptr<REFDES_TRACKER> refdesTracker = m_project->GetProjectFile().m_SchematicSettings->m_refDesTracker;
206
207 SCH_SHEET_LIST sheets = Hierarchy();
208 SCH_REFERENCE_LIST references;
209
210 sheets.GetSymbols( references );
211
212 for( const SCH_REFERENCE& ref : references )
213 {
214 refdesTracker->Insert( ref.GetFullRef( false ).ToStdString() );
215 }
216}
217
218
219bool SCHEMATIC::Contains( const SCH_REFERENCE& aRef ) const
220{
221 SCH_SHEET_LIST sheets = Hierarchy();
222 SCH_REFERENCE_LIST references;
223
228 sheets.GetSymbols( references );
229
230 return std::any_of( references.begin(), references.end(),
231 [&]( const SCH_REFERENCE& ref )
232 {
233 return ref.GetFullRef( true ) == aRef.GetFullRef( true );
234 } );
235}
236
237
238void SCHEMATIC::SetRoot( SCH_SHEET* aRootSheet )
239{
240 wxCHECK_RET( aRootSheet, wxS( "Call to SetRoot with null SCH_SHEET!" ) );
241
242 // Check if this is a virtual root (has niluuid) or a regular sheet
243 bool isVirtualRoot = ( aRootSheet->m_Uuid == niluuid );
244
245 wxLogTrace( traceSchSheetPaths,
246 "SetRoot called: sheet='%s', UUID=%s, isVirtualRoot=%d, m_topLevelSheets.size()=%zu",
247 aRootSheet->GetName(), aRootSheet->m_Uuid.AsString(), isVirtualRoot ? 1 : 0, m_topLevelSheets.size() );
248
249 for( size_t i = 0; i < m_topLevelSheets.size(); ++i )
250 {
251 SCH_SHEET* sheet = m_topLevelSheets[i];
252 wxLogTrace( traceSchSheetPaths, " m_topLevelSheets[%zu]: '%s' (UUID=%s, isVirtualRoot=%d)", i,
253 sheet ? sheet->GetName() : wxString( "(null)" ),
254 sheet ? sheet->m_Uuid.AsString() : wxString( "(null)" ),
255 sheet && sheet->m_Uuid == niluuid ? 1 : 0 );
256 }
257
258 if( isVirtualRoot )
259 {
260 // aRootSheet is already the virtual root, use it directly
261 m_rootSheet = aRootSheet;
262
263 // Ensure virtual root has a screen to hold top-level sheets
264 // Only create if we have a project (screens need a valid schematic parent)
265 if( !m_rootSheet->GetScreen() && m_project )
266 {
267 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
268 }
269
270 // Add all existing top-level sheets to the virtual root's screen
271 if( m_rootSheet->GetScreen() )
272 {
273 for( SCH_SHEET* sheet : m_topLevelSheets )
274 {
275 if( sheet && sheet->GetParent() != m_rootSheet )
276 {
277 sheet->SetParent( m_rootSheet );
278 }
279
280 // Add to screen if not already there
281 if( sheet && !m_rootSheet->GetScreen()->Items().contains( sheet ) )
282 {
283 m_rootSheet->GetScreen()->Append( sheet );
284 }
285 }
286 }
287
288 // Current sheet should point to the first top-level sheet if available
289 m_currentSheet->clear();
290
291 if( !m_topLevelSheets.empty() )
292 {
293 m_currentSheet->push_back( m_topLevelSheets[0] );
294 wxLogTrace( traceSchCurrentSheet,
295 "SetRoot: Set current sheet to first top-level sheet '%s', path='%s', size=%zu",
296 m_topLevelSheets[0]->GetName(), m_currentSheet->Path().AsString(), m_currentSheet->size() );
297 }
298 else
299 {
300 wxLogTrace( traceSchCurrentSheet, "SetRoot: No top-level sheets, current sheet left empty" );
301 }
302 // If no top-level sheets, leave current sheet empty - it will be set when sheets are added
303 }
304 else
305 {
306 // aRootSheet is a regular sheet, create virtual root and make it a child
307 if( m_rootSheet == nullptr || m_rootSheet->m_Uuid != niluuid )
308 {
309 // Need to create a new virtual root
310 m_rootSheet = new SCH_SHEET( this );
311 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
312 // Create screen only if we have a project
313 if( m_project )
314 {
315 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
316 }
317 }
318 else if( m_project && !m_rootSheet->GetScreen() )
319 {
320 // Virtual root exists but has no screen - create one now
321 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
322 }
323
324 // Add this sheet as a top-level sheet
325 m_topLevelSheets.clear();
326 m_topLevelSheets.push_back( aRootSheet );
327 aRootSheet->SetParent( m_rootSheet );
328
329 // Clear the virtual root's screen and add only the new sheet
330 if( m_rootSheet->GetScreen() )
331 {
332 // Don't free the sheet being added if it already exists in the screen
333 m_rootSheet->GetScreen()->Items().remove( aRootSheet );
334 m_rootSheet->GetScreen()->Clear();
335 m_rootSheet->GetScreen()->Append( aRootSheet );
336 }
337
338 m_currentSheet->clear();
339 m_currentSheet->push_back( aRootSheet );
340 wxLogTrace( traceSchCurrentSheet, "SetRoot: Set current sheet to root sheet '%s', path='%s', size=%zu",
341 aRootSheet->GetName(), m_currentSheet->Path().AsString(), m_currentSheet->size() );
342 }
343
344 if( m_project )
345 {
347 m_connectionGraph->Reset();
348
349 // Build screen list from root (which now has a screen)
350 m_variantNames.clear();
351
352 if( m_rootSheet->GetScreen() )
353 {
354 SCH_SCREENS screens( m_rootSheet );
355 std::set<wxString> variantNames = screens.GetVariantNames();
356 m_variantNames.insert( variantNames.begin(), variantNames.end() );
357 }
358 }
359}
360
361
363{
364 // Virtual root's screen is just a container - return the first top-level sheet's screen
365 // which is what callers actually want
366 if( !m_topLevelSheets.empty() && m_topLevelSheets[0] )
367 return m_topLevelSheets[0]->GetScreen();
368
369 return nullptr;
370}
371
372
374{
375 wxCHECK( !m_hierarchy.empty(), m_hierarchy );
376
377 return m_hierarchy;
378}
379
380
385
386
387void SCHEMATIC::GetContextualTextVars( wxArrayString* aVars ) const
388{
389 auto add = [&]( const wxString& aVar )
390 {
391 if( !alg::contains( *aVars, aVar ) )
392 aVars->push_back( aVar );
393 };
394
395 add( wxT( "#" ) );
396 add( wxT( "##" ) );
397 add( wxT( "SHEETPATH" ) );
398 add( wxT( "SHEETNAME" ) );
399 add( wxT( "FILENAME" ) );
400 add( wxT( "FILEPATH" ) );
401 add( wxT( "PROJECTNAME" ) );
402
403 if( !CurrentSheet().empty() )
405
406 for( std::pair<wxString, wxString> entry : m_project->GetTextVars() )
407 add( entry.first );
408}
409
410
411bool SCHEMATIC::ResolveTextVar( const SCH_SHEET_PATH* aSheetPath, wxString* token, int aDepth ) const
412{
413 wxCHECK( aSheetPath, false );
414
415 if( token->IsSameAs( wxT( "#" ) ) )
416 {
417 *token = aSheetPath->GetPageNumber();
418 return true;
419 }
420 else if( token->IsSameAs( wxT( "##" ) ) )
421 {
422 *token = wxString::Format( "%i", Root().CountSheets() );
423 return true;
424 }
425 else if( token->IsSameAs( wxT( "SHEETPATH" ) ) )
426 {
427 *token = aSheetPath->PathHumanReadable();
428 return true;
429 }
430 else if( token->IsSameAs( wxT( "SHEETNAME" ) ) )
431 {
432 *token = aSheetPath->Last()->GetName();
433 return true;
434 }
435 else if( token->IsSameAs( wxT( "FILENAME" ) ) )
436 {
437 wxFileName fn( GetFileName() );
438 *token = fn.GetFullName();
439 return true;
440 }
441 else if( token->IsSameAs( wxT( "FILEPATH" ) ) )
442 {
443 wxFileName fn( GetFileName() );
444 *token = fn.GetFullPath();
445 return true;
446 }
447 else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) )
448 {
449 *token = m_project->GetProjectName();
450 return true;
451 }
452
453 // aSheetPath->LastScreen() can be null during schematic loading
454 if( aSheetPath->LastScreen() && aSheetPath->LastScreen()->GetTitleBlock().TextVarResolver( token, m_project ) )
455 return true;
456
457 if( m_project->TextVarResolver( token ) )
458 return true;
459
460 return false;
461}
462
463
465{
466 // With virtual root pattern, m_rootSheet is the virtual root with no file
467 // Return filename from first top-level sheet if available
468 if( !IsValid() )
469 return wxString( wxEmptyString );
470
471 if( !m_topLevelSheets.empty() && m_topLevelSheets[0]->GetScreen() )
472 return m_topLevelSheets[0]->GetScreen()->GetFileName();
473
474 return wxString( wxEmptyString );
475}
476
477
479{
480 if( !m_project )
481 {
482 static SCHEMATIC_SETTINGS defaultSettings( nullptr, "schematic" );
483 return defaultSettings;
484 }
485 wxASSERT( m_project );
486 return *m_project->GetProjectFile().m_SchematicSettings;
487}
488
489
491{
492 wxASSERT( m_project );
493 return *m_project->GetProjectFile().m_ErcSettings;
494}
495
496
497std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
498{
499 SCH_SHEET_LIST sheetList = Hierarchy();
500 ERC_SETTINGS& settings = ErcSettings();
501
502 // Migrate legacy marker exclusions to new format to ensure exclusion matching functions across
503 // file versions. Silently drops any legacy exclusions which can not be mapped to the new format
504 // without risking an incorrect exclusion - this is preferable to silently dropping
505 // new ERC errors / warnings due to an incorrect match between a legacy and new
506 // marker serialization format
507 std::set<wxString> migratedExclusions;
508
509 for( auto it = settings.m_ErcExclusions.begin(); it != settings.m_ErcExclusions.end(); )
510 {
511 SCH_MARKER* testMarker = SCH_MARKER::DeserializeFromString( sheetList, *it );
512
513 if( !testMarker )
514 {
515 it = settings.m_ErcExclusions.erase( it );
516 continue;
517 }
518
519 if( testMarker->IsLegacyMarker() )
520 {
521 const wxString settingsKey = testMarker->GetRCItem()->GetSettingsKey();
522
523 if( settingsKey != wxT( "pin_to_pin" ) && settingsKey != wxT( "hier_label_mismatch" )
524 && settingsKey != wxT( "different_unit_net" ) )
525 {
526 migratedExclusions.insert( testMarker->SerializeToString() );
527 }
528
529 it = settings.m_ErcExclusions.erase( it );
530 }
531 else
532 {
533 ++it;
534 }
535
536 delete testMarker;
537 }
538
539 settings.m_ErcExclusions.insert( migratedExclusions.begin(), migratedExclusions.end() );
540
541 // End of legacy exclusion removal / migrations
542
543 for( const SCH_SHEET_PATH& sheet : sheetList )
544 {
545 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
546 {
547 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
548 wxString serialized = marker->SerializeToString();
549 std::set<wxString>::iterator it = settings.m_ErcExclusions.find( serialized );
550
551 if( it != settings.m_ErcExclusions.end() )
552 {
553 marker->SetExcluded( true, settings.m_ErcExclusionComments[serialized] );
554 settings.m_ErcExclusions.erase( it );
555 }
556 }
557 }
558
559 std::vector<SCH_MARKER*> newMarkers;
560
561 for( const wxString& serialized : settings.m_ErcExclusions )
562 {
563 SCH_MARKER* marker = SCH_MARKER::DeserializeFromString( sheetList, serialized );
564
565 if( marker )
566 {
567 marker->SetExcluded( true, settings.m_ErcExclusionComments[serialized] );
568 newMarkers.push_back( marker );
569 }
570 }
571
572 settings.m_ErcExclusions.clear();
573
574 return newMarkers;
575}
576
577
578std::shared_ptr<BUS_ALIAS> SCHEMATIC::GetBusAlias( const wxString& aLabel ) const
579{
580 for( const std::shared_ptr<BUS_ALIAS>& alias : m_busAliases )
581 {
582 if( alias && alias->GetName() == aLabel )
583 return alias;
584 }
585
586 return nullptr;
587}
588
589
590void SCHEMATIC::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
591{
592 if( !aAlias )
593 return;
594
595 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
596 {
597 return candidate && candidate->GetName() == aAlias->GetName() && candidate->Members() == aAlias->Members();
598 };
599
600 auto it = std::find_if( m_busAliases.begin(), m_busAliases.end(), sameDefinition );
601
602 if( it != m_busAliases.end() )
603 return;
604
605 m_busAliases.push_back( aAlias );
606
608}
609
610
611void SCHEMATIC::SetBusAliases( const std::vector<std::shared_ptr<BUS_ALIAS>>& aAliases )
612{
613 m_busAliases.clear();
614
615 for( const std::shared_ptr<BUS_ALIAS>& alias : aAliases )
616 {
617 if( !alias )
618 continue;
619
620 std::shared_ptr<BUS_ALIAS> clone = alias->Clone();
621
622 auto sameDefinition = [&]( const std::shared_ptr<BUS_ALIAS>& candidate ) -> bool
623 {
624 return candidate && candidate->GetName() == clone->GetName() && candidate->Members() == clone->Members();
625 };
626
627 if( std::find_if( m_busAliases.begin(), m_busAliases.end(), sameDefinition ) != m_busAliases.end() )
628 continue;
629
630 m_busAliases.push_back( clone );
631 }
632
634}
635
636
638{
639 m_busAliases.clear();
640
641 if( !m_project )
642 return;
643
644 const auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
645
646 for( const auto& alias : projectAliases )
647 {
648 std::shared_ptr<BUS_ALIAS> busAlias = std::make_shared<BUS_ALIAS>();
649
650 busAlias->SetName( alias.first );
651 busAlias->Members() = alias.second;
652
653 m_busAliases.push_back( busAlias );
654 }
655}
656
657
659{
660 if( !m_project )
661 return;
662
663 auto& projectAliases = m_project->GetProjectFile().m_BusAliases;
664
665 projectAliases.clear();
666
667 std::set<wxString> seen;
668
669 for( const std::shared_ptr<BUS_ALIAS>& alias : m_busAliases )
670 {
671 if( !alias )
672 continue;
673
674 if( !seen.insert( alias->GetName() ).second )
675 continue;
676
677 projectAliases.emplace( alias->GetName(), alias->Members() );
678 }
679}
680
681
683{
684 std::set<wxString> names;
685
686 for( const auto& [key, subgraphList] : m_connectionGraph->GetNetMap() )
687 {
688 CONNECTION_SUBGRAPH* firstSubgraph = subgraphList[0];
689
690 if( !firstSubgraph->GetDriverConnection()->IsBus()
692 {
693 names.insert( key.Name );
694 }
695 }
696
697 return names;
698}
699
700
701bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
702{
703 wxString remainder;
704 wxString ref = token->BeforeFirst( ':', &remainder );
705 KIID_PATH path( ref );
706 KIID uuid = path.back();
707 SCH_SHEET_PATH sheetPath;
708 SCH_ITEM* refItem = ResolveItem( KIID( uuid ), &sheetPath, true );
709
710 if( path.size() > 1 )
711 {
712 path.pop_back();
713 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
714 }
715
716 // Note: We don't expand nested variables or evaluate math expressions here.
717 // The multi-pass loop in GetShownText handles all variable and expression resolution
718 // before cross-references are resolved. This ensures table cell variables like ${ROW}
719 // are expanded correctly.
720
721 if( refItem && refItem->Type() == SCH_SYMBOL_T )
722 {
723 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
724
725 bool resolved = refSymbol->ResolveTextVar( &sheetPath, &remainder, aDepth + 1 );
726
727 if( resolved )
728 {
729 *token = std::move( remainder );
730 }
731 else
732 {
733 // Field/function not found on symbol
734 *token =
735 wxString::Format( wxT( "<Unresolved: %s:%s>" ), refSymbol->GetRef( &sheetPath, false ), remainder );
736 }
737
738 return true;
739 }
740 else if( refItem && refItem->Type() == SCH_SHEET_T )
741 {
742 SCH_SHEET* refSheet = static_cast<SCH_SHEET*>( refItem );
743
744 sheetPath.push_back( refSheet );
745
746 wxString remainderBefore = remainder;
747
748 if( refSheet->ResolveTextVar( &sheetPath, &remainder, aDepth + 1 ) )
749 *token = std::move( remainder );
750
751 // If the remainder still contains unresolved variables or expressions,
752 // return false so ExpandTextVars keeps the ${...} wrapper
753 if( remainderBefore.Contains( wxT( "${" ) ) || remainderBefore.Contains( wxT( "@{" ) ) )
754 return false;
755
756 return true; // Cross-reference is resolved
757 }
758
759 // If UUID resolution failed, try to resolve by reference designator
760 // This handles both exact matches (J601A) and parent references for multi-unit symbols (J601)
761 if( !refItem )
762 {
764 Hierarchy().GetSymbols( refs );
765
766 SCH_SYMBOL* foundSymbol = nullptr;
767 SCH_SHEET_PATH foundPath;
768
769 for( int ii = 0; ii < (int) refs.GetCount(); ii++ )
770 {
771 SCH_REFERENCE& reference = refs[ii];
772 wxString symbolRef = reference.GetSymbol()->GetRef( &reference.GetSheetPath(), false );
773
774 // Try exact match first
775 if( symbolRef == ref )
776 {
777 foundSymbol = reference.GetSymbol();
778 foundPath = reference.GetSheetPath();
779 break;
780 }
781
782 // For multi-unit symbols, try matching parent reference (e.g., J601 matches J601A)
783 if( symbolRef.StartsWith( ref ) && symbolRef.Length() == ref.Length() + 1 )
784 {
785 wxChar lastChar = symbolRef.Last();
786 if( lastChar >= 'A' && lastChar <= 'Z' )
787 {
788 foundSymbol = reference.GetSymbol();
789 foundPath = reference.GetSheetPath();
790 // Don't break - continue looking for exact match
791 }
792 }
793 }
794
795 if( foundSymbol )
796 {
797 bool resolved = foundSymbol->ResolveTextVar( &foundPath, &remainder, aDepth + 1 );
798
799 if( resolved )
800 {
801 *token = std::move( remainder );
802 }
803 else
804 {
805 // Field/function not found on symbol
806 *token = wxString::Format( wxT( "<Unresolved: %s:%s>" ), foundSymbol->GetRef( &foundPath, false ),
807 remainder );
808 }
809
810 return true;
811 }
812
813 // Symbol not found - set unresolved error
814 *token = wxString::Format( wxT( "<Unresolved: %s>" ), ref );
815 return true;
816 }
817
818 // Reference not found - show error message
819 *token = wxString::Format( wxT( "<Unknown reference: %s>" ), ref );
820 return true;
821}
822
823
824std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetNamesMap() const
825{
826 std::map<int, wxString> namesMap;
827
828 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
829 {
830 if( sheet.size() == 1 )
831 namesMap[sheet.GetVirtualPageNumber()] = _( "<root sheet>" );
832 else
833 namesMap[sheet.GetVirtualPageNumber()] = sheet.Last()->GetName();
834 }
835
836 return namesMap;
837}
838
839
840std::map<int, wxString> SCHEMATIC::GetVirtualPageToSheetPagesMap() const
841{
842 std::map<int, wxString> pagesMap;
843
844 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
845 pagesMap[sheet.GetVirtualPageNumber()] = sheet.GetPageNumber();
846
847 return pagesMap;
848}
849
850
851wxString SCHEMATIC::ConvertRefsToKIIDs( const wxString& aSource ) const
852{
853 wxString newbuf;
854 size_t sourceLen = aSource.length();
855
856 for( size_t i = 0; i < sourceLen; ++i )
857 {
858 // Check for escaped expressions: \${ or \@{
859 // These should be copied verbatim without any ref→KIID conversion
860 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
861 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
862 {
863 // Copy the escape sequence and the entire escaped expression
864 newbuf.append( aSource[i] ); // backslash
865 newbuf.append( aSource[i + 1] ); // $ or @
866 newbuf.append( aSource[i + 2] ); // {
867 i += 2;
868
869 // Find and copy everything until the matching closing brace
870 int braceDepth = 1;
871 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
872 {
873 if( aSource[i] == '{' )
874 braceDepth++;
875 else if( aSource[i] == '}' )
876 braceDepth--;
877
878 newbuf.append( aSource[i] );
879 }
880 i--; // Back up one since the for loop will increment
881 continue;
882 }
883
884 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
885 {
886 wxString token;
887 bool isCrossRef = false;
888 int nesting = 0;
889
890 for( i = i + 2; i < sourceLen; ++i )
891 {
892 if( aSource[i] == '{' && ( aSource[i - 1] == '_' || aSource[i - 1] == '^' || aSource[i - 1] == '~' ) )
893 {
894 nesting++;
895 }
896
897 if( aSource[i] == '}' )
898 {
899 nesting--;
900
901 if( nesting < 0 )
902 break;
903 }
904
905 if( aSource[i] == ':' )
906 isCrossRef = true;
907
908 token.append( aSource[i] );
909 }
910
911 if( isCrossRef )
912 {
913 wxString remainder;
914 wxString ref = token.BeforeFirst( ':', &remainder );
915 SCH_REFERENCE_LIST references;
916
917 Hierarchy().GetSymbols( references );
918
919 for( size_t jj = 0; jj < references.GetCount(); jj++ )
920 {
921 SCH_SYMBOL* refSymbol = references[jj].GetSymbol();
922
923 if( ref == refSymbol->GetRef( &references[jj].GetSheetPath(), true ) )
924 {
925 KIID_PATH path = references[jj].GetSheetPath().Path();
926 path.push_back( refSymbol->m_Uuid );
927
928 token = path.AsString() + wxS( ":" ) + remainder;
929 break;
930 }
931 }
932 }
933
934 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
935 }
936 else
937 {
938 newbuf.append( aSource[i] );
939 }
940 }
941
942 return newbuf;
943}
944
945
946wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
947{
948 wxString newbuf;
949 size_t sourceLen = aSource.length();
950
951 for( size_t i = 0; i < sourceLen; ++i )
952 {
953 // Check for escaped expressions: \${ or \@{
954 // These should be copied verbatim without any KIID→ref conversion
955 if( aSource[i] == '\\' && i + 2 < sourceLen && aSource[i + 2] == '{' &&
956 ( aSource[i + 1] == '$' || aSource[i + 1] == '@' ) )
957 {
958 // Copy the escape sequence and the entire escaped expression
959 newbuf.append( aSource[i] ); // backslash
960 newbuf.append( aSource[i + 1] ); // $ or @
961 newbuf.append( aSource[i + 2] ); // {
962 i += 2;
963
964 // Find and copy everything until the matching closing brace
965 int braceDepth = 1;
966 for( i = i + 1; i < sourceLen && braceDepth > 0; ++i )
967 {
968 if( aSource[i] == '{' )
969 braceDepth++;
970 else if( aSource[i] == '}' )
971 braceDepth--;
972
973 newbuf.append( aSource[i] );
974 }
975 i--; // Back up one since the for loop will increment
976 continue;
977 }
978
979 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i + 1] == '{' )
980 {
981 wxString token;
982 bool isCrossRef = false;
983
984 for( i = i + 2; i < sourceLen; ++i )
985 {
986 if( aSource[i] == '}' )
987 break;
988
989 if( aSource[i] == ':' )
990 isCrossRef = true;
991
992 token.append( aSource[i] );
993 }
994
995 if( isCrossRef )
996 {
997 wxString remainder;
998 wxString ref = token.BeforeFirst( ':', &remainder );
999 KIID_PATH path( ref );
1000 KIID uuid = path.back();
1001 SCH_SHEET_PATH sheetPath;
1002 SCH_ITEM* refItem = ResolveItem( uuid, &sheetPath, true );
1003
1004 if( path.size() > 1 )
1005 {
1006 path.pop_back();
1007 sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
1008 }
1009
1010 if( refItem && refItem->Type() == SCH_SYMBOL_T )
1011 {
1012 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
1013 token = refSymbol->GetRef( &sheetPath, true ) + wxS( ":" ) + remainder;
1014 }
1015 }
1016
1017 newbuf.append( wxS( "${" ) + token + wxS( "}" ) );
1018 }
1019 else
1020 {
1021 newbuf.append( aSource[i] );
1022 }
1023 }
1024
1025 return newbuf;
1026}
1027
1028
1035
1036
1038{
1039 // Filename is rootSheetName-sheetName-...-sheetName
1040 // Note that we need to fetch the rootSheetName out of its filename, as the root SCH_SHEET's
1041 // name is just a timestamp.
1042
1043 // Skip virtual root if present
1044 size_t startIdx = 0;
1045 if( CurrentSheet().size() > 0 && CurrentSheet().at( 0 )->IsVirtualRootSheet() )
1046 startIdx = 1;
1047
1048 // Handle the case where we only have a virtual root (shouldn't happen in practice)
1049 if( startIdx >= CurrentSheet().size() )
1050 return wxEmptyString;
1051
1052 wxFileName rootFn( CurrentSheet().at( startIdx )->GetFileName() );
1053 wxString filename = rootFn.GetName();
1054
1055 for( unsigned i = startIdx + 1; i < CurrentSheet().size(); i++ )
1056 filename += wxT( "-" ) + CurrentSheet().at( i )->GetName();
1057
1058 return filename;
1059}
1060
1061
1063{
1064 SCH_SCREEN* screen;
1065 SCH_SCREENS s_list( Root() );
1066
1067 // Set the sheet count, and the sheet number (1 for root sheet)
1068 int sheet_count;
1069
1070 // Handle virtual root case
1071 if( Root().m_Uuid == niluuid )
1072 {
1073 // Virtual root: count all top-level sheets
1074 sheet_count = 0;
1075
1076 for( const SCH_SHEET* topSheet : m_topLevelSheets )
1077 {
1078 if( topSheet )
1079 sheet_count += topSheet->CountSheets();
1080 }
1081 }
1082 else
1083 {
1084 // Traditional single root
1085 sheet_count = Root().CountSheets();
1086 }
1087
1088 int sheet_number = 1;
1089 const KIID_PATH& current_sheetpath = CurrentSheet().Path();
1090
1091 // @todo Remove all pseudo page number system is left over from prior to real page number
1092 // implementation.
1093 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1094 {
1095 if( sheet.Path() == current_sheetpath ) // Current sheet path found
1096 break;
1097
1098 sheet_number++; // Not found, increment before this current path
1099 }
1100
1101 for( screen = s_list.GetFirst(); screen != nullptr; screen = s_list.GetNext() )
1102 screen->SetPageCount( sheet_count );
1103
1104 CurrentSheet().SetVirtualPageNumber( sheet_number );
1105 CurrentSheet().LastScreen()->SetVirtualPageNumber( sheet_number );
1106 CurrentSheet().LastScreen()->SetPageNumber( CurrentSheet().GetPageNumber() );
1107}
1108
1109
1111{
1112 std::map<wxString, std::set<int>>& pageRefsMap = GetPageRefsMap();
1113
1114 pageRefsMap.clear();
1115
1116 for( const SCH_SHEET_PATH& sheet : Hierarchy() )
1117 {
1118 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1119 {
1120 SCH_GLOBALLABEL* global = static_cast<SCH_GLOBALLABEL*>( item );
1121 wxString resolvedLabel = global->GetShownText( &sheet, false );
1122
1123 pageRefsMap[resolvedLabel].insert( sheet.GetVirtualPageNumber() );
1124 }
1125 }
1126
1127 bool show = Settings().m_IntersheetRefsShow;
1128
1129 // Refresh all visible global labels. Note that we have to collect them first as the
1130 // SCH_SCREEN::Update() call is going to invalidate the RTree iterator.
1131
1132 std::vector<SCH_GLOBALLABEL*> currentSheetGlobalLabels;
1133
1134 for( EDA_ITEM* item : CurrentSheet().LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
1135 currentSheetGlobalLabels.push_back( static_cast<SCH_GLOBALLABEL*>( item ) );
1136
1137 for( SCH_GLOBALLABEL* globalLabel : currentSheetGlobalLabels )
1138 {
1139 std::vector<SCH_FIELD>& fields = globalLabel->GetFields();
1140
1141 fields[0].SetVisible( show );
1142
1143 if( show )
1144 {
1145 if( fields.size() == 1 && fields[0].GetTextPos() == globalLabel->GetPosition() )
1146 globalLabel->AutoplaceFields( CurrentSheet().LastScreen(), AUTOPLACE_AUTO );
1147
1148 CurrentSheet().LastScreen()->Update( globalLabel );
1149
1150 for( SCH_FIELD& field : globalLabel->GetFields() )
1151 field.ClearBoundingBoxCache();
1152
1153 globalLabel->ClearBoundingBoxCache();
1154
1155 if( m_schematicHolder )
1156 m_schematicHolder->IntersheetRefUpdate( globalLabel );
1157 }
1158 }
1159}
1160
1161
1162wxString SCHEMATIC::GetOperatingPoint( const wxString& aNetName, int aPrecision, const wxString& aRange )
1163{
1164 wxString spiceNetName( aNetName.Lower() );
1166
1167 if( spiceNetName == wxS( "gnd" ) || spiceNetName == wxS( "0" ) )
1168 return wxEmptyString;
1169
1170 auto it = m_operatingPoints.find( spiceNetName );
1171
1172 if( it != m_operatingPoints.end() )
1173 return SPICE_VALUE( it->second ).ToString( { aPrecision, aRange } );
1174 else if( m_operatingPoints.empty() )
1175 return wxS( "--" );
1176 else
1177 return wxS( "?" );
1178}
1179
1180
1182{
1183 SCH_SCREENS screens( Root() );
1184
1185 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1186 {
1187 std::deque<EDA_ITEM*> allItems;
1188
1189 for( SCH_ITEM* item : screen->Items() )
1190 allItems.push_back( item );
1191
1192 // Add missing junctions and breakup wires as needed
1193 for( const VECTOR2I& point : screen->GetNeededJunctions( allItems ) )
1194 {
1195 SCH_JUNCTION* junction = new SCH_JUNCTION( point );
1196 screen->Append( junction );
1197
1198 // Breakup wires
1199 for( SCH_LINE* wire : screen->GetBusesAndWires( point, true ) )
1200 {
1201 SCH_LINE* newSegment = wire->NonGroupAware_BreakAt( point );
1202 screen->Append( newSegment );
1203 }
1204 }
1205 }
1206}
1207
1208
1209void SCHEMATIC::OnItemsAdded( std::vector<SCH_ITEM*>& aNewItems )
1210{
1212}
1213
1214
1215void SCHEMATIC::OnItemsRemoved( std::vector<SCH_ITEM*>& aRemovedItems )
1216{
1218}
1219
1220
1221void SCHEMATIC::OnItemsChanged( std::vector<SCH_ITEM*>& aItems )
1222{
1224}
1225
1226
1231
1232
1234{
1235 if( !alg::contains( m_listeners, aListener ) )
1236 m_listeners.push_back( aListener );
1237}
1238
1239
1241{
1242 auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
1243
1244 if( i != m_listeners.end() )
1245 {
1246 std::iter_swap( i, m_listeners.end() - 1 );
1247 m_listeners.pop_back();
1248 }
1249}
1250
1251
1253{
1254 m_listeners.clear();
1255}
1256
1257
1259{
1260 // Use a sorted sheetList to reduce file churn
1261 SCH_SHEET_LIST sheetList = Hierarchy();
1262 ERC_SETTINGS& ercSettings = ErcSettings();
1263
1264 ercSettings.m_ErcExclusions.clear();
1265 ercSettings.m_ErcExclusionComments.clear();
1266
1267 for( unsigned i = 0; i < sheetList.size(); i++ )
1268 {
1269 for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_MARKER_T ) )
1270 {
1271 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
1272
1273 if( marker->IsExcluded() )
1274 {
1275 wxString serialized = marker->SerializeToString();
1276 ercSettings.m_ErcExclusions.insert( serialized );
1277 ercSettings.m_ErcExclusionComments[serialized] = marker->GetComment();
1278 }
1279 }
1280 }
1281}
1282
1283
1285{
1286 SCH_SHEET_LIST sheetList = Hierarchy();
1287
1288 for( SCH_MARKER* marker : ResolveERCExclusions() )
1289 {
1290 SCH_SHEET_PATH errorPath;
1291 ignore_unused( sheetList.ResolveItem( marker->GetRCItem()->GetMainItemID(), &errorPath ) );
1292
1293 if( errorPath.LastScreen() )
1294 errorPath.LastScreen()->Append( marker );
1295 else
1296 RootScreen()->Append( marker );
1297 }
1298
1299 // Once we have the ERC Exclusions, record them in the project file so that
1300 // they are retained even before the schematic is saved (PCB Editor can also save the project)
1302}
1303
1304
1306{
1307 return static_cast<EMBEDDED_FILES*>( this );
1308}
1309
1310
1312{
1313 return static_cast<const EMBEDDED_FILES*>( this );
1314}
1315
1316
1317void SCHEMATIC::RunOnNestedEmbeddedFiles( const std::function<void( EMBEDDED_FILES* )>& aFunction )
1318{
1319 SCH_SCREENS screens( Root() );
1320
1321 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1322 {
1323 for( auto& [name, libSym] : screen->GetLibSymbols() )
1324 aFunction( libSym->GetEmbeddedFiles() );
1325 }
1326}
1327
1328
1329std::set<KIFONT::OUTLINE_FONT*> SCHEMATIC::GetFonts() const
1330{
1331 std::set<KIFONT::OUTLINE_FONT*> fonts;
1332
1333 SCH_SHEET_LIST sheetList = Hierarchy();
1334
1335 for( const SCH_SHEET_PATH& sheet : sheetList )
1336 {
1337 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
1338 {
1339 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
1340 {
1341 KIFONT::FONT* font = text->GetFont();
1342
1343 if( !font || font->IsStroke() )
1344 continue;
1345
1346 using EMBEDDING_PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
1347 auto* outline = static_cast<KIFONT::OUTLINE_FONT*>( font );
1348
1349 if( outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::EDITABLE
1350 || outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::INSTALLABLE )
1351 {
1352 fonts.insert( outline );
1353 }
1354 }
1355 }
1356 }
1357
1358 return fonts;
1359}
1360
1361
1363{
1364 std::set<KIFONT::OUTLINE_FONT*> fonts = GetFonts();
1365
1366 for( KIFONT::OUTLINE_FONT* font : fonts )
1367 {
1368 auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
1369
1370 if( !file )
1371 {
1372 wxLogTrace( "EMBED", "Failed to add font file: %s", font->GetFileName() );
1373 continue;
1374 }
1375
1377 }
1378}
1379
1380
1381std::set<const SCH_SCREEN*> SCHEMATIC::GetSchematicsSharedByMultipleProjects() const
1382{
1383 std::set<const SCH_SCREEN*> retv;
1384
1385 wxCHECK( m_rootSheet, retv );
1386
1387 SCH_SHEET_LIST hierarchy( m_rootSheet );
1388 SCH_SCREENS screens( m_rootSheet );
1389
1390 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1391 {
1392 for( const SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1393 {
1394 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
1395
1396 const std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = symbol->GetInstances();
1397
1398 for( const SCH_SYMBOL_INSTANCE& instance : symbolInstances )
1399 {
1400 if( !hierarchy.HasPath( instance.m_Path ) )
1401 {
1402 retv.insert( screen );
1403 break;
1404 }
1405 }
1406
1407 if( retv.count( screen ) )
1408 break;
1409 }
1410 }
1411
1412 return retv;
1413}
1414
1415
1417{
1418 wxCHECK( m_rootSheet, false );
1419
1420 SCH_SCREENS screens( m_rootSheet );
1421
1422 for( const SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
1423 {
1424 wxCHECK2( screen, continue );
1425
1426 if( screen->GetRefCount() > 1 )
1427 return true;
1428 }
1429
1430 return false;
1431}
1432
1433
1434void SCHEMATIC::CleanUp( SCH_COMMIT* aCommit, SCH_SCREEN* aScreen )
1435{
1436 SCH_SELECTION_TOOL* selectionTool = m_schematicHolder ? m_schematicHolder->GetSelectionTool() : nullptr;
1437 std::vector<SCH_LINE*> lines;
1438 std::vector<SCH_JUNCTION*> junctions;
1439 std::vector<SCH_NO_CONNECT*> ncs;
1440 std::vector<SCH_ITEM*> items_to_remove;
1441 bool changed = true;
1442
1443 if( aScreen == nullptr )
1444 aScreen = GetCurrentScreen();
1445
1446 auto remove_item = [&]( SCH_ITEM* aItem ) -> void
1447 {
1448 changed = true;
1449
1450 if( !( aItem->GetFlags() & STRUCT_DELETED ) )
1451 {
1452 aItem->SetFlags( STRUCT_DELETED );
1453
1454 if( aItem->IsSelected() && selectionTool )
1455 selectionTool->RemoveItemFromSel( aItem, true /*quiet mode*/ );
1456
1457 if( m_schematicHolder )
1458 {
1459 m_schematicHolder->RemoveFromScreen( aItem, aScreen );
1460 }
1461 aCommit->Removed( aItem, aScreen );
1462 }
1463 };
1464
1465
1466 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_JUNCTION_T ) )
1467 {
1468 if( !aScreen->IsExplicitJunction( item->GetPosition() ) )
1469 {
1470 if( item->IsSelected() || item->HasFlag( SELECTED_BY_DRAG ) )
1471 continue;
1472
1473 items_to_remove.push_back( item );
1474 }
1475 else
1476 junctions.push_back( static_cast<SCH_JUNCTION*>( item ) );
1477 }
1478
1479 for( SCH_ITEM* item : items_to_remove )
1480 remove_item( item );
1481
1482 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_NO_CONNECT_T ) )
1483 ncs.push_back( static_cast<SCH_NO_CONNECT*>( item ) );
1484
1485 alg::for_all_pairs( junctions.begin(), junctions.end(),
1486 [&]( SCH_JUNCTION* aFirst, SCH_JUNCTION* aSecond )
1487 {
1488 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
1489 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
1490 {
1491 return;
1492 }
1493
1494 if( aFirst->GetPosition() == aSecond->GetPosition() )
1495 remove_item( aSecond );
1496 } );
1497
1498 alg::for_all_pairs( ncs.begin(), ncs.end(),
1499 [&]( SCH_NO_CONNECT* aFirst, SCH_NO_CONNECT* aSecond )
1500 {
1501 if( ( aFirst->GetEditFlags() & STRUCT_DELETED )
1502 || ( aSecond->GetEditFlags() & STRUCT_DELETED ) )
1503 {
1504 return;
1505 }
1506
1507 if( aFirst->GetPosition() == aSecond->GetPosition() )
1508 remove_item( aSecond );
1509 } );
1510
1511
1512 auto minX = []( const SCH_LINE* l )
1513 {
1514 return std::min( l->GetStartPoint().x, l->GetEndPoint().x );
1515 };
1516
1517 auto maxX = []( const SCH_LINE* l )
1518 {
1519 return std::max( l->GetStartPoint().x, l->GetEndPoint().x );
1520 };
1521
1522 auto minY = []( const SCH_LINE* l )
1523 {
1524 return std::min( l->GetStartPoint().y, l->GetEndPoint().y );
1525 };
1526
1527 auto maxY = []( const SCH_LINE* l )
1528 {
1529 return std::max( l->GetStartPoint().y, l->GetEndPoint().y );
1530 };
1531
1532 // Would be nice to put lines in a canonical form here by swapping
1533 // start <-> end as needed but I don't know what swapping breaks.
1534 while( changed )
1535 {
1536 changed = false;
1537 lines.clear();
1538
1539 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_LINE_T ) )
1540 {
1541 if( item->GetLayer() == LAYER_WIRE || item->GetLayer() == LAYER_BUS )
1542 lines.push_back( static_cast<SCH_LINE*>( item ) );
1543 }
1544
1545 // Sort by minimum X position
1546 std::sort( lines.begin(), lines.end(),
1547 [&]( const SCH_LINE* a, const SCH_LINE* b )
1548 {
1549 return minX( a ) < minX( b );
1550 } );
1551
1552 for( auto it1 = lines.begin(); it1 != lines.end(); ++it1 )
1553 {
1554 SCH_LINE* firstLine = *it1;
1555
1556 if( firstLine->GetEditFlags() & STRUCT_DELETED )
1557 continue;
1558
1559 if( firstLine->IsNull() )
1560 {
1561 remove_item( firstLine );
1562 continue;
1563 }
1564
1565 int firstRightXEdge = maxX( firstLine );
1566 auto it2 = it1;
1567
1568 for( ++it2; it2 != lines.end(); ++it2 )
1569 {
1570 SCH_LINE* secondLine = *it2;
1571 int secondLeftXEdge = minX( secondLine );
1572
1573 // impossible to overlap remaining lines
1574 if( secondLeftXEdge > firstRightXEdge )
1575 break;
1576
1577 // No Y axis overlap
1578 if( !( std::max( minY( firstLine ), minY( secondLine ) )
1579 <= std::min( maxY( firstLine ), maxY( secondLine ) ) ) )
1580 {
1581 continue;
1582 }
1583
1584 if( secondLine->GetFlags() & STRUCT_DELETED )
1585 continue;
1586
1587 if( !secondLine->IsParallel( firstLine ) || !secondLine->IsStrokeEquivalent( firstLine )
1588 || secondLine->GetLayer() != firstLine->GetLayer() )
1589 {
1590 continue;
1591 }
1592
1593 // Remove identical lines
1594 if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
1595 && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
1596 {
1597 remove_item( secondLine );
1598 continue;
1599 }
1600
1601 // See if we can merge an overlap (or two colinear touching segments with
1602 // no junction where they meet).
1603 SCH_LINE* mergedLine = secondLine->MergeOverlap( aScreen, firstLine, true );
1604
1605 if( mergedLine != nullptr )
1606 {
1607 remove_item( firstLine );
1608 remove_item( secondLine );
1609
1610 if( m_schematicHolder )
1611 {
1612 m_schematicHolder->AddToScreen( mergedLine, aScreen );
1613 }
1614
1615 aCommit->Added( mergedLine, aScreen );
1616
1617 if( selectionTool && ( firstLine->IsSelected() || secondLine->IsSelected() ) )
1618 selectionTool->AddItemToSel( mergedLine, true /*quiet mode*/ );
1619
1620 break;
1621 }
1622 }
1623 }
1624 }
1625}
1626
1627
1629 TOOL_MANAGER* aToolManager, PROGRESS_REPORTER* aProgressReporter,
1630 KIGFX::SCH_VIEW* aSchView,
1631 std::function<void( SCH_ITEM* )>* aChangedItemHandler,
1632 PICKED_ITEMS_LIST* aLastChangeList )
1633{
1634 SCHEMATIC_SETTINGS& settings = Settings();
1636 SCH_SHEET_LIST list = Hierarchy();
1637 SCH_COMMIT localCommit( aToolManager );
1638
1639 if( !aCommit )
1640 aCommit = &localCommit;
1641
1642 PROF_TIMER timer;
1643
1644 // Ensure schematic graph is accurate
1645 if( aCleanupFlags == LOCAL_CLEANUP )
1646 {
1647 CleanUp( aCommit, GetCurrentScreen() );
1648 }
1649 else if( aCleanupFlags == GLOBAL_CLEANUP )
1650 {
1651 for( const SCH_SHEET_PATH& sheet : list )
1652 CleanUp( aCommit, sheet.LastScreen() );
1653 }
1654
1655 timer.Stop();
1656 wxLogTrace( "CONN_PROFILE", "SchematicCleanUp() %0.4f ms", timer.msecs() );
1657
1658 if( settings.m_IntersheetRefsShow )
1660
1661 if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP
1662 || aLastChangeList == nullptr || ConnectionGraph()->IsMinor() )
1663 {
1664 // Clear all resolved netclass caches in case labels have changed
1665 m_project->GetProjectFile().NetSettings()->ClearAllCaches();
1666
1667 // Update all rule areas so we can cascade implied connectivity changes
1668 std::unordered_set<SCH_SCREEN*> all_screens;
1669
1670 for( const SCH_SHEET_PATH& path : list )
1671 all_screens.insert( path.LastScreen() );
1672
1673 SCH_RULE_AREA::UpdateRuleAreasInScreens( all_screens, aSchView );
1674
1675 // Recalculate all connectivity
1676 ConnectionGraph()->Recalculate( list, true, aChangedItemHandler, aProgressReporter );
1677 }
1678 else
1679 {
1680 struct CHANGED_ITEM
1681 {
1682 SCH_ITEM* item;
1683 SCH_ITEM* linked_item;
1684 SCH_SCREEN* screen;
1685 };
1686
1687 // Final change sets
1688 std::set<SCH_ITEM*> changed_items;
1689 std::set<VECTOR2I> pts;
1690 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> item_paths;
1691
1692 // Working change sets
1693 std::unordered_set<SCH_SCREEN*> changed_screens;
1694 std::set<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> changed_rule_areas;
1695 std::vector<CHANGED_ITEM> changed_connectable_items;
1696
1697 // Lambda to add an item to the connectivity update sets
1698 auto addItemToChangeSet = [&changed_items, &pts, &item_paths]( CHANGED_ITEM itemData )
1699 {
1700 std::vector<SCH_SHEET_PATH>& paths = itemData.screen->GetClientSheetPaths();
1701
1702 std::vector<VECTOR2I> tmp_pts = itemData.item->GetConnectionPoints();
1703 pts.insert( tmp_pts.begin(), tmp_pts.end() );
1704 changed_items.insert( itemData.item );
1705
1706 for( SCH_SHEET_PATH& path : paths )
1707 item_paths.insert( std::make_pair( path, itemData.item ) );
1708
1709 if( !itemData.linked_item || !itemData.linked_item->IsConnectable() )
1710 return;
1711
1712 tmp_pts = itemData.linked_item->GetConnectionPoints();
1713 pts.insert( tmp_pts.begin(), tmp_pts.end() );
1714 changed_items.insert( itemData.linked_item );
1715
1716 // We have to directly add the pins here because the link may not exist on the schematic
1717 // anymore and so won't be picked up by GetScreen()->Items().Overlapping() below.
1718 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( itemData.linked_item ) )
1719 {
1720 std::vector<SCH_PIN*> pins = symbol->GetPins();
1721 changed_items.insert( pins.begin(), pins.end() );
1722 }
1723
1724 for( SCH_SHEET_PATH& path : paths )
1725 item_paths.insert( std::make_pair( path, itemData.linked_item ) );
1726 };
1727
1728 // Get all changed connectable items and determine all changed screens
1729 for( unsigned ii = 0; ii < aLastChangeList->GetCount(); ++ii )
1730 {
1731 switch( aLastChangeList->GetPickedItemStatus( ii ) )
1732 {
1733 // Only care about changed, new, and deleted items, the other
1734 // cases are not connectivity-related
1735 case UNDO_REDO::CHANGED:
1736 case UNDO_REDO::NEWITEM:
1737 case UNDO_REDO::DELETED: break;
1738
1739 default: continue;
1740 }
1741
1742 SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItem( ii ) );
1743
1744 if( item )
1745 {
1746 SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( aLastChangeList->GetScreenForItem( ii ) );
1747 changed_screens.insert( screen );
1748
1749 if( item->Type() == SCH_RULE_AREA_T )
1750 {
1751 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( item );
1752 changed_rule_areas.insert( { ruleArea, screen } );
1753 }
1754 else if( item->IsConnectable() )
1755 {
1756 SCH_ITEM* linked_item = dynamic_cast<SCH_ITEM*>( aLastChangeList->GetPickedItemLink( ii ) );
1757 changed_connectable_items.push_back( { item, linked_item, screen } );
1758 }
1759 }
1760 }
1761
1762 // Update rule areas in changed screens to propagate any directive connectivity changes
1763 std::vector<std::pair<SCH_RULE_AREA*, SCH_SCREEN*>> forceUpdateRuleAreas =
1764 SCH_RULE_AREA::UpdateRuleAreasInScreens( changed_screens, aSchView );
1765
1766 std::for_each( forceUpdateRuleAreas.begin(), forceUpdateRuleAreas.end(),
1767 [&]( std::pair<SCH_RULE_AREA*, SCH_SCREEN*>& updatedRuleArea )
1768 {
1769 changed_rule_areas.insert( updatedRuleArea );
1770 } );
1771
1772 // If a SCH_RULE_AREA was changed, we need to add all past and present contained items to
1773 // update their connectivity
1774 std::map<KIID, EDA_ITEM*> itemMap;
1775 list.FillItemMap( itemMap );
1776
1777 auto addPastAndPresentContainedItems = [&]( SCH_RULE_AREA* changedRuleArea, SCH_SCREEN* screen )
1778 {
1779 for( const KIID& pastItem : changedRuleArea->GetPastContainedItems() )
1780 {
1781 if( itemMap.contains( pastItem ) )
1782 addItemToChangeSet( { static_cast<SCH_ITEM*>( itemMap[pastItem] ), nullptr, screen } );
1783 }
1784
1785 for( SCH_ITEM* containedItem : changedRuleArea->GetContainedItems() )
1786 addItemToChangeSet( { containedItem, nullptr, screen } );
1787 };
1788
1789 for( const auto& [changedRuleArea, screen] : changed_rule_areas )
1790 addPastAndPresentContainedItems( changedRuleArea, screen );
1791
1792 // Add all changed items, and associated items, to the change set
1793 for( CHANGED_ITEM& changed_item_data : changed_connectable_items )
1794 {
1795 addItemToChangeSet( changed_item_data );
1796
1797 // If a SCH_DIRECTIVE_LABEL was changed which is attached to a SCH_RULE_AREA, we need
1798 // to add the contained items to the change set to force update of their connectivity
1799 if( changed_item_data.item->Type() == SCH_DIRECTIVE_LABEL_T )
1800 {
1801 const std::vector<VECTOR2I> labelConnectionPoints = changed_item_data.item->GetConnectionPoints();
1802
1803 auto candidateRuleAreas = changed_item_data.screen->Items().Overlapping(
1804 SCH_RULE_AREA_T, changed_item_data.item->GetBoundingBox() );
1805
1806 for( SCH_ITEM* candidateRuleArea : candidateRuleAreas )
1807 {
1808 SCH_RULE_AREA* ruleArea = static_cast<SCH_RULE_AREA*>( candidateRuleArea );
1809 std::vector<SHAPE*> borderShapes = ruleArea->MakeEffectiveShapes( true );
1810
1811 if( ruleArea->GetPolyShape().CollideEdge( labelConnectionPoints[0], nullptr, 5 ) )
1812 addPastAndPresentContainedItems( ruleArea, changed_item_data.screen );
1813 }
1814 }
1815 }
1816
1817 for( const VECTOR2I& pt : pts )
1818 {
1819 for( SCH_ITEM* item : GetCurrentScreen()->Items().Overlapping( pt ) )
1820 {
1821 // Leave this check in place. Overlapping items are not necessarily connectable.
1822 if( !item->IsConnectable() )
1823 continue;
1824
1825 if( item->Type() == SCH_LINE_T )
1826 {
1827 if( item->HitTest( pt ) )
1828 changed_items.insert( item );
1829 }
1830 else if( item->Type() == SCH_SYMBOL_T && item->IsConnected( pt ) )
1831 {
1832 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1833 std::vector<SCH_PIN*> pins = symbol->GetPins();
1834
1835 changed_items.insert( pins.begin(), pins.end() );
1836 }
1837 else if( item->Type() == SCH_SHEET_T )
1838 {
1839 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1840
1841 wxCHECK2( sheet, continue );
1842
1843 std::vector<SCH_SHEET_PIN*> sheetPins = sheet->GetPins();
1844 changed_items.insert( sheetPins.begin(), sheetPins.end() );
1845 }
1846 else
1847 {
1848 if( item->IsConnected( pt ) )
1849 changed_items.insert( item );
1850 }
1851 }
1852 }
1853
1854 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> all_items =
1855 ConnectionGraph()->ExtractAffectedItems( changed_items );
1856
1857 all_items.insert( item_paths.begin(), item_paths.end() );
1858
1859 CONNECTION_GRAPH new_graph( this );
1860
1861 new_graph.SetLastCodes( ConnectionGraph() );
1862
1863 std::shared_ptr<NET_SETTINGS> netSettings = m_project->GetProjectFile().NetSettings();
1864
1865 std::set<wxString> affectedNets;
1866
1867 for( auto& [path, item] : all_items )
1868 {
1869 wxCHECK2( item, continue );
1870 item->SetConnectivityDirty();
1871 SCH_CONNECTION* conn = item->Connection();
1872
1873 if( conn )
1874 affectedNets.insert( conn->Name() );
1875 }
1876
1877 // Reset resolved netclass cache for this connection
1878 for( const wxString& netName : affectedNets )
1879 netSettings->ClearCacheForNet( netName );
1880
1881 new_graph.Recalculate( list, false, aChangedItemHandler, aProgressReporter );
1882 ConnectionGraph()->Merge( new_graph );
1883 }
1884
1885 if( !localCommit.Empty() )
1886 localCommit.Push( _( "Schematic Cleanup" ) );
1887}
1888
1889
1891{
1892 Reset();
1893
1894 // Create virtual root with niluuid and a screen to hold top-level sheets
1895 SCH_SHEET* virtualRoot = new SCH_SHEET( this );
1896 const_cast<KIID&>( virtualRoot->m_Uuid ) = niluuid;
1897 virtualRoot->SetScreen( new SCH_SCREEN( this ) ); // Virtual root has a screen
1898
1899 // Create the actual first top-level sheet
1900 SCH_SHEET* rootSheet = new SCH_SHEET( this );
1901 SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
1902
1903 const_cast<KIID&>( rootSheet->m_Uuid ) = rootScreen->GetUuid();
1904 rootSheet->SetScreen( rootScreen );
1905 rootScreen->SetFileName( "untitled.kicad_sch" ); // Set default filename to avoid conflicts
1906 rootScreen->SetPageNumber( wxT( "1" ) );
1907
1908 // Set parent to virtual root
1909 rootSheet->SetParent( virtualRoot );
1910
1911 // Add the top-level sheet to the virtual root's screen
1912 virtualRoot->GetScreen()->Append( rootSheet );
1913
1914 // Don't leave root page number empty
1915 SCH_SHEET_PATH rootSheetPath;
1916 rootSheetPath.push_back( virtualRoot );
1917 rootSheetPath.push_back( rootSheet );
1918 rootSheetPath.SetPageNumber( wxT( "1" ) );
1919
1920 // Set up the schematic structure
1921 m_rootSheet = virtualRoot;
1922 m_topLevelSheets.clear();
1923 m_topLevelSheets.push_back( rootSheet );
1924
1925 m_currentSheet->clear();
1926 m_currentSheet->push_back( rootSheet );
1927
1928 // Rehash sheetpaths in hierarchy since we changed the uuid.
1930}
1931
1932
1933std::vector<SCH_SHEET*> SCHEMATIC::GetTopLevelSheets() const
1934{
1935 return m_topLevelSheets;
1936}
1937
1938
1940{
1941 wxCHECK_RET( aSheet, wxS( "Cannot add null sheet!" ) );
1942 wxCHECK_RET( aSheet->GetScreen(), wxS( "Cannot add virtual root as top-level sheet!" ) );
1943
1944 // Make sure we have a virtual root
1945 if( m_rootSheet == nullptr )
1946 {
1947 m_rootSheet = new SCH_SHEET( this );
1948 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
1949 // Create screen only if we have a project
1950 if( m_project )
1951 {
1952 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
1953 }
1954 }
1955
1956 // Ensure virtual root has a screen (may have been created before project was set)
1957 if( !m_rootSheet->GetScreen() && m_project )
1958 {
1959 m_rootSheet->SetScreen( new SCH_SCREEN( this ) );
1960 }
1961
1962 // Set parent to virtual root
1963 aSheet->SetParent( m_rootSheet );
1964
1965 // Add to the virtual root's screen if it exists
1966 if( m_rootSheet->GetScreen() )
1967 {
1968 m_rootSheet->GetScreen()->Append( aSheet );
1969 }
1970
1971 // Add to our list
1972 m_topLevelSheets.push_back( aSheet );
1973
1974 // Refresh hierarchy
1976}
1977
1978
1980{
1981 auto it = std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet );
1982
1983 if( it == m_topLevelSheets.end() )
1984 return false;
1985
1986 m_topLevelSheets.erase( it );
1987
1988 // If we're removing the current sheet, switch to another one
1989 if( !m_currentSheet->empty() && m_currentSheet->at( 0 ) == aSheet )
1990 {
1991 m_currentSheet->clear();
1992 if( !m_topLevelSheets.empty() )
1993 {
1994 m_currentSheet->push_back( m_topLevelSheets[0] );
1995 }
1996 }
1997
1999 return true;
2000}
2001
2002
2003bool SCHEMATIC::IsTopLevelSheet( const SCH_SHEET* aSheet ) const
2004{
2005 return std::find( m_topLevelSheets.begin(), m_topLevelSheets.end(), aSheet ) != m_topLevelSheets.end();
2006}
2007
2008
2010{
2011 SCH_SHEET_LIST hierarchy;
2012
2013 wxLogTrace( traceSchSheetPaths, "BuildSheetListSortedByPageNumbers: %zu top-level sheets",
2014 m_topLevelSheets.size() );
2015
2016 // Can't build hierarchy without top-level sheets
2017 if( m_topLevelSheets.empty() )
2018 return hierarchy;
2019
2020 // For each top-level sheet, build its hierarchy
2021 for( SCH_SHEET* sheet : m_topLevelSheets )
2022 {
2023 if( sheet )
2024 {
2025 wxLogTrace( traceSchSheetPaths, " Top-level sheet: '%s' (UUID=%s, isVirtualRoot=%d)", sheet->GetName(),
2026 sheet->m_Uuid.AsString(), sheet->m_Uuid == niluuid ? 1 : 0 );
2027
2028 // Build the sheet list for this top-level sheet
2029 SCH_SHEET_LIST sheetList;
2030 sheetList.BuildSheetList( sheet, false );
2031
2032 // Add all sheets from this top-level sheet's hierarchy
2033 for( const SCH_SHEET_PATH& path : sheetList )
2034 {
2035 hierarchy.push_back( path );
2036 }
2037 }
2038 }
2039
2040 hierarchy.SortByPageNumbers();
2041
2042 return hierarchy;
2043}
2044
2045
2047{
2048 SCH_SHEET_LIST sheets;
2049
2050 // For each top-level sheet, build its hierarchy
2051 for( SCH_SHEET* sheet : m_topLevelSheets )
2052 {
2053 if( sheet )
2054 {
2055 SCH_SHEET_LIST sheetList;
2056 sheetList.BuildSheetList( sheet, false );
2057
2058 // Add all sheets from this top-level sheet's hierarchy
2059 for( const SCH_SHEET_PATH& path : sheetList )
2060 {
2061 sheets.push_back( path );
2062 }
2063 }
2064 }
2065
2066 return sheets;
2067}
2068
2069
2071{
2072 wxArrayString variantNames;
2073
2074 // There is no default variant name. This is just a place holder for UI controls.
2075 variantNames.Add( GetDefaultVariantName() );
2076
2077 for( const wxString& name : m_variantNames )
2078 variantNames.Add( name );
2079
2080 return variantNames;
2081}
2082
2083
2085{
2086 if( m_currentVariant.IsEmpty() || ( m_currentVariant == GetDefaultVariantName() ) )
2087 return wxEmptyString;
2088
2089 return m_currentVariant;
2090}
2091
2092
2093void SCHEMATIC::SetCurrentVariant( const wxString& aVariantName )
2094{
2095 // Internally an empty string is the default variant. Set to default if the variant name doesn't exist.
2096 if( ( aVariantName == GetDefaultVariantName() ) || !m_variantNames.contains( aVariantName ) )
2097 m_currentVariant = wxEmptyString;
2098 else
2099 m_currentVariant = aVariantName;
2100}
2101
2102
2103void SCHEMATIC::DeleteVariant( const wxString& aVariantName )
2104{
2105 wxCHECK( m_rootSheet, /* void */ );
2106
2107 SCH_SCREENS allScreens( m_rootSheet );
2108
2109 allScreens.DeleteVariant( aVariantName );
2110}
2111
2112
2113void SCHEMATIC::SaveToHistory( const wxString& aProjectPath, std::vector<wxString>& aFiles )
2114{
2115 wxString projPath = m_project->GetProjectPath();
2116
2117 if( projPath.IsEmpty() )
2118 return; // no project yet
2119
2120 // Verify we're saving for the correct project
2121 if( !projPath.IsSameAs( aProjectPath ) )
2122 {
2123 wxLogTrace( traceAutoSave, wxS( "[history] sch saver skipping - project path mismatch: %s vs %s" ), projPath,
2124 aProjectPath );
2125 return;
2126 }
2127
2128 // Ensure project path has trailing separator for StartsWith tests & Mid calculations.
2129 if( !projPath.EndsWith( wxFILE_SEP_PATH ) )
2130 projPath += wxFILE_SEP_PATH;
2131
2132 wxFileName historyRoot( projPath, wxEmptyString );
2133 historyRoot.AppendDir( wxS( ".history" ) );
2134 wxString historyRootPath = historyRoot.GetPath();
2135
2136 // Iterate full schematic hierarchy (all sheets & their screens).
2137 SCH_SHEET_LIST sheetList = Hierarchy();
2138
2139 // Acquire plugin once.
2140 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
2141
2142 for( const SCH_SHEET_PATH& path : sheetList )
2143 {
2144 SCH_SHEET* sheet = path.Last();
2145 SCH_SCREEN* screen = path.LastScreen();
2146
2147 if( !sheet || !screen )
2148 continue;
2149
2150 wxFileName abs = m_project->AbsolutePath( screen->GetFileName() );
2151
2152 if( !abs.IsOk() )
2153 continue; // no filename
2154
2155 wxString absPath = abs.GetFullPath();
2156
2157 if( absPath.IsEmpty() || !absPath.StartsWith( projPath ) )
2158 continue; // external / unsaved subsheet
2159
2160 wxString rel = absPath.Mid( projPath.length() );
2161
2162 // Destination mirrors project-relative path under .history
2163 wxFileName dst( historyRootPath, rel );
2164
2165 // Ensure destination directory exists
2166 wxFileName dstDir( dst );
2167 dstDir.SetFullName( wxEmptyString );
2168
2169 if( !dstDir.DirExists() )
2170 wxFileName::Mkdir( dstDir.GetPath(), 0777, wxPATH_MKDIR_FULL );
2171
2172 try
2173 {
2174 pi->SaveSchematicFile( dst.GetFullPath(), sheet, this );
2175 aFiles.push_back( dst.GetFullPath() );
2176 wxLogTrace( traceAutoSave, wxS( "[history] sch saver exported sheet '%s' -> '%s'" ), absPath,
2177 dst.GetFullPath() );
2178 }
2179 catch( const IO_ERROR& ioe )
2180 {
2181 wxLogTrace( traceAutoSave, wxS( "[history] sch saver export failed for '%s': %s" ), absPath,
2182 wxString::FromUTF8( ioe.What() ) );
2183 }
2184 }
2185}
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:148
const KIID m_Uuid
Definition eda_item.h:516
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:145
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:336
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
wxString AsString() const
Definition kiid.cpp:246
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.
void SetCurrentVariant(const wxString &aVariantName)
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:518
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:540
void ResolveERCExclusionsPostUpdate()
Update markers to match recorded exclusions.
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.
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:515
std::vector< SCH_SHEET * > m_topLevelSheets
List of top-level sheets (direct children of virtual root)
Definition schematic.h:506
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.
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.
std::vector< SCH_MARKER * > ResolveERCExclusions()
void EmbedFonts() override
Embed fonts in the schematic.
SCHEMATIC_SETTINGS & Settings() const
SCH_SCREEN * GetCurrentScreen() const
Definition schematic.h:196
wxString ConvertKIIDsToRefs(const wxString &aSource) const
SCH_ITEM * ResolveItem(const KIID &aID, SCH_SHEET_PATH *aPathOut=nullptr, bool aAllowNullptrReturn=false) const
Definition schematic.h:119
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:558
std::map< wxString, std::set< int > > & GetPageRefsMap()
Definition schematic.h:243
SCH_SHEET_LIST BuildUnorderedSheetList() const
void DeleteVariant(const wxString &aVariantName)
Delete all information for aVariantName.
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.
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 SetRoot(SCH_SHEET *aRootSheet)
Initialize the schematic with a new root sheet.
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:500
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:198
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:556
void InvokeListeners(Func &&aFunc, Args &&... args)
Definition schematic.h:494
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:171
void updateProjectBusAliases()
static bool m_IsSchematicExists
True if a SCHEMATIC exists, false if not.
Definition schematic.h:472
void RemoveAllListeners()
Remove all listeners.
void GetContextualTextVars(wxArrayString *aVars) const
SCH_SHEET & Root() const
Definition schematic.h:125
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:550
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:545
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:186
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:535
ERC_SETTINGS & ErcSettings() const
std::set< wxString > m_variantNames
Definition schematic.h:560
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:503
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:116
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:507
int GetUnit() const
Definition sch_item.h:238
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:321
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:728
SCH_SCREEN * GetNext()
SCH_SCREEN * GetFirst()
void DeleteVariant(const wxString &aVariantName)
std::set< wxString > GetVariantNames() const
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:117
const wxString & GetFileName() const
Definition sch_screen.h:152
const KIID & GetUuid() const
Definition sch_screen.h:520
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
const TITLE_BLOCK & GetTitleBlock() const
Definition sch_screen.h:163
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:47
wxString GetName() const
Definition sch_sheet.h:113
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:116
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:198
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 traceSchCurrentSheet
Flag to enable debug output of current sheet tracking in the schematic editor.
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()
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