KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_sheet_path.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 (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <refdes_utils.h>
27#include <hash.h>
28#include <sch_screen.h>
29#include <sch_marker.h>
30#include <sch_label.h>
31#include <sch_shape.h>
32#include <sch_sheet_path.h>
33#include <sch_symbol.h>
34#include <sch_sheet.h>
35#include <schematic.h>
36#include <template_fieldnames.h>
37#include <trace_helpers.h>
38
39#include <wx/filename.h>
40#include <wx/log.h>
41
42
49{
50public:
52 SCH_ITEM( nullptr, NOT_USED )
53 {}
54
55 wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const override
56 {
57 return _( "(Deleted Item)" );
58 }
59
60 wxString GetClass() const override
61 {
62 return wxT( "DELETED_SHEET_ITEM" );
63 }
64
66 {
67 static DELETED_SHEET_ITEM* item = nullptr;
68
69 if( !item )
70 item = new DELETED_SHEET_ITEM();
71
72 return item;
73 }
74
75 // pure virtuals:
76 void SetPosition( const VECTOR2I& ) override {}
77 void Move( const VECTOR2I& aMoveVector ) override {}
78 void MirrorHorizontally( int aCenter ) override {}
79 void MirrorVertically( int aCenter ) override {}
80 void Rotate( const VECTOR2I& aCenter, bool aRotateCCW ) override {}
81
82 double Similarity( const SCH_ITEM& aOther ) const override
83 {
84 return 0.0;
85 }
86
87 bool operator==( const SCH_ITEM& aOther ) const override
88 {
89 return false;
90 }
91
92#if defined(DEBUG)
93 void Show( int , std::ostream& ) const override {}
94#endif
95};
96
97
99{
100 m_DNP = aSymbol.GetDNP();
103}
104
105
106namespace std
107{
109 {
110 return path.GetCurrentHash();
111 }
112}
113
114
120
121
123{
124 initFromOther( aOther );
125}
126
127
129{
130 initFromOther( aOther );
131 return *this;
132}
133
134
135// Move assignment operator
137{
138 m_sheets = std::move( aOther.m_sheets );
139
140 m_virtualPageNumber = aOther.m_virtualPageNumber;
141 m_current_hash = aOther.m_current_hash;
142 m_cached_page_number = aOther.m_cached_page_number;
143
144 m_recursion_test_cache = std::move( aOther.m_recursion_test_cache );
145
146 return *this;
147}
148
149
151{
152 SCH_SHEET_PATH retv = *this;
153
154 size_t size = aOther.size();
155
156 for( size_t i = 0; i < size; i++ )
157 retv.push_back( aOther.at( i ) );
158
159 return retv;
160}
161
162
164{
165 m_sheets = aOther.m_sheets;
169
170 // Note: don't copy m_recursion_test_cache as it is slow and we want std::vector<SCH_SHEET_PATH>
171 // to be very fast to construct for use in the connectivity algorithm.
173}
174
176{
177 m_current_hash = 0;
178
179 for( SCH_SHEET* sheet : m_sheets )
180 hash_combine( m_current_hash, sheet->m_Uuid.Hash() );
181}
182
183
184int SCH_SHEET_PATH::Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const
185{
186 if( size() > aSheetPathToTest.size() )
187 return 1;
188
189 if( size() < aSheetPathToTest.size() )
190 return -1;
191
192 // otherwise, same number of sheets.
193 for( unsigned i = 0; i < size(); i++ )
194 {
195 if( at( i )->m_Uuid < aSheetPathToTest.at( i )->m_Uuid )
196 return -1;
197
198 if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
199 return 1;
200 }
201
202 return 0;
203}
204
205
206int SCH_SHEET_PATH::ComparePageNum( const SCH_SHEET_PATH& aSheetPathToTest ) const
207{
208 wxString pageA = this->GetPageNumber();
209 wxString pageB = aSheetPathToTest.GetPageNumber();
210
211 int pageNumComp = SCH_SHEET::ComparePageNum( pageA, pageB );
212
213 if( pageNumComp == 0 )
214 {
215 int virtualPageA = GetVirtualPageNumber();
216 int virtualPageB = aSheetPathToTest.GetVirtualPageNumber();
217
218 if( virtualPageA > virtualPageB )
219 pageNumComp = 1;
220 else if( virtualPageA < virtualPageB )
221 pageNumComp = -1;
222 }
223
224 return pageNumComp;
225}
226
227
228bool SCH_SHEET_PATH::IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const
229{
230 if( aSheetPathToTest.size() > size() )
231 return false;
232
233 for( size_t i = 0; i < aSheetPathToTest.size(); ++i )
234 {
235 if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
236 {
237 wxLogTrace( traceSchSheetPaths, "Sheet path '%s' is not within path '%s'.",
238 aSheetPathToTest.Path().AsString(), Path().AsString() );
239
240 return false;
241 }
242 }
243
244 wxLogTrace( traceSchSheetPaths, "Sheet path '%s' is within path '%s'.",
245 aSheetPathToTest.Path().AsString(), Path().AsString() );
246
247 return true;
248}
249
250
252{
253 if( !empty() )
254 return m_sheets.back();
255
256 return nullptr;
257}
258
259
261{
262 SCH_SHEET* lastSheet = Last();
263
264 if( lastSheet )
265 return lastSheet->GetScreen();
266
267 return nullptr;
268}
269
270
272{
273 SCH_SHEET* lastSheet = Last();
274
275 if( lastSheet )
276 return lastSheet->GetScreen();
277
278 return nullptr;
279}
280
281
283{
284 for( SCH_SHEET* sheet : m_sheets )
285 {
286 if( sheet->GetExcludedFromSim() )
287 return true;
288 }
289
290 return false;
291}
292
293
295{
296 for( SCH_SHEET* sheet : m_sheets )
297 {
298 if( sheet->GetExcludedFromBOM() )
299 return true;
300 }
301
302 return false;
303}
304
305
307{
308 for( SCH_SHEET* sheet : m_sheets )
309 {
310 if( sheet->GetExcludedFromBoard() )
311 return true;
312 }
313
314 return false;
315}
316
317
319{
320 for( SCH_SHEET* sheet : m_sheets )
321 {
322 if( sheet->GetDNP() )
323 return true;
324 }
325
326 return false;
327}
328
329
331{
332 wxString s;
333
334 s = wxT( "/" ); // This is the root path
335
336 // Start at 1 to avoid the root sheet, which does not need to be added to the path.
337 // Its timestamp changes anyway.
338 for( unsigned i = 1; i < size(); i++ )
339 s += at( i )->m_Uuid.AsString() + "/";
340
341 return s;
342}
343
344
346{
348 size_t size = m_sheets.size();
349
350 if( m_sheets.empty() )
351 return path;
352
353 if( m_sheets[0]->m_Uuid != niluuid )
354 {
355 path.reserve( size );
356 path.push_back( m_sheets[0]->m_Uuid );
357 }
358 else
359 {
360 // Skip the virtual root
361 path.reserve( size - 1 );
362 }
363
364 for( size_t i = 1; i < size; i++ )
365 path.push_back( m_sheets[i]->m_Uuid );
366
367 return path;
368}
369
370
371wxString SCH_SHEET_PATH::PathHumanReadable( bool aUseShortRootName,
372 bool aStripTrailingSeparator ) const
373{
374 wxString s;
375
376 // Determine the starting index - skip virtual root if present
377 size_t startIdx = 0;
378
379 if( !empty() && at( 0 )->IsVirtualRootSheet() )
380 startIdx = 1;
381
382 if( aUseShortRootName )
383 {
384 s = wxS( "/" ); // Use only the short name in netlists
385 }
386 else
387 {
388 wxString fileName;
389
390 if( size() > startIdx && at( startIdx )->GetScreen() )
391 fileName = at( startIdx )->GetScreen()->GetFileName();
392
393 wxFileName fn = fileName;
394
395 s = fn.GetName() + wxS( "/" );
396 }
397
398 // Start at startIdx + 1 since we've already processed the root sheet.
399 for( unsigned i = startIdx + 1; i < size(); i++ )
400 s << at( i )->GetField( FIELD_T::SHEET_NAME )->GetShownText( false ) << wxS( "/" );
401
402 if( aStripTrailingSeparator && s.EndsWith( "/" ) )
403 s = s.Left( s.length() - 1 );
404
405 return s;
406}
407
408
410{
411 std::vector<SCH_ITEM*> items;
412
413 std::copy_if( LastScreen()->Items().begin(), LastScreen()->Items().end(),
414 std::back_inserter( items ),
415 []( SCH_ITEM* aItem )
416 {
417 return ( aItem->Type() == SCH_SYMBOL_T
418 || aItem->Type() == SCH_GLOBAL_LABEL_T
419 || aItem->Type() == SCH_SHAPE_T );
420 } );
421
422 std::optional<wxString> variantName;
423 const SCHEMATIC* schematic = LastScreen()->Schematic();
424
425 if( schematic )
426 variantName = schematic->GetCurrentVariant();
427
428 for( SCH_ITEM* item : items )
429 {
430 if( item->Type() == SCH_SYMBOL_T )
431 {
432 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
433
434 symbol->GetField( FIELD_T::REFERENCE )->SetText( symbol->GetRef( this ) );
435 symbol->SetUnit( symbol->GetUnitSelection( this ) );
436 LastScreen()->Update( item, false );
437 }
438 else if( item->Type() == SCH_GLOBAL_LABEL_T )
439 {
440 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( item );
441
442 if( label->GetFields().size() > 0 ) // Possible when reading a legacy .sch schematic
443 {
444 SCH_FIELD* intersheetRefs = label->GetField( FIELD_T::INTERSHEET_REFS );
445
446 // Fixup for legacy files which didn't store a position for the intersheet refs
447 // unless they were shown.
448 if( intersheetRefs->GetPosition() == VECTOR2I() && !intersheetRefs->IsVisible() )
450
451 intersheetRefs->SetVisible( label->Schematic()->Settings().m_IntersheetRefsShow );
452 LastScreen()->Update( intersheetRefs );
453 }
454 }
455 else if( item->Type() == SCH_SHAPE_T )
456 {
457 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
458 shape->UpdateHatching();
459 }
460 }
461}
462
463
464void SCH_SHEET_PATH::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
465 bool aForceIncludeOrphanSymbols ) const
466{
467 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
468 {
469 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
470 AppendSymbol( aReferences, symbol, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
471 }
472}
473
474
476 bool aIncludePowerSymbols,
477 bool aForceIncludeOrphanSymbols ) const
478{
479 // Skip pseudo-symbols, which have a reference starting with #. This mainly
480 // affects power symbols.
481 if( aIncludePowerSymbols || aSymbol->GetRef( this )[0] != wxT( '#' ) )
482 {
483 if( aSymbol->GetLibSymbolRef() || aForceIncludeOrphanSymbols )
484 {
485 SCH_REFERENCE schReference( aSymbol, *this );
486
487 schReference.SetSheetNumber( GetPageNumberAsInt() );
488 aReferences.AddItem( schReference );
489 }
490 }
491}
492
493
495 bool aIncludePowerSymbols ) const
496{
497 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
498 {
499 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
500 AppendMultiUnitSymbol( aRefList, symbol, aIncludePowerSymbols );
501 }
502}
503
504
506 SCH_SYMBOL* aSymbol,
507 bool aIncludePowerSymbols ) const
508{
509 // Skip pseudo-symbols, which have a reference starting with #. This mainly
510 // affects power symbols.
511 if( !aIncludePowerSymbols && aSymbol->GetRef( this )[0] == wxT( '#' ) )
512 return;
513
514 LIB_SYMBOL* symbol = aSymbol->GetLibSymbolRef().get();
515
516 if( symbol && symbol->GetUnitCount() > 1 )
517 {
518 SCH_REFERENCE schReference = SCH_REFERENCE( aSymbol, *this );
519 schReference.SetSheetNumber( GetPageNumberAsInt() );
520 wxString reference_str = schReference.GetRef();
521
522 // Never lock unassigned references
523 if( reference_str[reference_str.Len() - 1] == '?' )
524 return;
525
526 aRefList[reference_str].AddItem( schReference );
527 }
528}
529
530
532{
533 return m_current_hash == d1.GetCurrentHash();
534}
535
536
537bool SCH_SHEET_PATH::TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName )
538{
539 auto pair = std::make_pair( aSrcFileName, aDestFileName );
540
541 if( m_recursion_test_cache.count( pair ) )
542 return m_recursion_test_cache.at( pair );
543
544 SCHEMATIC* sch = LastScreen()->Schematic();
545
546 wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_PATH::TestForRecursion!" );
547
548 wxFileName rootFn = sch->GetFileName();
549 wxFileName srcFn = aSrcFileName;
550 wxFileName destFn = aDestFileName;
551
552 if( srcFn.IsRelative() )
553 srcFn.MakeAbsolute( rootFn.GetPath() );
554
555 if( destFn.IsRelative() )
556 destFn.MakeAbsolute( rootFn.GetPath() );
557
558 // The source and destination sheet file names cannot be the same.
559 if( srcFn == destFn )
560 {
561 m_recursion_test_cache[pair] = true;
562 return true;
563 }
564
568 unsigned i = 0;
569
570 while( i < size() )
571 {
572 wxFileName cmpFn = at( i )->GetFileName();
573
574 if( cmpFn.IsRelative() )
575 cmpFn.MakeAbsolute( rootFn.GetPath() );
576
577 // Test if the file name of the destination sheet is in anywhere in this sheet path.
578 if( cmpFn == destFn )
579 break;
580
581 i++;
582 }
583
584 // The destination sheet file name was not found in the sheet path or the destination
585 // sheet file name is the root sheet so no recursion is possible.
586 if( i >= size() || i == 0 )
587 {
588 m_recursion_test_cache[pair] = false;
589 return false;
590 }
591
592 // Walk back up to the root sheet to see if the source file name is already a parent in
593 // the sheet path. If so, recursion will occur.
594 do
595 {
596 i -= 1;
597
598 wxFileName cmpFn = at( i )->GetFileName();
599
600 if( cmpFn.IsRelative() )
601 cmpFn.MakeAbsolute( rootFn.GetPath() );
602
603 if( cmpFn == srcFn )
604 {
605 m_recursion_test_cache[pair] = true;
606 return true;
607 }
608
609 } while( i != 0 );
610
611 // The source sheet file name is not a parent of the destination sheet file name.
612 m_recursion_test_cache[pair] = false;
613 return false;
614}
615
616
618{
619 SCH_SHEET* sheet = Last();
620
621 wxCHECK( sheet, wxEmptyString );
622
623 KIID_PATH tmpPath = Path();
624 tmpPath.pop_back();
625
626 return sheet->getPageNumber( tmpPath );
627}
628
630{
631 long page;
632 wxString pageStr = GetPageNumber();
633
634 if( pageStr.ToLong( &page ) )
635 return (int) page;
636
637 return GetVirtualPageNumber();
638}
639
640
641void SCH_SHEET_PATH::SetPageNumber( const wxString& aPageNumber )
642{
643 SCH_SHEET* sheet = Last();
644
645 wxCHECK( sheet, /* void */ );
646
647 KIID_PATH tmpPath = Path();
648
649 tmpPath.pop_back();
650
651 sheet->addInstance( tmpPath );
652 sheet->setPageNumber( tmpPath, aPageNumber );
653}
654
655
657 const wxString& aProjectName )
658{
659 wxCHECK( !aProjectName.IsEmpty(), /* void */ );
660
661 SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
662 SCH_SHEET_PATH currentSheetPath( *this );
663
664 // Prefix the new hierarchical path.
665 newSheetPath = newSheetPath + currentSheetPath;
666
667 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
668 {
669 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
670
671 wxCHECK2( symbol, continue );
672
673 SCH_SYMBOL_INSTANCE newSymbolInstance;
674
675 if( symbol->GetInstance( newSymbolInstance, Path(), true ) )
676 {
677 newSymbolInstance.m_ProjectName = aProjectName;
678
679 // Use an existing symbol instance for this path if it exists.
680 newSymbolInstance.m_Path = newSheetPath.Path();
681 symbol->AddHierarchicalReference( newSymbolInstance );
682 }
683 else if( !symbol->GetInstances().empty() )
684 {
685 newSymbolInstance.m_ProjectName = aProjectName;
686
687 // Use the first symbol instance if any symbol instance data exists.
688 newSymbolInstance = symbol->GetInstances()[0];
689 newSymbolInstance.m_Path = newSheetPath.Path();
690 symbol->AddHierarchicalReference( newSymbolInstance );
691 }
692 else
693 {
694 newSymbolInstance.m_ProjectName = aProjectName;
695
696 // Fall back to the last saved symbol field and unit settings if there is no
697 // instance data.
698 newSymbolInstance.m_Path = newSheetPath.Path();
699 newSymbolInstance.m_Reference = symbol->GetField( FIELD_T::REFERENCE )->GetText();
700 newSymbolInstance.m_Unit = symbol->GetUnit();
701 symbol->AddHierarchicalReference( newSymbolInstance );
702 }
703 }
704}
705
706
708{
709 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
710 {
711 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
712
713 wxCHECK2( symbol, continue );
714
715 SCH_SHEET_PATH fullSheetPath( aPrefixSheetPath );
716 SCH_SHEET_PATH currentSheetPath( *this );
717
718 // Prefix the hierarchical path of the symbol instance to be removed.
719 fullSheetPath = fullSheetPath + currentSheetPath;
720 symbol->RemoveInstance( fullSheetPath );
721 }
722}
723
724
725void SCH_SHEET_PATH::CheckForMissingSymbolInstances( const wxString& aProjectName )
726{
727 // Skip sheet paths without screens (e.g., sheets that haven't been loaded yet or virtual root)
728 if( aProjectName.IsEmpty() || !LastScreen() )
729 return;
730
731 wxLogTrace( traceSchSheetPaths, "CheckForMissingSymbolInstances for path: %s (project: %s)",
732 PathHumanReadable( false ), aProjectName );
733 wxLogTrace( traceSchSheetPaths, " Sheet path size=%zu, Path().AsString()='%s'",
734 size(), Path().AsString() );
735
736 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
737 {
738 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
739
740 wxCHECK2( symbol, continue );
741
742 SCH_SYMBOL_INSTANCE symbolInstance;
743
744 if( !symbol->GetInstance( symbolInstance, Path() ) )
745 {
746 wxLogTrace( traceSchSheetPaths, "Adding missing symbol \"%s\" instance data for "
747 "sheet path '%s'.",
748 symbol->m_Uuid.AsString(), PathHumanReadable( false ) );
749
750 // Legacy schematics that are not shared do not contain separate instance data.
751 // The symbol reference and unit are saved in the reference field and unit entries.
752 if( ( LastScreen()->GetRefCount() <= 1 ) &&
753 ( LastScreen()->GetFileFormatVersionAtLoad() <= 20200310 ) )
754 {
755 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
756 symbolInstance.m_Reference = refField->GetShownText( this, true );
757 symbolInstance.m_Unit = symbol->GetUnit();
758
759 wxLogTrace( traceSchSheetPaths,
760 " Legacy format: Using reference '%s' from field, unit %d",
761 symbolInstance.m_Reference, symbolInstance.m_Unit );
762 }
763 else
764 {
765 // When schematics are shared, we cannot know which instance the current symbol
766 // reference field and unit belong to. In this case, we clear the reference
767 // annotation and set the unit to 1.
768 symbolInstance.m_Reference = UTIL::GetRefDesUnannotated( symbol->GetPrefix() );
769
770 wxLogTrace( traceSchSheetPaths,
771 " Shared schematic: Setting unannotated reference '%s'",
772 symbolInstance.m_Reference );
773 }
774
775 symbolInstance.m_ProjectName = aProjectName;
776 symbolInstance.m_Path = Path();
777 symbol->AddHierarchicalReference( symbolInstance );
778
779 wxLogTrace( traceSchSheetPaths,
780 " Created instance: ref=%s, path=%s",
781 symbolInstance.m_Reference, symbolInstance.m_Path.AsString() );
782 }
783 else
784 {
785 wxLogTrace( traceSchSheetPaths,
786 " Symbol %s already has instance: ref=%s, path=%s",
787 symbol->m_Uuid.AsString(),
788 symbolInstance.m_Reference,
789 symbolInstance.m_Path.AsString() );
790 }
791 }
792}
793
794
796{
797 wxCHECK( m_sheets.size() > 1, /* void */ );
798
799 wxFileName sheetFileName = Last()->GetFileName();
800
801 // If the sheet file name is absolute, then the user requested is so don't make it relative.
802 if( sheetFileName.IsAbsolute() )
803 return;
804
805 SCH_SCREEN* screen = LastScreen();
806 SCH_SCREEN* parentScreen = m_sheets[ m_sheets.size() - 2 ]->GetScreen();
807
808 wxCHECK( screen && parentScreen, /* void */ );
809
810 wxFileName fileName = screen->GetFileName();
811 wxFileName parentFileName = parentScreen->GetFileName();
812
813 // SCH_SCREEN file names must be absolute. If they are not, someone set them incorrectly
814 // on load or on creation.
815 wxCHECK( fileName.IsAbsolute() && parentFileName.IsAbsolute(), /* void */ );
816
817 if( fileName.GetPath() == parentFileName.GetPath() )
818 {
819 Last()->SetFileName( fileName.GetFullName() );
820 }
821 else if( fileName.MakeRelativeTo( parentFileName.GetPath() ) )
822 {
823 Last()->SetFileName( fileName.GetFullPath() );
824 }
825 else
826 {
827 Last()->SetFileName( screen->GetFileName() );
828 }
829
830 wxLogTrace( tracePathsAndFiles,
831 wxT( "\n File name: '%s'"
832 "\n parent file name '%s',"
833 "\n sheet '%s' file name '%s'." ),
834 screen->GetFileName(), parentScreen->GetFileName(), PathHumanReadable(),
835 Last()->GetFileName() );
836}
837
838
840{
841 if( aSheet != nullptr )
842 BuildSheetList( aSheet, false );
843}
844
845
846void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
847{
848 if( !aSheet )
849 return;
850
851 wxLogTrace( traceSchSheetPaths,
852 "BuildSheetList called with sheet '%s' (UUID=%s, isVirtualRoot=%d)",
853 aSheet->GetName(),
854 aSheet->m_Uuid.AsString(),
855 aSheet->m_Uuid == niluuid ? 1 : 0 );
856
857 // Special handling for virtual root: process its children without adding the root itself
858 if( aSheet->IsVirtualRootSheet() )
859 {
860 wxLogTrace( traceSchSheetPaths, " Skipping virtual root, processing children only" );
861
862 if( aSheet->GetScreen() )
863 {
864 std::vector<SCH_ITEM*> childSheets;
865 aSheet->GetScreen()->GetSheets( &childSheets );
866
867 for( SCH_ITEM* item : childSheets )
868 {
869 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
870 BuildSheetList( sheet, aCheckIntegrity );
871 }
872 }
873
874 return;
875 }
876
877 std::vector<SCH_SHEET*> badSheets;
878
879 m_currentSheetPath.push_back( aSheet );
880 m_currentSheetPath.SetVirtualPageNumber( static_cast<int>( size() ) + 1 );
881 push_back( m_currentSheetPath );
882
883 if( m_currentSheetPath.LastScreen() )
884 {
885 wxString parentFileName = aSheet->GetFileName();
886 std::vector<SCH_ITEM*> childSheets;
887 m_currentSheetPath.LastScreen()->GetSheets( &childSheets );
888
889 for( SCH_ITEM* item : childSheets )
890 {
891 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
892
893 if( aCheckIntegrity )
894 {
895 if( !m_currentSheetPath.TestForRecursion( sheet->GetFileName(), parentFileName ) )
896 BuildSheetList( sheet, true );
897 else
898 badSheets.push_back( sheet );
899 }
900 else
901 {
902 // If we are not performing a full recursion test, at least check if we are in
903 // a simple recursion scenario to prevent stack overflow crashes
904 wxCHECK2_MSG( sheet->GetFileName() != aSheet->GetFileName(), continue,
905 wxT( "Recursion prevented in SCH_SHEET_LIST::BuildSheetList" ) );
906
907 BuildSheetList( sheet, false );
908 }
909 }
910 }
911
912 if( aCheckIntegrity )
913 {
914 for( SCH_SHEET* sheet : badSheets )
915 {
916 m_currentSheetPath.LastScreen()->Remove( sheet );
917 m_currentSheetPath.LastScreen()->SetContentModified();
918 }
919 }
920
921 m_currentSheetPath.pop_back();
922}
923
924
925void SCH_SHEET_LIST::SortByHierarchicalPageNumbers( bool aUpdateVirtualPageNums )
926{
927 for( const SCH_SHEET_PATH& path : *this )
928 path.CachePageNumber();
929
930 std::sort( begin(), end(),
931 []( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b ) -> bool
932 {
933 // Find the divergence point in the paths
934 size_t common_len = 0;
935 size_t min_len = std::min( a.size(), b.size() );
936
937 while( common_len < min_len && a.at( common_len )->m_Uuid == b.at( common_len )->m_Uuid )
938 common_len++;
939
940 // If one path is a prefix of the other, the shorter one comes first
941 // This ensures parents come before children
942 if( common_len == a.size() )
943 return true; // a is a prefix of b - a is the parent
944 if( common_len == b.size() )
945 return false; // b is a prefix of a - b is the parent
946
947 // Paths diverge at common_len
948 // If they share the same parent, sort by page number
949 // This ensures siblings are sorted by page number
950 SCH_SHEET* sheet_a = a.at( common_len );
951 SCH_SHEET* sheet_b = b.at( common_len );
952
953 // Create partial paths to get to these sheets for page number comparison
954 KIID_PATH ancestor;
955 for( size_t i = 0; i < common_len; i++ )
956 ancestor.push_back( a.at( i )->m_Uuid );
957
958 // Compare page numbers - use the last sheet's page number
959 wxString page_a = sheet_a->getPageNumber( ancestor );
960 wxString page_b = sheet_b->getPageNumber( ancestor );
961
962 int retval = SCH_SHEET::ComparePageNum( page_a, page_b );
963
964 if( retval != 0 )
965 return retval < 0;
966
967 // If page numbers are the same, use virtual page numbers as a tie-breaker
969 return true;
970 else if( a.GetVirtualPageNumber() > b.GetVirtualPageNumber() )
971 return false;
972
973 // Finally, use UUIDs for stable ordering when everything else is equal
974 return a.GetCurrentHash() < b.GetCurrentHash();
975 } );
976
977 if( aUpdateVirtualPageNums )
978 {
979 int virtualPageNum = 1;
980
981 for( SCH_SHEET_PATH& sheet : *this )
982 sheet.SetVirtualPageNumber( virtualPageNum++ );
983 }
984}
985
986
987void SCH_SHEET_LIST::SortByPageNumbers( bool aUpdateVirtualPageNums )
988{
989 for( const SCH_SHEET_PATH& path : *this )
990 path.CachePageNumber();
991
992 std::sort( begin(), end(),
993 []( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b ) -> bool
994 {
997
998 if( retval < 0 )
999 return true;
1000 else if( retval > 0 )
1001 return false;
1002
1004 return true;
1005 else if( a.GetVirtualPageNumber() > b.GetVirtualPageNumber() )
1006 return false;
1007
1008 // Enforce strict ordering. If the page numbers are the same, use UUIDs
1009 return a.GetCurrentHash() < b.GetCurrentHash();
1010 } );
1011
1012 if( aUpdateVirtualPageNums )
1013 {
1014 int virtualPageNum = 1;
1015
1016 for( SCH_SHEET_PATH& sheet : *this )
1017 sheet.SetVirtualPageNumber( virtualPageNum++ );
1018 }
1019}
1020
1021
1022bool SCH_SHEET_LIST::NameExists( const wxString& aSheetName ) const
1023{
1024 for( const SCH_SHEET_PATH& sheet : *this )
1025 {
1026 if( sheet.Last()->GetName() == aSheetName )
1027 return true;
1028 }
1029
1030 return false;
1031}
1032
1033
1034bool SCH_SHEET_LIST::PageNumberExists( const wxString& aPageNumber ) const
1035{
1036 for( const SCH_SHEET_PATH& sheet : *this )
1037 {
1038 if( sheet.GetPageNumber() == aPageNumber )
1039 return true;
1040 }
1041
1042 return false;
1043}
1044
1045
1046void SCH_SHEET_LIST::TrimToPageNumbers( const std::vector<wxString>& aPageInclusions )
1047{
1048 auto it = std::remove_if( begin(), end(),
1049 [&]( const SCH_SHEET_PATH& sheet )
1050 {
1051 return std::find( aPageInclusions.begin(),
1052 aPageInclusions.end(),
1053 sheet.GetPageNumber() ) == aPageInclusions.end();
1054 } );
1055
1056 erase( it, end() );
1057}
1058
1059
1061{
1062 for( const SCH_SHEET_PATH& sheet : *this )
1063 {
1064 if( sheet.LastScreen() && sheet.LastScreen()->IsContentModified() )
1065 return true;
1066 }
1067
1068 return false;
1069}
1070
1071
1073{
1074 for( const SCH_SHEET_PATH& sheet : *this )
1075 {
1076 if( sheet.LastScreen() )
1077 sheet.LastScreen()->SetContentModified( false );
1078 }
1079}
1080
1081
1082SCH_ITEM* SCH_SHEET_LIST::ResolveItem( const KIID& aID, SCH_SHEET_PATH* aPathOut, bool aAllowNullptrReturn ) const
1083{
1084 for( const SCH_SHEET_PATH& sheet : *this )
1085 {
1086 SCH_ITEM* item = sheet.ResolveItem( aID );
1087
1088 if( item )
1089 {
1090 if( aPathOut )
1091 *aPathOut = sheet;
1092
1093 return item;
1094 }
1095 }
1096
1097 // Not found; weak reference has been deleted.
1098 if( aAllowNullptrReturn )
1099 return nullptr;
1100 else
1102}
1103
1104
1106{
1107 for( SCH_ITEM* aItem : LastScreen()->Items() )
1108 {
1109 if( aItem->m_Uuid == aID )
1110 return aItem;
1111
1112 SCH_ITEM* childMatch = nullptr;
1113
1114 aItem->RunOnChildren(
1115 [&]( SCH_ITEM* aChild )
1116 {
1117 if( aChild->m_Uuid == aID )
1118 childMatch = aChild;
1119 },
1121
1122 if( childMatch )
1123 return childMatch;
1124 }
1125
1126 return nullptr;
1127}
1128
1129
1130void SCH_SHEET_LIST::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
1131{
1132 for( const SCH_SHEET_PATH& sheet : *this )
1133 {
1134 SCH_SCREEN* screen = sheet.LastScreen();
1135
1136 for( SCH_ITEM* aItem : screen->Items() )
1137 {
1138 aMap[ aItem->m_Uuid ] = aItem;
1139
1140 aItem->RunOnChildren(
1141 [&]( SCH_ITEM* aChild )
1142 {
1143 aMap[ aChild->m_Uuid ] = aChild;
1144 },
1146 }
1147 }
1148}
1149
1150
1152{
1153 // List of reference for power symbols
1154 SCH_REFERENCE_LIST references;
1155 SCH_REFERENCE_LIST additionalreferences; // Todo: add as a parameter to this function
1156
1157 // Map of locked symbols (not used, but needed by Annotate()
1158 SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
1159
1160 // Build the list of power symbols:
1161 for( SCH_SHEET_PATH& sheet : *this )
1162 {
1163 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1164 {
1165 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1166 LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
1167
1168 if( libSymbol && libSymbol->IsPower() )
1169 {
1170 SCH_REFERENCE schReference( symbol, sheet );
1171 references.AddItem( schReference );
1172 }
1173 }
1174 }
1175
1176 // Find duplicate, and silently clear annotation of duplicate
1177 std::map<wxString, int> ref_list; // stores the existing references
1178
1179 for( unsigned ii = 0; ii< references.GetCount(); ++ii )
1180 {
1181 wxString curr_ref = references[ii].GetRef();
1182
1183 if( curr_ref.IsEmpty() )
1184 continue;
1185
1186 if( ref_list.find( curr_ref ) == ref_list.end() )
1187 {
1188 ref_list[curr_ref] = ii;
1189 continue;
1190 }
1191
1192 // Possible duplicate, if the ref ends by a number:
1193 if( curr_ref.Last() < '0' && curr_ref.Last() > '9' )
1194 continue; // not annotated
1195
1196 // Duplicate: clear annotation by removing the number ending the ref
1197 while( !curr_ref.IsEmpty() && curr_ref.Last() >= '0' && curr_ref.Last() <= '9' )
1198 curr_ref.RemoveLast();
1199
1200 references[ii].SetRef( curr_ref );
1201 }
1202
1203 // Break full symbol reference into name (prefix) and number:
1204 // example: IC1 become IC, and 1
1205 references.SplitReferences();
1206
1207 // Ensure all power symbols have the reference starting by '#'
1208 // (Not sure this is really useful)
1209 for( unsigned ii = 0; ii< references.GetCount(); ++ii )
1210 {
1211 SCH_REFERENCE& ref_unit = references[ii];
1212
1213 if( ref_unit.GetRef()[0] != '#' )
1214 {
1215 wxString new_ref = "#" + ref_unit.GetRef();
1216 ref_unit.SetRef( new_ref );
1217 ref_unit.SetRefNum( ii );
1218 }
1219 }
1220}
1221
1222
1223void SCH_SHEET_LIST::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
1224 bool aForceIncludeOrphanSymbols ) const
1225{
1226 for( const SCH_SHEET_PATH& sheet : *this )
1227 sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
1228}
1229
1230
1232 const SCH_SHEET_PATH& aSheetPath,
1233 bool aIncludePowerSymbols,
1234 bool aForceIncludeOrphanSymbols ) const
1235{
1236 for( const SCH_SHEET_PATH& sheet : *this )
1237 {
1238 if( sheet.IsContainedWithin( aSheetPath ) )
1239 sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
1240 }
1241}
1242
1243
1244void SCH_SHEET_LIST::GetSheetsWithinPath( std::vector<SCH_SHEET_PATH>& aSheets,
1245 const SCH_SHEET_PATH& aSheetPath ) const
1246{
1247 for( const SCH_SHEET_PATH& sheet : *this )
1248 {
1249 if( sheet.IsContainedWithin( aSheetPath ) )
1250 aSheets.push_back( sheet );
1251 }
1252}
1253
1254
1255std::optional<SCH_SHEET_PATH> SCH_SHEET_LIST::GetSheetPathByKIIDPath( const KIID_PATH& aPath,
1256 bool aIncludeLastSheet ) const
1257{
1258 for( const SCH_SHEET_PATH& sheet : *this )
1259 {
1260 KIID_PATH testPath = sheet.Path();
1261
1262 if( !aIncludeLastSheet )
1263 testPath.pop_back();
1264
1265 if( testPath == aPath )
1266 return SCH_SHEET_PATH( sheet );
1267 }
1268
1269 return std::nullopt;
1270}
1271
1272
1274 bool aIncludePowerSymbols ) const
1275{
1276 for( auto it = begin(); it != end(); ++it )
1277 {
1279 ( *it ).GetMultiUnitSymbols( tempMap, aIncludePowerSymbols );
1280
1281 for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : tempMap )
1282 {
1283 // Merge this list into the main one
1284 unsigned n_refs = pair.second.GetCount();
1285
1286 for( unsigned thisRef = 0; thisRef < n_refs; ++thisRef )
1287 aRefList[pair.first].AddItem( pair.second[thisRef] );
1288 }
1289 }
1290}
1291
1292
1293bool SCH_SHEET_LIST::TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
1294 const wxString& aDestFileName )
1295{
1296 if( empty() )
1297 return false;
1298
1299 SCHEMATIC* sch = at( 0 ).LastScreen()->Schematic();
1300
1301 wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_LIST::TestForRecursion!" );
1302
1303 wxFileName rootFn = sch->GetFileName();
1304 wxFileName destFn = aDestFileName;
1305
1306 if( destFn.IsRelative() )
1307 destFn.MakeAbsolute( rootFn.GetPath() );
1308
1309 // Test each SCH_SHEET_PATH in this SCH_SHEET_LIST for potential recursion.
1310 for( unsigned i = 0; i < size(); i++ )
1311 {
1312 // Test each SCH_SHEET_PATH in the source sheet.
1313 for( unsigned j = 0; j < aSrcSheetHierarchy.size(); j++ )
1314 {
1315 const SCH_SHEET_PATH* sheetPath = &aSrcSheetHierarchy[j];
1316
1317 for( unsigned k = 0; k < sheetPath->size(); k++ )
1318 {
1319 if( at( i ).TestForRecursion( sheetPath->GetSheet( k )->GetFileName(),
1320 aDestFileName ) )
1321 {
1322 return true;
1323 }
1324 }
1325 }
1326 }
1327
1328 // The source sheet file can safely be added to the destination sheet file.
1329 return false;
1330}
1331
1332
1334{
1335 for( SCH_SHEET_PATH& path : *this )
1336 {
1337 if( path.Path() == aPath->Path() )
1338 return &path;
1339 }
1340
1341 return nullptr;
1342}
1343
1344
1346{
1347 for( SCH_SHEET_PATH& sheetpath : *this )
1348 {
1349 if( sheetpath.LastScreen() == aScreen )
1350 return sheetpath;
1351 }
1352
1353 return SCH_SHEET_PATH();
1354}
1355
1356
1358{
1359 SCH_SHEET_LIST retval;
1360
1361 for( const SCH_SHEET_PATH& sheetpath : *this )
1362 {
1363 if( sheetpath.LastScreen() == aScreen )
1364 retval.push_back( sheetpath );
1365 }
1366
1367 return retval;
1368}
1369
1370
1372 const std::vector<SCH_SYMBOL_INSTANCE>& aSymbolInstances )
1373{
1374 for( SCH_SHEET_PATH& sheetPath : *this )
1375 {
1376 for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1377 {
1378 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1379
1380 wxCHECK2( symbol, continue );
1381
1382 KIID_PATH sheetPathWithSymbolUuid = sheetPath.Path();
1383 sheetPathWithSymbolUuid.push_back( symbol->m_Uuid );
1384
1385 auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
1386 [ sheetPathWithSymbolUuid ]( const SCH_SYMBOL_INSTANCE& r ) -> bool
1387 {
1388 return sheetPathWithSymbolUuid == r.m_Path;
1389 } );
1390
1391 if( it == aSymbolInstances.end() )
1392 {
1393 wxLogTrace( traceSchSheetPaths, "No symbol instance found for symbol '%s'",
1394 sheetPathWithSymbolUuid.AsString() );
1395 continue;
1396 }
1397
1398 // Symbol instance paths are stored and looked up in memory with the root path so use
1399 // the full path here.
1400 symbol->AddHierarchicalReference( sheetPath.Path(), it->m_Reference, it->m_Unit );
1401 symbol->GetField( FIELD_T::REFERENCE )->SetText( it->m_Reference );
1402
1403 if( !it->m_Value.IsEmpty() )
1404 symbol->SetValueFieldText( it->m_Value );
1405
1406 if( !it->m_Footprint.IsEmpty() )
1407 symbol->SetFootprintFieldText( it->m_Footprint );
1408
1409 symbol->UpdatePrefix();
1410 }
1411 }
1412}
1413
1414
1415void SCH_SHEET_LIST::UpdateSheetInstanceData( const std::vector<SCH_SHEET_INSTANCE>& aSheetInstances )
1416{
1417
1418 for( SCH_SHEET_PATH& path : *this )
1419 {
1420 SCH_SHEET* sheet = path.Last();
1421
1422 wxCHECK2( sheet && path.Last(), continue );
1423
1424 auto it = std::find_if( aSheetInstances.begin(), aSheetInstances.end(),
1425 [&path]( const SCH_SHEET_INSTANCE& r ) -> bool
1426 {
1427 return path.Path() == r.m_Path;
1428 } );
1429
1430 if( it == aSheetInstances.end() )
1431 {
1432 wxLogTrace( traceSchSheetPaths, "No sheet instance found for path '%s'",
1433 path.Path().AsString() );
1434 continue;
1435 }
1436
1437 wxLogTrace( traceSchSheetPaths, "Setting sheet '%s' instance '%s' page number '%s'",
1438 ( sheet->GetName().IsEmpty() ) ? wxString( wxT( "root" ) ) : sheet->GetName(),
1439 path.Path().AsString(), it->m_PageNumber );
1440 path.SetPageNumber( it->m_PageNumber );
1441 }
1442}
1443
1444
1445std::vector<KIID_PATH> SCH_SHEET_LIST::GetPaths() const
1446{
1447 std::vector<KIID_PATH> paths;
1448
1449 for( const SCH_SHEET_PATH& sheetPath : *this )
1450 paths.emplace_back( sheetPath.Path() );
1451
1452 return paths;
1453}
1454
1455
1456std::vector<SCH_SHEET_INSTANCE> SCH_SHEET_LIST::GetSheetInstances() const
1457{
1458 std::vector<SCH_SHEET_INSTANCE> retval;
1459
1460 for( const SCH_SHEET_PATH& path : *this )
1461 {
1462 const SCH_SHEET* sheet = path.Last();
1463
1464 wxCHECK2( sheet, continue );
1465
1466 SCH_SHEET_INSTANCE instance;
1467 SCH_SHEET_PATH tmpPath = path;
1468
1469 tmpPath.pop_back();
1470 instance.m_Path = tmpPath.Path();
1471 instance.m_PageNumber = path.GetPageNumber();
1472
1473 retval.push_back( std::move( instance ) );
1474 }
1475
1476 return retval;
1477}
1478
1479
1481{
1482 for( const SCH_SHEET_PATH& instance : *this )
1483 {
1484 if( !instance.GetPageNumber().IsEmpty() )
1485 return false;
1486 }
1487
1488 return true;
1489}
1490
1491
1493{
1494 // Don't accidentally renumber existing sheets.
1495 wxCHECK( AllSheetPageNumbersEmpty(), /* void */ );
1496
1497 wxString tmp;
1498 int pageNumber = 1;
1499
1500 for( SCH_SHEET_PATH& instance : *this )
1501 {
1502 if( instance.Last()->IsVirtualRootSheet() )
1503 continue;
1504
1505 tmp.Printf( "%d", pageNumber );
1506 instance.SetPageNumber( tmp );
1507 pageNumber += 1;
1508 }
1509}
1510
1511
1513 const wxString& aProjectName )
1514{
1515 for( SCH_SHEET_PATH& sheetPath : *this )
1516 sheetPath.AddNewSymbolInstances( aPrefixSheetPath, aProjectName );
1517}
1518
1519
1521{
1522 for( SCH_SHEET_PATH& sheetPath : *this )
1523 sheetPath.RemoveSymbolInstances( aPrefixSheetPath );
1524}
1525
1526
1528 int aLastVirtualPageNumber )
1529{
1530 wxString pageNumber;
1531 int lastUsedPageNumber = 1;
1532 int nextVirtualPageNumber = aLastVirtualPageNumber;
1533
1534 // Fetch the list of page numbers already in use.
1535 std::vector< wxString > usedPageNumbers;
1536
1537 if( aPrefixSheetPath.size() )
1538 {
1539 SCH_SHEET_LIST prefixHierarchy( aPrefixSheetPath.at( 0 ) );
1540
1541 for( const SCH_SHEET_PATH& path : prefixHierarchy )
1542 {
1543 pageNumber = path.GetPageNumber();
1544
1545 if( !pageNumber.IsEmpty() )
1546 usedPageNumbers.emplace_back( pageNumber );
1547 }
1548 }
1549
1550 for( SCH_SHEET_PATH& sheetPath : *this )
1551 {
1552 KIID_PATH tmp = sheetPath.Path();
1553 SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
1554
1555 // Prefix the new hierarchical path.
1556 newSheetPath = newSheetPath + sheetPath;
1557
1558 // Sheets cannot have themselves in the path.
1559 tmp.pop_back();
1560
1561 SCH_SHEET* sheet = sheetPath.Last();
1562
1563 wxCHECK2( sheet, continue );
1564
1565 nextVirtualPageNumber += 1;
1566
1567 SCH_SHEET_INSTANCE instance;
1568
1569 // Add the instance if it doesn't already exist
1570 if( !sheet->getInstance( instance, tmp, true ) )
1571 {
1572 sheet->addInstance( tmp );
1573 sheet->getInstance( instance, tmp, true );
1574 }
1575
1576 // Get a new page number if we don't have one
1577 if( instance.m_PageNumber.IsEmpty() )
1578 {
1579 // Generate the next available page number.
1580 do
1581 {
1582 pageNumber.Printf( wxT( "%d" ), lastUsedPageNumber );
1583 lastUsedPageNumber += 1;
1584 } while( std::find( usedPageNumbers.begin(), usedPageNumbers.end(), pageNumber ) !=
1585 usedPageNumbers.end() );
1586
1587 instance.m_PageNumber = pageNumber;
1588 newSheetPath.SetVirtualPageNumber( nextVirtualPageNumber );
1589 }
1590
1591 newSheetPath.SetPageNumber( instance.m_PageNumber );
1592 usedPageNumbers.push_back( instance.m_PageNumber );
1593 }
1594}
1595
1596
1597void SCH_SHEET_LIST::CheckForMissingSymbolInstances( const wxString& aProjectName )
1598{
1599 wxLogTrace( traceSchSheetPaths,
1600 "SCH_SHEET_LIST::CheckForMissingSymbolInstances: Processing %zu sheet paths",
1601 size() );
1602
1603 for( SCH_SHEET_PATH& sheetPath : *this )
1604 {
1605 wxLogTrace( traceSchSheetPaths,
1606 " Processing sheet path: '%s' (size=%zu, KIID_PATH='%s')",
1607 sheetPath.PathHumanReadable( false ),
1608 sheetPath.size(),
1609 sheetPath.Path().AsString() );
1610 sheetPath.CheckForMissingSymbolInstances( aProjectName );
1611 }
1612}
1613
1614
1616{
1617 int lastVirtualPageNumber = 1;
1618
1619 for( const SCH_SHEET_PATH& sheetPath : *this )
1620 {
1621 if( sheetPath.GetVirtualPageNumber() > lastVirtualPageNumber )
1622 lastVirtualPageNumber = sheetPath.GetVirtualPageNumber();
1623 }
1624
1625 return lastVirtualPageNumber;
1626}
1627
1628
1629bool SCH_SHEET_LIST::HasPath( const KIID_PATH& aPath ) const
1630{
1631 for( const SCH_SHEET_PATH& path : *this )
1632 {
1633 if( path.Path() == aPath )
1634 return true;
1635 }
1636
1637 return false;
1638}
1639
1640
1641bool SCH_SHEET_LIST::ContainsSheet( const SCH_SHEET* aSheet ) const
1642{
1643 for( const SCH_SHEET_PATH& path : *this )
1644 {
1645 for( size_t i = 0; i < path.size(); i++ )
1646 {
1647 if( path.at( i ) == aSheet )
1648 return true;
1649 }
1650 }
1651
1652 return false;
1653}
1654
1655
1656std::optional<SCH_SHEET_PATH> SCH_SHEET_LIST::GetOrdinalPath( const SCH_SCREEN* aScreen ) const
1657{
1658 // Sheet paths with sheets that do not have a screen object are not valid.
1659 if( !aScreen )
1660 return std::nullopt;
1661
1662 for( const SCH_SHEET_PATH& path: *this )
1663 {
1664 if( path.LastScreen() == aScreen )
1665 return std::optional<SCH_SHEET_PATH>( path );
1666 }
1667
1668 return std::nullopt;
1669}
bool IsContentModified() const
Definition base_screen.h:60
void SetContentModified(bool aModified=true)
Definition base_screen.h:59
wxString GetClass() const override
Return the class name.
void SetPosition(const VECTOR2I &) override
static DELETED_SHEET_ITEM * GetInstance()
double Similarity(const SCH_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
void Rotate(const VECTOR2I &aCenter, bool aRotateCCW) override
Rotate the item around aCenter 90 degrees in the clockwise direction.
void Move(const VECTOR2I &aMoveVector) override
Move the item by aMoveVector to a new position.
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
bool operator==(const SCH_ITEM &aOther) const override
void MirrorVertically(int aCenter) override
Mirror item vertically about aCenter.
void MirrorHorizontally(int aCenter) override
Mirror item horizontally about aCenter.
const KIID m_Uuid
Definition eda_item.h:516
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual void UpdateHatching() const
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
virtual bool IsVisible() const
Definition eda_text.h:187
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:397
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
wxString AsString() const
Definition kiid.cpp:356
Definition kiid.h:49
wxString AsString() const
Definition kiid.cpp:246
Define a library symbol object.
Definition lib_symbol.h:85
bool IsPower() const override
int GetUnitCount() const override
Holds all the data relating to one schematic.
Definition schematic.h:88
wxString GetFileName() const
Helper to retrieve the filename from the root sheet screen.
SCHEMATIC_SETTINGS & Settings() const
wxString GetCurrentVariant() const
Return the current variant being edited.
VECTOR2I GetPosition() const override
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
void SetText(const wxString &aText) override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:611
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:244
int GetUnit() const
Definition sch_item.h:238
virtual void SetUnit(int aUnit)
Definition sch_item.h:237
SCH_ITEM(EDA_ITEM *aParent, KICAD_T aType, int aUnit=0, int aBodyStyle=0)
Definition sch_item.cpp:51
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:212
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
void SplitReferences()
Attempt to split all reference designators into a name (U) and number (1).
void AddItem(const SCH_REFERENCE &aItem)
A helper to define a symbol's reference designator in a schematic.
void SetRef(const wxString &aReference)
void SetRefNum(int aNum)
wxString GetRef() const
void SetSheetNumber(int aSheetNumber)
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:117
const wxString & GetFileName() const
Definition sch_screen.h:152
SCHEMATIC * Schematic() const
void Update(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Update aItem's bounding box in the tree.
void GetSheets(std::vector< SCH_ITEM * > *aItems) const
Similar to Items().OfType( SCH_SHEET_T ), but return the sheets in a deterministic order (L-R,...
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 GetMultiUnitSymbols(SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, bool aIncludePowerSymbols=true) const
Add a SCH_REFERENCE_LIST object to aRefList for each same-reference set of multi-unit parts in the li...
std::optional< SCH_SHEET_PATH > GetOrdinalPath(const SCH_SCREEN *aScreen) const
Return the ordinal sheet path of aScreen.
void FillItemMap(std::map< KIID, EDA_ITEM * > &aMap)
Fill an item cache for temporary use when many items need to be fetched.
SCH_SHEET_PATH m_currentSheetPath
void TrimToPageNumbers(const std::vector< wxString > &aPageInclusions)
Truncates the list by removing sheet's with page numbers not in the given list.
void SortByPageNumbers(bool aUpdateVirtualPageNums=true)
Sort the list of sheets by page number.
void AddNewSymbolInstances(const SCH_SHEET_PATH &aPrefixSheetPath, const wxString &aProjectName)
Attempt to add new symbol instances for all symbols in this list of sheet paths prefixed with aPrefix...
bool NameExists(const wxString &aSheetName) const
std::vector< SCH_SHEET_INSTANCE > GetSheetInstances() const
Fetch the instance information for all of the sheets in the hierarchy.
void UpdateSheetInstanceData(const std::vector< SCH_SHEET_INSTANCE > &aSheetInstances)
Update all of the sheet instance information using aSheetInstances.
void SetInitialPageNumbers()
Set initial sheet page numbers.
void RemoveSymbolInstances(const SCH_SHEET_PATH &aPrefixSheetPath)
SCH_SHEET_LIST FindAllSheetsForScreen(const SCH_SCREEN *aScreen) const
Return a SCH_SHEET_LIST with a copy of all the SCH_SHEET_PATH using a particular screen.
bool AllSheetPageNumbersEmpty() const
Check all of the sheet instance for empty page numbers.
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.
bool IsModified() const
Check the entire hierarchy for any modifications.
SCH_SHEET_LIST(SCH_SHEET *aSheet=nullptr)
Construct a flattened list of SCH_SHEET_PATH objects from aSheet.
void AnnotatePowerSymbols()
Silently annotate the not yet annotated power symbols of the entire hierarchy of the sheet path list.
int GetLastVirtualPageNumber() const
void UpdateSymbolInstanceData(const std::vector< SCH_SYMBOL_INSTANCE > &aSymbolInstances)
Update all of the symbol instance information using aSymbolInstances.
void GetSheetsWithinPath(std::vector< SCH_SHEET_PATH > &aSheets, const SCH_SHEET_PATH &aSheetPath) const
Add a SCH_SHEET_PATH object to aSheets for each sheet in the list that are contained within aSheetPat...
bool PageNumberExists(const wxString &aPageNumber) const
void SortByHierarchicalPageNumbers(bool aUpdateVirtualPageNums=true)
This works like SortByPageNumbers, but it sorts the sheets first by their hierarchical depth and then...
void AddNewSheetInstances(const SCH_SHEET_PATH &aPrefixSheetPath, int aLastVirtualPageNumber)
bool ContainsSheet(const SCH_SHEET *aSheet) const
std::vector< KIID_PATH > GetPaths() const
void GetSymbolsWithinPath(SCH_REFERENCE_LIST &aReferences, const SCH_SHEET_PATH &aSheetPath, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets that are contained wi...
void BuildSheetList(SCH_SHEET *aSheet, bool aCheckIntegrity)
Build the list of sheets and their sheet path from aSheet.
SCH_SHEET_PATH FindSheetForScreen(const SCH_SCREEN *aScreen)
Return the first SCH_SHEET_PATH object (not necessarily the only one) using a particular screen.
void CheckForMissingSymbolInstances(const wxString &aProjectName)
bool HasPath(const KIID_PATH &aPath) const
SCH_SHEET_PATH * FindSheetForPath(const SCH_SHEET_PATH *aPath)
Return a pointer to the first SCH_SHEET_PATH object (not necessarily the only one) matching the provi...
bool TestForRecursion(const SCH_SHEET_LIST &aSrcSheetHierarchy, const wxString &aDestFileName)
Test every SCH_SHEET_PATH in this SCH_SHEET_LIST to verify if adding the sheets stored in aSrcSheetHi...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
bool GetExcludedFromBOM() const
void AppendMultiUnitSymbol(SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, SCH_SYMBOL *aSymbol, bool aIncludePowerSymbols=true) const
Append a SCH_REFERENCE_LIST object to aRefList based on aSymbol, storing same-reference set of multi-...
const SCH_SHEET * GetSheet(unsigned aIndex) const
bool empty() const
Forwarded method from std::vector.
void GetSymbols(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Adds SCH_REFERENCE object to aReferences for each symbol in the sheet.
int ComparePageNum(const SCH_SHEET_PATH &aSheetPathToTest) const
Compare sheets by their page number.
size_t GetCurrentHash() const
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false) const
Return the sheet path in a human readable form made from the sheet names.
bool operator==(const SCH_SHEET_PATH &d1) const
void AddNewSymbolInstances(const SCH_SHEET_PATH &aPrefixSheetPath, const wxString &aProjectName)
Attempt to add new symbol instances for all symbols in this sheet path prefixed with aPrefixSheetPath...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
bool TestForRecursion(const wxString &aSrcFileName, const wxString &aDestFileName)
Test the SCH_SHEET_PATH file names to check adding the sheet stored in the file aSrcFileName to the s...
void UpdateAllScreenReferences() const
Update all the symbol references for this sheet path.
void MakeFilePathRelativeToParentSheet()
Make the sheet file name relative to its parent sheet.
SCH_ITEM * ResolveItem(const KIID &aID) const
Fetch a SCH_ITEM by ID.
wxString GetCachedPageNumber() const
std::vector< SCH_SHEET * > m_sheets
SCH_SCREEN * LastScreen()
int Cmp(const SCH_SHEET_PATH &aSheetPathToTest) const
Compare if this is the same sheet path as aSheetPathToTest.
void GetMultiUnitSymbols(SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, bool aIncludePowerSymbols=true) const
Add a SCH_REFERENCE_LIST object to aRefList for each same-reference set of multi-unit parts in the sh...
void initFromOther(const SCH_SHEET_PATH &aOther)
wxString m_cached_page_number
wxString GetPageNumber() const
void RemoveSymbolInstances(const SCH_SHEET_PATH &aPrefixSheetPath)
void CheckForMissingSymbolInstances(const wxString &aProjectName)
bool IsContainedWithin(const SCH_SHEET_PATH &aSheetPathToTest) const
Check if this path is contained inside aSheetPathToTest.
SCH_SHEET * at(size_t aIndex) const
Forwarded method from std::vector.
void SetVirtualPageNumber(int aPageNumber)
Set the sheet instance virtual page number.
void AppendSymbol(SCH_REFERENCE_LIST &aReferences, SCH_SYMBOL *aSymbol, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Append a SCH_REFERENCE object to aReferences based on aSymbol.
std::map< std::pair< wxString, wxString >, bool > m_recursion_test_cache
bool GetExcludedFromSim() const
wxString PathAsString() const
Return the path of time stamps which do not changes even when editing sheet parameters.
bool GetExcludedFromBoard() const
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
SCH_SHEET_PATH & operator=(const SCH_SHEET_PATH &aOther)
int GetPageNumberAsInt() const
bool GetDNP() const
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
SCH_SHEET_PATH operator+(const SCH_SHEET_PATH &aOther)
int m_virtualPageNumber
Page numbers are maintained by the sheet load order.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
int GetVirtualPageNumber() const
void pop_back()
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
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:347
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:341
bool getInstance(SCH_SHEET_INSTANCE &aInstance, const KIID_PATH &aSheetPath, bool aTestFromEnd=false) const
bool addInstance(const KIID_PATH &aInstance)
Add a new instance aSheetPath to the instance list.
wxString getPageNumber(const KIID_PATH &aParentPath) const
Return the sheet page number for aParentPath.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
wxString GetName() const
Definition sch_sheet.h:113
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:116
static int ComparePageNum(const wxString &aPageNumberA, const wxString &aPageNumberB)
Compare page numbers of schematic sheets.
void setPageNumber(const KIID_PATH &aInstance, const wxString &aPageNumber)
Set the page number for the sheet instance aInstance.
bool IsVirtualRootSheet() const
void InitializeAttributes(const SCH_SYMBOL &aSymbol)
Schematic symbol object.
Definition sch_symbol.h:76
void UpdatePrefix()
Set the prefix based on the current reference designator.
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:135
void RemoveInstance(const SCH_SHEET_PATH &aInstancePath)
void SetValueFieldText(const wxString &aValue)
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
void SetFootprintFieldText(const wxString &aFootprint)
void AddHierarchicalReference(const KIID_PATH &aPath, const wxString &aRef, int aUnit)
Add a full hierarchical reference to this symbol.
bool GetInstance(SCH_SYMBOL_INSTANCE &aInstance, const KIID_PATH &aSheetPath, bool aTestFromEnd=false) const
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:184
virtual bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
wxString GetPrefix() const
Definition sch_symbol.h:238
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
bool m_ExcludedFromBOM
bool m_ExcludedFromSim
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
@ NO_RECURSE
Definition eda_item.h:52
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug output.
const wxChar *const traceSchSheetPaths
Flag to enable debug output of schematic symbol sheet path manipulation code.
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
Definition hash.h:32
KIID niluuid(0)
wxString GetRefDesUnannotated(const wxString &aSource)
Return an unannotated refdes from either a prefix or an existing refdes.
STL namespace.
Collection of utility functions for component reference designators (refdes)
@ AUTOPLACE_AUTO
Definition sch_item.h:70
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::map< wxString, SCH_REFERENCE_LIST > SCH_MULTI_UNIT_REFERENCE_MAP
Container to map reference designators for multi-unit parts.
A simple container for sheet instance information.
A simple container for schematic symbol instance information.
size_t operator()(const SCH_SHEET_PATH &path) const
@ INTERSHEET_REFS
Global label cross-reference page numbers.
@ REFERENCE
Field Reference of part, i.e. "IC21".
VECTOR2I end
wxLogTrace helper definitions.
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_SHAPE_T
Definition typeinfo.h:153
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:79
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:172
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695