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