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
106
107
109{
110 m_DNP = aSheet.GetDNP();
114 m_ExcludedFromPosFiles = false; // Sheets don't have position files exclusion
115}
116
117
118namespace std
119{
121 {
122 return path.GetCurrentHash();
123 }
124}
125
126
132
133
135{
136 initFromOther( aOther );
137}
138
139
141{
142 initFromOther( aOther );
143 return *this;
144}
145
146
147// Move assignment operator
149{
150 m_sheets = std::move( aOther.m_sheets );
151
152 m_virtualPageNumber = aOther.m_virtualPageNumber;
153 m_current_hash = aOther.m_current_hash;
154 m_cached_page_number = aOther.m_cached_page_number;
155
156 m_recursion_test_cache = std::move( aOther.m_recursion_test_cache );
157
158 return *this;
159}
160
161
163{
164 SCH_SHEET_PATH retv = *this;
165
166 size_t size = aOther.size();
167
168 for( size_t i = 0; i < size; i++ )
169 retv.push_back( aOther.at( i ) );
170
171 return retv;
172}
173
174
176{
177 m_sheets = aOther.m_sheets;
181
182 // Note: don't copy m_recursion_test_cache as it is slow and we want std::vector<SCH_SHEET_PATH>
183 // to be very fast to construct for use in the connectivity algorithm.
185}
186
188{
189 m_current_hash = 0;
190
191 for( SCH_SHEET* sheet : m_sheets )
192 hash_combine( m_current_hash, sheet->m_Uuid.Hash() );
193}
194
195
196int SCH_SHEET_PATH::Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const
197{
198 if( size() > aSheetPathToTest.size() )
199 return 1;
200
201 if( size() < aSheetPathToTest.size() )
202 return -1;
203
204 // otherwise, same number of sheets.
205 for( unsigned i = 0; i < size(); i++ )
206 {
207 if( at( i )->m_Uuid < aSheetPathToTest.at( i )->m_Uuid )
208 return -1;
209
210 if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
211 return 1;
212 }
213
214 return 0;
215}
216
217
218int SCH_SHEET_PATH::ComparePageNum( const SCH_SHEET_PATH& aSheetPathToTest ) const
219{
220 wxString pageA = this->GetPageNumber();
221 wxString pageB = aSheetPathToTest.GetPageNumber();
222
223 int pageNumComp = SCH_SHEET::ComparePageNum( pageA, pageB );
224
225 if( pageNumComp == 0 )
226 {
227 int virtualPageA = GetVirtualPageNumber();
228 int virtualPageB = aSheetPathToTest.GetVirtualPageNumber();
229
230 if( virtualPageA > virtualPageB )
231 pageNumComp = 1;
232 else if( virtualPageA < virtualPageB )
233 pageNumComp = -1;
234 }
235
236 return pageNumComp;
237}
238
239
240bool SCH_SHEET_PATH::IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const
241{
242 if( aSheetPathToTest.size() > size() )
243 return false;
244
245 for( size_t i = 0; i < aSheetPathToTest.size(); ++i )
246 {
247 if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
248 {
249 wxLogTrace( traceSchSheetPaths, "Sheet path '%s' is not within path '%s'.",
250 aSheetPathToTest.Path().AsString(), Path().AsString() );
251
252 return false;
253 }
254 }
255
256 wxLogTrace( traceSchSheetPaths, "Sheet path '%s' is within path '%s'.",
257 aSheetPathToTest.Path().AsString(), Path().AsString() );
258
259 return true;
260}
261
262
264{
265 if( !empty() )
266 return m_sheets.back();
267
268 return nullptr;
269}
270
271
273{
274 SCH_SHEET* lastSheet = Last();
275
276 if( lastSheet )
277 return lastSheet->GetScreen();
278
279 return nullptr;
280}
281
282
284{
285 SCH_SHEET* lastSheet = Last();
286
287 if( lastSheet )
288 return lastSheet->GetScreen();
289
290 return nullptr;
291}
292
293
295{
296 for( SCH_SHEET* sheet : m_sheets )
297 {
298 if( sheet->GetExcludedFromSim() )
299 return true;
300 }
301
302 return false;
303}
304
305
306bool SCH_SHEET_PATH::GetExcludedFromSim( const wxString& aVariantName ) const
307{
308 if( aVariantName.IsEmpty() )
309 return GetExcludedFromSim();
310
311 for( SCH_SHEET* sheet : m_sheets )
312 {
313 if( sheet->GetExcludedFromSim( this, aVariantName ) )
314 return true;
315 }
316
317 return false;
318}
319
320
322{
323 for( SCH_SHEET* sheet : m_sheets )
324 {
325 if( sheet->GetExcludedFromBOM() )
326 return true;
327 }
328
329 return false;
330}
331
332
333bool SCH_SHEET_PATH::GetExcludedFromBOM( const wxString& aVariantName ) const
334{
335 if( aVariantName.IsEmpty() )
336 return GetExcludedFromBOM();
337
338 for( SCH_SHEET* sheet : m_sheets )
339 {
340 if( sheet->GetExcludedFromBOM( this, aVariantName ) )
341 return true;
342 }
343
344 return false;
345}
346
347
349{
350 for( SCH_SHEET* sheet : m_sheets )
351 {
352 if( sheet->GetExcludedFromBoard() )
353 return true;
354 }
355
356 return false;
357}
358
359
361{
362 for( SCH_SHEET* sheet : m_sheets )
363 {
364 if( sheet->GetDNP() )
365 return true;
366 }
367
368 return false;
369}
370
371
372bool SCH_SHEET_PATH::GetDNP( const wxString& aVariantName ) const
373{
374 if( aVariantName.IsEmpty() )
375 return GetDNP();
376
377 for( SCH_SHEET* sheet : m_sheets )
378 {
379 if( sheet->GetDNP( this, aVariantName ) )
380 return true;
381 }
382
383 return false;
384}
385
386
388{
389 wxString s;
390
391 s = wxT( "/" ); // This is the root path
392
393 // Start at 1 to avoid the root sheet, which does not need to be added to the path.
394 // Its timestamp changes anyway.
395 for( unsigned i = 1; i < size(); i++ )
396 s += at( i )->m_Uuid.AsString() + "/";
397
398 return s;
399}
400
401
403{
405 size_t size = m_sheets.size();
406
407 if( m_sheets.empty() )
408 return path;
409
410 if( m_sheets[0]->m_Uuid != niluuid )
411 {
412 path.reserve( size );
413 path.push_back( m_sheets[0]->m_Uuid );
414 }
415 else
416 {
417 // Skip the virtual root
418 path.reserve( size - 1 );
419 }
420
421 for( size_t i = 1; i < size; i++ )
422 path.push_back( m_sheets[i]->m_Uuid );
423
424 return path;
425}
426
427
428wxString SCH_SHEET_PATH::PathHumanReadable( bool aUseShortRootName,
429 bool aStripTrailingSeparator ) const
430{
431 wxString s;
432
433 // Determine the starting index - skip virtual root if present
434 size_t startIdx = 0;
435
436 if( !empty() && at( 0 )->IsVirtualRootSheet() )
437 startIdx = 1;
438
439 if( aUseShortRootName )
440 {
441 s = wxS( "/" ); // Use only the short name in netlists
442 }
443 else
444 {
445 wxString fileName;
446
447 if( size() > startIdx && at( startIdx )->GetScreen() )
448 fileName = at( startIdx )->GetScreen()->GetFileName();
449
450 wxFileName fn = fileName;
451
452 s = fn.GetName() + wxS( "/" );
453 }
454
455 // Start at startIdx + 1 since we've already processed the root sheet.
456 for( unsigned i = startIdx + 1; i < size(); i++ )
457 s << at( i )->GetField( FIELD_T::SHEET_NAME )->GetShownText( false ) << wxS( "/" );
458
459 if( aStripTrailingSeparator && s.EndsWith( "/" ) )
460 s = s.Left( s.length() - 1 );
461
462 return s;
463}
464
465
467{
468 std::vector<SCH_ITEM*> items;
469
470 std::copy_if( LastScreen()->Items().begin(), LastScreen()->Items().end(),
471 std::back_inserter( items ),
472 []( SCH_ITEM* aItem )
473 {
474 return ( aItem->Type() == SCH_SYMBOL_T
475 || aItem->Type() == SCH_GLOBAL_LABEL_T
476 || aItem->Type() == SCH_SHAPE_T );
477 } );
478
479 std::optional<wxString> variantName;
480 const SCHEMATIC* schematic = LastScreen()->Schematic();
481
482 if( schematic )
483 variantName = schematic->GetCurrentVariant();
484
485 for( SCH_ITEM* item : items )
486 {
487 if( item->Type() == SCH_SYMBOL_T )
488 {
489 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
490
491 symbol->GetField( FIELD_T::REFERENCE )->SetText( symbol->GetRef( this ) );
492 symbol->SetUnit( symbol->GetUnitSelection( this ) );
493 LastScreen()->Update( item, false );
494 }
495 else if( item->Type() == SCH_GLOBAL_LABEL_T )
496 {
497 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( item );
498
499 if( label->GetFields().size() > 0 ) // Possible when reading a legacy .sch schematic
500 {
501 SCH_FIELD* intersheetRefs = label->GetField( FIELD_T::INTERSHEET_REFS );
502
503 // Fixup for legacy files which didn't store a position for the intersheet refs
504 // unless they were shown.
505 if( intersheetRefs->GetPosition() == VECTOR2I() && !intersheetRefs->IsVisible() )
507
508 intersheetRefs->SetVisible( label->Schematic()->Settings().m_IntersheetRefsShow );
509 LastScreen()->Update( intersheetRefs );
510 }
511 }
512 else if( item->Type() == SCH_SHAPE_T )
513 {
514 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
515 shape->UpdateHatching();
516 }
517 }
518}
519
520
521void SCH_SHEET_PATH::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
522 bool aForceIncludeOrphanSymbols ) const
523{
524 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
525 {
526 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
527 AppendSymbol( aReferences, symbol, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
528 }
529}
530
531
533 bool aIncludePowerSymbols,
534 bool aForceIncludeOrphanSymbols ) const
535{
536 // Skip pseudo-symbols, which have a reference starting with #. This mainly
537 // affects power symbols.
538 if( aIncludePowerSymbols || aSymbol->GetRef( this )[0] != wxT( '#' ) )
539 {
540 if( aSymbol->GetLibSymbolRef() || aForceIncludeOrphanSymbols )
541 {
542 SCH_REFERENCE schReference( aSymbol, *this );
543
544 schReference.SetSheetNumber( GetPageNumberAsInt() );
545 aReferences.AddItem( schReference );
546 }
547 }
548}
549
550
552 bool aIncludePowerSymbols ) const
553{
554 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
555 {
556 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
557 AppendMultiUnitSymbol( aRefList, symbol, aIncludePowerSymbols );
558 }
559}
560
561
563 SCH_SYMBOL* aSymbol,
564 bool aIncludePowerSymbols ) const
565{
566 // Skip pseudo-symbols, which have a reference starting with #. This mainly
567 // affects power symbols.
568 if( !aIncludePowerSymbols && aSymbol->GetRef( this )[0] == wxT( '#' ) )
569 return;
570
571 LIB_SYMBOL* symbol = aSymbol->GetLibSymbolRef().get();
572
573 if( symbol && symbol->GetUnitCount() > 1 )
574 {
575 SCH_REFERENCE schReference = SCH_REFERENCE( aSymbol, *this );
576 schReference.SetSheetNumber( GetPageNumberAsInt() );
577 wxString reference_str = schReference.GetRef();
578
579 // Never lock unassigned references
580 if( reference_str[reference_str.Len() - 1] == '?' )
581 return;
582
583 aRefList[reference_str].AddItem( schReference );
584 }
585}
586
587
589{
590 return m_current_hash == d1.GetCurrentHash();
591}
592
593
594bool SCH_SHEET_PATH::TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName )
595{
596 auto pair = std::make_pair( aSrcFileName, aDestFileName );
597
598 if( m_recursion_test_cache.count( pair ) )
599 return m_recursion_test_cache.at( pair );
600
601 SCHEMATIC* sch = LastScreen()->Schematic();
602
603 wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_PATH::TestForRecursion!" );
604
605 wxFileName rootFn = sch->GetFileName();
606 wxFileName srcFn = aSrcFileName;
607 wxFileName destFn = aDestFileName;
608
609 if( srcFn.IsRelative() )
610 srcFn.MakeAbsolute( rootFn.GetPath() );
611
612 if( destFn.IsRelative() )
613 destFn.MakeAbsolute( rootFn.GetPath() );
614
615 // The source and destination sheet file names cannot be the same.
616 if( srcFn == destFn )
617 {
618 m_recursion_test_cache[pair] = true;
619 return true;
620 }
621
625 unsigned i = 0;
626
627 while( i < size() )
628 {
629 wxFileName cmpFn = at( i )->GetFileName();
630
631 if( cmpFn.IsRelative() )
632 cmpFn.MakeAbsolute( rootFn.GetPath() );
633
634 // Test if the file name of the destination sheet is in anywhere in this sheet path.
635 if( cmpFn == destFn )
636 break;
637
638 i++;
639 }
640
641 // The destination sheet file name was not found in the sheet path or the destination
642 // sheet file name is the root sheet so no recursion is possible.
643 if( i >= size() || i == 0 )
644 {
645 m_recursion_test_cache[pair] = false;
646 return false;
647 }
648
649 // Walk back up to the root sheet to see if the source file name is already a parent in
650 // the sheet path. If so, recursion will occur.
651 do
652 {
653 i -= 1;
654
655 wxFileName cmpFn = at( i )->GetFileName();
656
657 if( cmpFn.IsRelative() )
658 cmpFn.MakeAbsolute( rootFn.GetPath() );
659
660 if( cmpFn == srcFn )
661 {
662 m_recursion_test_cache[pair] = true;
663 return true;
664 }
665
666 } while( i != 0 );
667
668 // The source sheet file name is not a parent of the destination sheet file name.
669 m_recursion_test_cache[pair] = false;
670 return false;
671}
672
673
675{
676 SCH_SHEET* sheet = Last();
677
678 wxCHECK( sheet, wxEmptyString );
679
680 KIID_PATH tmpPath = Path();
681
682 if( !tmpPath.empty() )
683 tmpPath.pop_back();
684 else
685 wxFAIL_MSG( wxS( "Sheet paths must have a least one valid sheet." ) );
686
687 return sheet->getPageNumber( tmpPath );
688}
689
691{
692 long page;
693 wxString pageStr = GetPageNumber();
694
695 if( pageStr.ToLong( &page ) )
696 return (int) page;
697
698 return GetVirtualPageNumber();
699}
700
701
702void SCH_SHEET_PATH::SetPageNumber( const wxString& aPageNumber )
703{
704 SCH_SHEET* sheet = Last();
705
706 wxCHECK( sheet, /* void */ );
707
708 KIID_PATH tmpPath = Path();
709
710 if( !tmpPath.empty() )
711 {
712 tmpPath.pop_back();
713 }
714 else
715 {
716 wxCHECK_MSG( false, /* void */, wxS( "Sheet paths must have a least one valid sheet." ) );
717 }
718
719 sheet->addInstance( tmpPath );
720 sheet->setPageNumber( tmpPath, aPageNumber );
721}
722
723
725 const wxString& aProjectName )
726{
727 wxCHECK( !aProjectName.IsEmpty(), /* void */ );
728
729 SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
730 SCH_SHEET_PATH currentSheetPath( *this );
731
732 // Prefix the new hierarchical path.
733 newSheetPath = newSheetPath + currentSheetPath;
734
735 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
736 {
737 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
738
739 wxCHECK2( symbol, continue );
740
741 SCH_SYMBOL_INSTANCE newSymbolInstance;
742
743 if( symbol->GetInstance( newSymbolInstance, Path(), true ) )
744 {
745 newSymbolInstance.m_ProjectName = aProjectName;
746
747 // Use an existing symbol instance for this path if it exists.
748 newSymbolInstance.m_Path = newSheetPath.Path();
749 symbol->AddHierarchicalReference( newSymbolInstance );
750 }
751 else if( !symbol->GetInstances().empty() )
752 {
753 newSymbolInstance.m_ProjectName = aProjectName;
754
755 // Use the first symbol instance if any symbol instance data exists.
756 newSymbolInstance = symbol->GetInstances()[0];
757 newSymbolInstance.m_Path = newSheetPath.Path();
758 symbol->AddHierarchicalReference( newSymbolInstance );
759 }
760 else
761 {
762 newSymbolInstance.m_ProjectName = aProjectName;
763
764 // Fall back to the last saved symbol field and unit settings if there is no
765 // instance data.
766 newSymbolInstance.m_Path = newSheetPath.Path();
767 newSymbolInstance.m_Reference = symbol->GetField( FIELD_T::REFERENCE )->GetText();
768 newSymbolInstance.m_Unit = symbol->GetUnit();
769 symbol->AddHierarchicalReference( newSymbolInstance );
770 }
771 }
772}
773
774
776{
777 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
778 {
779 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
780
781 wxCHECK2( symbol, continue );
782
783 SCH_SHEET_PATH fullSheetPath( aPrefixSheetPath );
784 SCH_SHEET_PATH currentSheetPath( *this );
785
786 // Prefix the hierarchical path of the symbol instance to be removed.
787 fullSheetPath = fullSheetPath + currentSheetPath;
788 symbol->RemoveInstance( fullSheetPath );
789 }
790}
791
792
793void SCH_SHEET_PATH::CheckForMissingSymbolInstances( const wxString& aProjectName )
794{
795 // Skip sheet paths without screens (e.g., sheets that haven't been loaded yet or virtual root)
796 if( aProjectName.IsEmpty() || !LastScreen() )
797 return;
798
799 wxLogTrace( traceSchSheetPaths, "CheckForMissingSymbolInstances for path: %s (project: %s)",
800 PathHumanReadable( false ), aProjectName );
801 wxLogTrace( traceSchSheetPaths, " Sheet path size=%zu, Path().AsString()='%s'",
802 size(), Path().AsString() );
803
804 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
805 {
806 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
807
808 wxCHECK2( symbol, continue );
809
810 SCH_SYMBOL_INSTANCE symbolInstance;
811
812 if( !symbol->GetInstance( symbolInstance, Path() ) )
813 {
814 wxLogTrace( traceSchSheetPaths, "Adding missing symbol \"%s\" instance data for "
815 "sheet path '%s'.",
816 symbol->m_Uuid.AsString(), PathHumanReadable( false ) );
817
818 // Legacy schematics that are not shared do not contain separate instance data.
819 // The symbol reference and unit are saved in the reference field and unit entries.
820 if( ( LastScreen()->GetRefCount() <= 1 ) &&
821 ( LastScreen()->GetFileFormatVersionAtLoad() <= 20200310 ) )
822 {
823 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
824 symbolInstance.m_Reference = refField->GetShownText( this, true );
825 symbolInstance.m_Unit = symbol->GetUnit();
826
827 wxLogTrace( traceSchSheetPaths,
828 " Legacy format: Using reference '%s' from field, unit %d",
829 symbolInstance.m_Reference, symbolInstance.m_Unit );
830 }
831 else
832 {
833 // When schematics are shared, we cannot know which instance the current symbol
834 // reference field and unit belong to. In this case, we clear the reference
835 // annotation and set the unit to 1.
836 symbolInstance.m_Reference = UTIL::GetRefDesUnannotated( symbol->GetPrefix() );
837
838 wxLogTrace( traceSchSheetPaths,
839 " Shared schematic: Setting unannotated reference '%s'",
840 symbolInstance.m_Reference );
841 }
842
843 symbolInstance.m_ProjectName = aProjectName;
844 symbolInstance.m_Path = Path();
845 symbol->AddHierarchicalReference( symbolInstance );
846
847 wxLogTrace( traceSchSheetPaths,
848 " Created instance: ref=%s, path=%s",
849 symbolInstance.m_Reference, symbolInstance.m_Path.AsString() );
850 }
851 else
852 {
853 wxLogTrace( traceSchSheetPaths,
854 " Symbol %s already has instance: ref=%s, path=%s",
855 symbol->m_Uuid.AsString(),
856 symbolInstance.m_Reference,
857 symbolInstance.m_Path.AsString() );
858 }
859 }
860}
861
862
864{
865 wxCHECK( m_sheets.size() > 1, /* void */ );
866
867 wxFileName sheetFileName = Last()->GetFileName();
868
869 // If the sheet file name is absolute, then the user requested is so don't make it relative.
870 if( sheetFileName.IsAbsolute() )
871 return;
872
873 SCH_SCREEN* screen = LastScreen();
874 SCH_SCREEN* parentScreen = m_sheets[ m_sheets.size() - 2 ]->GetScreen();
875
876 wxCHECK( screen && parentScreen, /* void */ );
877
878 wxFileName fileName = screen->GetFileName();
879 wxFileName parentFileName = parentScreen->GetFileName();
880
881 // SCH_SCREEN file names must be absolute. If they are not, someone set them incorrectly
882 // on load or on creation.
883 wxCHECK( fileName.IsAbsolute() && parentFileName.IsAbsolute(), /* void */ );
884
885 if( fileName.GetPath() == parentFileName.GetPath() )
886 {
887 Last()->SetFileName( fileName.GetFullName() );
888 }
889 else if( fileName.MakeRelativeTo( parentFileName.GetPath() ) )
890 {
891 Last()->SetFileName( fileName.GetFullPath() );
892 }
893 else
894 {
895 Last()->SetFileName( screen->GetFileName() );
896 }
897
898 wxLogTrace( tracePathsAndFiles,
899 wxT( "\n File name: '%s'"
900 "\n parent file name '%s',"
901 "\n sheet '%s' file name '%s'." ),
902 screen->GetFileName(), parentScreen->GetFileName(), PathHumanReadable(),
903 Last()->GetFileName() );
904}
905
906
908{
909 if( aSheet != nullptr )
910 BuildSheetList( aSheet, false );
911}
912
913
914void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
915{
916 if( !aSheet )
917 return;
918
919 wxLogTrace( traceSchSheetPaths,
920 "BuildSheetList called with sheet '%s' (UUID=%s, isVirtualRoot=%d)",
921 aSheet->GetName(),
922 aSheet->m_Uuid.AsString(),
923 aSheet->m_Uuid == niluuid ? 1 : 0 );
924
925 // Special handling for virtual root: process its children without adding the root itself
926 if( aSheet->IsVirtualRootSheet() )
927 {
928 wxLogTrace( traceSchSheetPaths, " Skipping virtual root, processing children only" );
929
930 if( aSheet->GetScreen() )
931 {
932 std::vector<SCH_ITEM*> childSheets;
933 aSheet->GetScreen()->GetSheets( &childSheets );
934
935 for( SCH_ITEM* item : childSheets )
936 {
937 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
938 BuildSheetList( sheet, aCheckIntegrity );
939 }
940 }
941
942 return;
943 }
944
945 std::vector<SCH_SHEET*> badSheets;
946
947 m_currentSheetPath.push_back( aSheet );
948 m_currentSheetPath.SetVirtualPageNumber( static_cast<int>( size() ) + 1 );
949 push_back( m_currentSheetPath );
950
951 if( m_currentSheetPath.LastScreen() )
952 {
953 wxString parentFileName = aSheet->GetFileName();
954 std::vector<SCH_ITEM*> childSheets;
955 m_currentSheetPath.LastScreen()->GetSheets( &childSheets );
956
957 for( SCH_ITEM* item : childSheets )
958 {
959 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
960
961 if( aCheckIntegrity )
962 {
963 if( !m_currentSheetPath.TestForRecursion( sheet->GetFileName(), parentFileName ) )
964 BuildSheetList( sheet, true );
965 else
966 badSheets.push_back( sheet );
967 }
968 else
969 {
970 // If we are not performing a full recursion test, at least check if we are in
971 // a simple recursion scenario to prevent stack overflow crashes
972 wxCHECK2_MSG( sheet->GetFileName() != aSheet->GetFileName(), continue,
973 wxT( "Recursion prevented in SCH_SHEET_LIST::BuildSheetList" ) );
974
975 BuildSheetList( sheet, false );
976 }
977 }
978 }
979
980 if( aCheckIntegrity )
981 {
982 for( SCH_SHEET* sheet : badSheets )
983 {
984 m_currentSheetPath.LastScreen()->Remove( sheet );
985 m_currentSheetPath.LastScreen()->SetContentModified();
986 }
987 }
988
989 m_currentSheetPath.pop_back();
990}
991
992
993void SCH_SHEET_LIST::SortByHierarchicalPageNumbers( bool aUpdateVirtualPageNums )
994{
995 for( const SCH_SHEET_PATH& path : *this )
996 path.CachePageNumber();
997
998 std::sort( begin(), end(),
999 []( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b ) -> bool
1000 {
1001 // Find the divergence point in the paths
1002 size_t common_len = 0;
1003 size_t min_len = std::min( a.size(), b.size() );
1004
1005 while( common_len < min_len && a.at( common_len )->m_Uuid == b.at( common_len )->m_Uuid )
1006 common_len++;
1007
1008 // If one path is a prefix of the other, the shorter one comes first
1009 // This ensures parents come before children
1010 if( common_len == a.size() )
1011 return true; // a is a prefix of b - a is the parent
1012 if( common_len == b.size() )
1013 return false; // b is a prefix of a - b is the parent
1014
1015 // Paths diverge at common_len
1016 // If they share the same parent, sort by page number
1017 // This ensures siblings are sorted by page number
1018 SCH_SHEET* sheet_a = a.at( common_len );
1019 SCH_SHEET* sheet_b = b.at( common_len );
1020
1021 // Create partial paths to get to these sheets for page number comparison
1022 KIID_PATH ancestor;
1023 for( size_t i = 0; i < common_len; i++ )
1024 ancestor.push_back( a.at( i )->m_Uuid );
1025
1026 // Compare page numbers - use the last sheet's page number
1027 wxString page_a = sheet_a->getPageNumber( ancestor );
1028 wxString page_b = sheet_b->getPageNumber( ancestor );
1029
1030 int retval = SCH_SHEET::ComparePageNum( page_a, page_b );
1031
1032 if( retval != 0 )
1033 return retval < 0;
1034
1035 // If page numbers are the same, use virtual page numbers as a tie-breaker
1037 return true;
1038 else if( a.GetVirtualPageNumber() > b.GetVirtualPageNumber() )
1039 return false;
1040
1041 // Finally, use UUIDs for stable ordering when everything else is equal
1042 return a.GetCurrentHash() < b.GetCurrentHash();
1043 } );
1044
1045 if( aUpdateVirtualPageNums )
1046 {
1047 int virtualPageNum = 1;
1048
1049 for( SCH_SHEET_PATH& sheet : *this )
1050 sheet.SetVirtualPageNumber( virtualPageNum++ );
1051 }
1052}
1053
1054
1055void SCH_SHEET_LIST::SortByPageNumbers( bool aUpdateVirtualPageNums )
1056{
1057 for( const SCH_SHEET_PATH& path : *this )
1058 path.CachePageNumber();
1059
1060 std::sort( begin(), end(),
1061 []( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b ) -> bool
1062 {
1064 b.GetCachedPageNumber() );
1065
1066 if( retval < 0 )
1067 return true;
1068 else if( retval > 0 )
1069 return false;
1070
1072 return true;
1073 else if( a.GetVirtualPageNumber() > b.GetVirtualPageNumber() )
1074 return false;
1075
1076 // Enforce strict ordering. If the page numbers are the same, use UUIDs
1077 return a.GetCurrentHash() < b.GetCurrentHash();
1078 } );
1079
1080 if( aUpdateVirtualPageNums )
1081 {
1082 int virtualPageNum = 1;
1083
1084 for( SCH_SHEET_PATH& sheet : *this )
1085 sheet.SetVirtualPageNumber( virtualPageNum++ );
1086 }
1087}
1088
1089
1090bool SCH_SHEET_LIST::NameExists( const wxString& aSheetName ) const
1091{
1092 for( const SCH_SHEET_PATH& sheet : *this )
1093 {
1094 if( sheet.Last()->GetName() == aSheetName )
1095 return true;
1096 }
1097
1098 return false;
1099}
1100
1101
1102bool SCH_SHEET_LIST::PageNumberExists( const wxString& aPageNumber ) const
1103{
1104 for( const SCH_SHEET_PATH& sheet : *this )
1105 {
1106 if( sheet.GetPageNumber() == aPageNumber )
1107 return true;
1108 }
1109
1110 return false;
1111}
1112
1113
1114void SCH_SHEET_LIST::TrimToPageNumbers( const std::vector<wxString>& aPageInclusions )
1115{
1116 auto it = std::remove_if( begin(), end(),
1117 [&]( const SCH_SHEET_PATH& sheet )
1118 {
1119 return std::find( aPageInclusions.begin(),
1120 aPageInclusions.end(),
1121 sheet.GetPageNumber() ) == aPageInclusions.end();
1122 } );
1123
1124 erase( it, end() );
1125}
1126
1127
1129{
1130 for( const SCH_SHEET_PATH& sheet : *this )
1131 {
1132 if( sheet.LastScreen() && sheet.LastScreen()->IsContentModified() )
1133 return true;
1134 }
1135
1136 return false;
1137}
1138
1139
1141{
1142 for( const SCH_SHEET_PATH& sheet : *this )
1143 {
1144 if( sheet.LastScreen() )
1145 sheet.LastScreen()->SetContentModified( false );
1146 }
1147}
1148
1149
1150SCH_ITEM* SCH_SHEET_LIST::ResolveItem( const KIID& aID, SCH_SHEET_PATH* aPathOut, bool aAllowNullptrReturn ) const
1151{
1152 for( const SCH_SHEET_PATH& sheet : *this )
1153 {
1154 SCH_ITEM* item = sheet.ResolveItem( aID );
1155
1156 if( item )
1157 {
1158 if( aPathOut )
1159 *aPathOut = sheet;
1160
1161 return item;
1162 }
1163 }
1164
1165 // Not found; weak reference has been deleted.
1166 if( aAllowNullptrReturn )
1167 return nullptr;
1168 else
1170}
1171
1172
1174{
1175 for( SCH_ITEM* aItem : LastScreen()->Items() )
1176 {
1177 if( aItem->m_Uuid == aID )
1178 return aItem;
1179
1180 SCH_ITEM* childMatch = nullptr;
1181
1182 aItem->RunOnChildren(
1183 [&]( SCH_ITEM* aChild )
1184 {
1185 if( aChild->m_Uuid == aID )
1186 childMatch = aChild;
1187 },
1189
1190 if( childMatch )
1191 return childMatch;
1192 }
1193
1194 return nullptr;
1195}
1196
1197
1198void SCH_SHEET_LIST::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
1199{
1200 for( const SCH_SHEET_PATH& sheet : *this )
1201 {
1202 SCH_SCREEN* screen = sheet.LastScreen();
1203
1204 for( SCH_ITEM* aItem : screen->Items() )
1205 {
1206 aMap[ aItem->m_Uuid ] = aItem;
1207
1208 aItem->RunOnChildren(
1209 [&]( SCH_ITEM* aChild )
1210 {
1211 aMap[ aChild->m_Uuid ] = aChild;
1212 },
1214 }
1215 }
1216}
1217
1218
1220{
1221 // List of reference for power symbols
1222 SCH_REFERENCE_LIST references;
1223 SCH_REFERENCE_LIST additionalreferences; // Todo: add as a parameter to this function
1224
1225 // Map of locked symbols (not used, but needed by Annotate()
1226 SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
1227
1228 // Build the list of power symbols:
1229 for( SCH_SHEET_PATH& sheet : *this )
1230 {
1231 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1232 {
1233 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1234 LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
1235
1236 if( libSymbol && libSymbol->IsPower() )
1237 {
1238 SCH_REFERENCE schReference( symbol, sheet );
1239 references.AddItem( schReference );
1240 }
1241 }
1242 }
1243
1244 // Find duplicate, and silently clear annotation of duplicate
1245 std::map<wxString, int> ref_list; // stores the existing references
1246
1247 for( unsigned ii = 0; ii< references.GetCount(); ++ii )
1248 {
1249 wxString curr_ref = references[ii].GetRef();
1250
1251 if( curr_ref.IsEmpty() )
1252 continue;
1253
1254 if( ref_list.find( curr_ref ) == ref_list.end() )
1255 {
1256 ref_list[curr_ref] = ii;
1257 continue;
1258 }
1259
1260 // Possible duplicate, if the ref ends by a number:
1261 if( curr_ref.Last() < '0' && curr_ref.Last() > '9' )
1262 continue; // not annotated
1263
1264 // Duplicate: clear annotation by removing the number ending the ref
1265 while( !curr_ref.IsEmpty() && curr_ref.Last() >= '0' && curr_ref.Last() <= '9' )
1266 curr_ref.RemoveLast();
1267
1268 references[ii].SetRef( curr_ref );
1269 }
1270
1271 // Break full symbol reference into name (prefix) and number:
1272 // example: IC1 become IC, and 1
1273 references.SplitReferences();
1274
1275 // Ensure all power symbols have the reference starting by '#'
1276 // (Not sure this is really useful)
1277 for( unsigned ii = 0; ii< references.GetCount(); ++ii )
1278 {
1279 SCH_REFERENCE& ref_unit = references[ii];
1280
1281 if( ref_unit.GetRef()[0] != '#' )
1282 {
1283 wxString new_ref = "#" + ref_unit.GetRef();
1284 ref_unit.SetRef( new_ref );
1285 ref_unit.SetRefNum( ii );
1286 }
1287 }
1288}
1289
1290
1291void SCH_SHEET_LIST::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
1292 bool aForceIncludeOrphanSymbols ) const
1293{
1294 for( const SCH_SHEET_PATH& sheet : *this )
1295 sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
1296}
1297
1298
1300 const SCH_SHEET_PATH& aSheetPath,
1301 bool aIncludePowerSymbols,
1302 bool aForceIncludeOrphanSymbols ) const
1303{
1304 for( const SCH_SHEET_PATH& sheet : *this )
1305 {
1306 if( sheet.IsContainedWithin( aSheetPath ) )
1307 sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
1308 }
1309}
1310
1311
1312void SCH_SHEET_LIST::GetSheetsWithinPath( std::vector<SCH_SHEET_PATH>& aSheets,
1313 const SCH_SHEET_PATH& aSheetPath ) const
1314{
1315 for( const SCH_SHEET_PATH& sheet : *this )
1316 {
1317 if( sheet.IsContainedWithin( aSheetPath ) )
1318 aSheets.push_back( sheet );
1319 }
1320}
1321
1322
1323std::optional<SCH_SHEET_PATH> SCH_SHEET_LIST::GetSheetPathByKIIDPath( const KIID_PATH& aPath,
1324 bool aIncludeLastSheet ) const
1325{
1326 for( const SCH_SHEET_PATH& sheet : *this )
1327 {
1328 KIID_PATH testPath = sheet.Path();
1329
1330 if( !aIncludeLastSheet )
1331 testPath.pop_back();
1332
1333 if( testPath == aPath )
1334 return SCH_SHEET_PATH( sheet );
1335 }
1336
1337 return std::nullopt;
1338}
1339
1340
1342 bool aIncludePowerSymbols ) const
1343{
1344 for( auto it = begin(); it != end(); ++it )
1345 {
1347 ( *it ).GetMultiUnitSymbols( tempMap, aIncludePowerSymbols );
1348
1349 for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : tempMap )
1350 {
1351 // Merge this list into the main one
1352 unsigned n_refs = pair.second.GetCount();
1353
1354 for( unsigned thisRef = 0; thisRef < n_refs; ++thisRef )
1355 aRefList[pair.first].AddItem( pair.second[thisRef] );
1356 }
1357 }
1358}
1359
1360
1361bool SCH_SHEET_LIST::TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
1362 const wxString& aDestFileName )
1363{
1364 if( empty() )
1365 return false;
1366
1367 SCHEMATIC* sch = at( 0 ).LastScreen()->Schematic();
1368
1369 wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_LIST::TestForRecursion!" );
1370
1371 wxFileName rootFn = sch->GetFileName();
1372 wxFileName destFn = aDestFileName;
1373
1374 if( destFn.IsRelative() )
1375 destFn.MakeAbsolute( rootFn.GetPath() );
1376
1377 // Test each SCH_SHEET_PATH in this SCH_SHEET_LIST for potential recursion.
1378 for( unsigned i = 0; i < size(); i++ )
1379 {
1380 // Test each SCH_SHEET_PATH in the source sheet.
1381 for( unsigned j = 0; j < aSrcSheetHierarchy.size(); j++ )
1382 {
1383 const SCH_SHEET_PATH* sheetPath = &aSrcSheetHierarchy[j];
1384
1385 for( unsigned k = 0; k < sheetPath->size(); k++ )
1386 {
1387 if( at( i ).TestForRecursion( sheetPath->GetSheet( k )->GetFileName(),
1388 aDestFileName ) )
1389 {
1390 return true;
1391 }
1392 }
1393 }
1394 }
1395
1396 // The source sheet file can safely be added to the destination sheet file.
1397 return false;
1398}
1399
1400
1402{
1403 for( SCH_SHEET_PATH& path : *this )
1404 {
1405 if( path.Path() == aPath->Path() )
1406 return &path;
1407 }
1408
1409 return nullptr;
1410}
1411
1412
1414{
1415 for( SCH_SHEET_PATH& sheetpath : *this )
1416 {
1417 if( sheetpath.LastScreen() == aScreen )
1418 return sheetpath;
1419 }
1420
1421 return SCH_SHEET_PATH();
1422}
1423
1424
1426{
1427 SCH_SHEET_LIST retval;
1428
1429 for( const SCH_SHEET_PATH& sheetpath : *this )
1430 {
1431 if( sheetpath.LastScreen() == aScreen )
1432 retval.push_back( sheetpath );
1433 }
1434
1435 return retval;
1436}
1437
1438
1440 const std::vector<SCH_SYMBOL_INSTANCE>& aSymbolInstances )
1441{
1442 for( SCH_SHEET_PATH& sheetPath : *this )
1443 {
1444 for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1445 {
1446 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1447
1448 wxCHECK2( symbol, continue );
1449
1450 KIID_PATH sheetPathWithSymbolUuid = sheetPath.Path();
1451 sheetPathWithSymbolUuid.push_back( symbol->m_Uuid );
1452
1453 auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
1454 [ sheetPathWithSymbolUuid ]( const SCH_SYMBOL_INSTANCE& r ) -> bool
1455 {
1456 return sheetPathWithSymbolUuid == r.m_Path;
1457 } );
1458
1459 if( it == aSymbolInstances.end() )
1460 {
1461 wxLogTrace( traceSchSheetPaths, "No symbol instance found for symbol '%s'",
1462 sheetPathWithSymbolUuid.AsString() );
1463 continue;
1464 }
1465
1466 // Symbol instance paths are stored and looked up in memory with the root path so use
1467 // the full path here.
1468 symbol->AddHierarchicalReference( sheetPath.Path(), it->m_Reference, it->m_Unit );
1469 symbol->GetField( FIELD_T::REFERENCE )->SetText( it->m_Reference );
1470
1471 if( !it->m_Value.IsEmpty() )
1472 symbol->SetValueFieldText( it->m_Value );
1473
1474 if( !it->m_Footprint.IsEmpty() )
1475 symbol->SetFootprintFieldText( it->m_Footprint );
1476
1477 symbol->UpdatePrefix();
1478 }
1479 }
1480}
1481
1482
1483void SCH_SHEET_LIST::UpdateSheetInstanceData( const std::vector<SCH_SHEET_INSTANCE>& aSheetInstances )
1484{
1485
1486 for( SCH_SHEET_PATH& path : *this )
1487 {
1488 SCH_SHEET* sheet = path.Last();
1489
1490 wxCHECK2( sheet && path.Last(), continue );
1491
1492 auto it = std::find_if( aSheetInstances.begin(), aSheetInstances.end(),
1493 [&path]( const SCH_SHEET_INSTANCE& r ) -> bool
1494 {
1495 return path.Path() == r.m_Path;
1496 } );
1497
1498 if( it == aSheetInstances.end() )
1499 {
1500 wxLogTrace( traceSchSheetPaths, "No sheet instance found for path '%s'",
1501 path.Path().AsString() );
1502 continue;
1503 }
1504
1505 wxLogTrace( traceSchSheetPaths, "Setting sheet '%s' instance '%s' page number '%s'",
1506 ( sheet->GetName().IsEmpty() ) ? wxString( wxT( "root" ) ) : sheet->GetName(),
1507 path.Path().AsString(), it->m_PageNumber );
1508 path.SetPageNumber( it->m_PageNumber );
1509 }
1510}
1511
1512
1513std::vector<KIID_PATH> SCH_SHEET_LIST::GetPaths() const
1514{
1515 std::vector<KIID_PATH> paths;
1516
1517 for( const SCH_SHEET_PATH& sheetPath : *this )
1518 paths.emplace_back( sheetPath.Path() );
1519
1520 return paths;
1521}
1522
1523
1524std::vector<SCH_SHEET_INSTANCE> SCH_SHEET_LIST::GetSheetInstances() const
1525{
1526 std::vector<SCH_SHEET_INSTANCE> retval;
1527
1528 for( const SCH_SHEET_PATH& path : *this )
1529 {
1530 const SCH_SHEET* sheet = path.Last();
1531
1532 wxCHECK2( sheet, continue );
1533
1534 SCH_SHEET_INSTANCE instance;
1535 SCH_SHEET_PATH tmpPath = path;
1536
1537 tmpPath.pop_back();
1538 instance.m_Path = tmpPath.Path();
1539 instance.m_PageNumber = path.GetPageNumber();
1540
1541 retval.push_back( std::move( instance ) );
1542 }
1543
1544 return retval;
1545}
1546
1547
1549{
1550 for( const SCH_SHEET_PATH& instance : *this )
1551 {
1552 if( !instance.GetPageNumber().IsEmpty() )
1553 return false;
1554 }
1555
1556 return true;
1557}
1558
1559
1561{
1562 // Don't accidentally renumber existing sheets.
1563 wxCHECK( AllSheetPageNumbersEmpty(), /* void */ );
1564
1565 wxString tmp;
1566 int pageNumber = 1;
1567
1568 for( SCH_SHEET_PATH& instance : *this )
1569 {
1570 if( instance.Last()->IsVirtualRootSheet() )
1571 continue;
1572
1573 tmp.Printf( "%d", pageNumber );
1574 instance.SetPageNumber( tmp );
1575 pageNumber += 1;
1576 }
1577}
1578
1579
1581 const wxString& aProjectName )
1582{
1583 for( SCH_SHEET_PATH& sheetPath : *this )
1584 sheetPath.AddNewSymbolInstances( aPrefixSheetPath, aProjectName );
1585}
1586
1587
1589{
1590 for( SCH_SHEET_PATH& sheetPath : *this )
1591 sheetPath.RemoveSymbolInstances( aPrefixSheetPath );
1592}
1593
1594
1596 int aLastVirtualPageNumber )
1597{
1598 wxString pageNumber;
1599 int lastUsedPageNumber = 1;
1600 int nextVirtualPageNumber = aLastVirtualPageNumber;
1601
1602 // Fetch the list of page numbers already in use.
1603 std::vector< wxString > usedPageNumbers;
1604
1605 if( aPrefixSheetPath.size() )
1606 {
1607 SCH_SHEET_LIST prefixHierarchy( aPrefixSheetPath.at( 0 ) );
1608
1609 for( const SCH_SHEET_PATH& path : prefixHierarchy )
1610 {
1611 pageNumber = path.GetPageNumber();
1612
1613 if( !pageNumber.IsEmpty() )
1614 usedPageNumbers.emplace_back( pageNumber );
1615 }
1616 }
1617
1618 for( SCH_SHEET_PATH& sheetPath : *this )
1619 {
1620 KIID_PATH tmp = sheetPath.Path();
1621 SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
1622
1623 // Prefix the new hierarchical path.
1624 newSheetPath = newSheetPath + sheetPath;
1625
1626 // Sheets cannot have themselves in the path.
1627 tmp.pop_back();
1628
1629 SCH_SHEET* sheet = sheetPath.Last();
1630
1631 wxCHECK2( sheet, continue );
1632
1633 nextVirtualPageNumber += 1;
1634
1635 SCH_SHEET_INSTANCE instance;
1636
1637 // Add the instance if it doesn't already exist
1638 if( !sheet->getInstance( instance, tmp, true ) )
1639 {
1640 sheet->addInstance( tmp );
1641 sheet->getInstance( instance, tmp, true );
1642 }
1643
1644 // Get a new page number if we don't have one
1645 if( instance.m_PageNumber.IsEmpty() )
1646 {
1647 // Generate the next available page number.
1648 do
1649 {
1650 pageNumber.Printf( wxT( "%d" ), lastUsedPageNumber );
1651 lastUsedPageNumber += 1;
1652 } while( std::find( usedPageNumbers.begin(), usedPageNumbers.end(), pageNumber ) !=
1653 usedPageNumbers.end() );
1654
1655 instance.m_PageNumber = pageNumber;
1656 newSheetPath.SetVirtualPageNumber( nextVirtualPageNumber );
1657 }
1658
1659 newSheetPath.SetPageNumber( instance.m_PageNumber );
1660 usedPageNumbers.push_back( instance.m_PageNumber );
1661 }
1662}
1663
1664
1665void SCH_SHEET_LIST::CheckForMissingSymbolInstances( const wxString& aProjectName )
1666{
1667 wxLogTrace( traceSchSheetPaths,
1668 "SCH_SHEET_LIST::CheckForMissingSymbolInstances: Processing %zu sheet paths",
1669 size() );
1670
1671 for( SCH_SHEET_PATH& sheetPath : *this )
1672 {
1673 wxLogTrace( traceSchSheetPaths,
1674 " Processing sheet path: '%s' (size=%zu, KIID_PATH='%s')",
1675 sheetPath.PathHumanReadable( false ),
1676 sheetPath.size(),
1677 sheetPath.Path().AsString() );
1678 sheetPath.CheckForMissingSymbolInstances( aProjectName );
1679 }
1680}
1681
1682
1684{
1685 int lastVirtualPageNumber = 1;
1686
1687 for( const SCH_SHEET_PATH& sheetPath : *this )
1688 {
1689 if( sheetPath.GetVirtualPageNumber() > lastVirtualPageNumber )
1690 lastVirtualPageNumber = sheetPath.GetVirtualPageNumber();
1691 }
1692
1693 return lastVirtualPageNumber;
1694}
1695
1696
1697bool SCH_SHEET_LIST::HasPath( const KIID_PATH& aPath ) const
1698{
1699 for( const SCH_SHEET_PATH& path : *this )
1700 {
1701 if( path.Path() == aPath )
1702 return true;
1703 }
1704
1705 return false;
1706}
1707
1708
1709bool SCH_SHEET_LIST::ContainsSheet( const SCH_SHEET* aSheet ) const
1710{
1711 for( const SCH_SHEET_PATH& path : *this )
1712 {
1713 for( size_t i = 0; i < path.size(); i++ )
1714 {
1715 if( path.at( i ) == aSheet )
1716 return true;
1717 }
1718 }
1719
1720 return false;
1721}
1722
1723
1724std::optional<SCH_SHEET_PATH> SCH_SHEET_LIST::GetOrdinalPath( const SCH_SCREEN* aScreen ) const
1725{
1726 // Sheet paths with sheets that do not have a screen object are not valid.
1727 if( !aScreen )
1728 return std::nullopt;
1729
1730 for( const SCH_SHEET_PATH& path: *this )
1731 {
1732 if( path.LastScreen() == aScreen )
1733 return std::optional<SCH_SHEET_PATH>( path );
1734 }
1735
1736 return std::nullopt;
1737}
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:522
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual void UpdateHatching() const
virtual bool IsVisible() const
Definition eda_text.h:187
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:395
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:83
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
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:118
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0, const wxString &aVariantName=wxEmptyString) 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:630
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:247
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:54
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:118
const wxString & GetFileName() const
Definition sch_screen.h:153
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.
void InitializeAttributes(const SCH_SHEET &aSheet)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:382
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:376
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:142
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
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
bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flags.
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition sch_sheet.h:467
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)
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.
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
bool GetExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
bool GetInstance(SCH_SYMBOL_INSTANCE &aInstance, const KIID_PATH &aSheetPath, bool aTestFromEnd=false) const
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
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_ExcludedFromPosFiles
bool m_ExcludedFromSim
bool m_ExcludedFromBoard
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".
std::string path
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