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 <stambaughw@gmail.com>
6 * Copyright (C) 1992-2021 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 <sch_screen.h>
27 #include <sch_item.h>
28 #include <sch_marker.h>
29 #include <sch_reference_list.h>
30 #include <symbol_library.h>
31 #include <sch_sheet_path.h>
32 #include <sch_symbol.h>
33 #include <sch_sheet.h>
34 #include <schematic.h>
35 #include <template_fieldnames.h>
36 #include <trace_helpers.h>
37
38 #include <boost/functional/hash.hpp>
39 #include <wx/filename.h>
40 #include <wx/log.h>
41 #include "erc_item.h"
42
43
44 /**
45 * A singleton item of this class is returned for a weak reference that no longer exists.
46 * Its sole purpose is to flag the item as having been deleted.
47 */
48 class DELETED_SHEET_ITEM : public SCH_ITEM
49 {
50 public:
DELETED_SHEET_ITEM()51 DELETED_SHEET_ITEM() :
52 SCH_ITEM( nullptr, NOT_USED )
53 {}
54
GetSelectMenuText(EDA_UNITS aUnits) const55 wxString GetSelectMenuText( EDA_UNITS aUnits ) const override
56 {
57 return _( "(Deleted Item)" );
58 }
59
GetClass() const60 wxString GetClass() const override
61 {
62 return wxT( "DELETED_SHEET_ITEM" );
63 }
64
GetInstance()65 static DELETED_SHEET_ITEM* GetInstance()
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:
SetPosition(const wxPoint &)76 void SetPosition( const wxPoint& ) override {}
Print(const RENDER_SETTINGS * aSettings,const wxPoint & aOffset)77 void Print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset ) override {}
Move(const wxPoint & aMoveVector)78 void Move( const wxPoint& aMoveVector ) override {}
MirrorHorizontally(int aCenter)79 void MirrorHorizontally( int aCenter ) override {}
MirrorVertically(int aCenter)80 void MirrorVertically( int aCenter ) override {}
Rotate(const wxPoint & aCenter)81 void Rotate( const wxPoint& aCenter ) override {}
82
83 #if defined(DEBUG)
Show(int,std::ostream &) const84 void Show( int , std::ostream& ) const override {}
85 #endif
86 };
87
88
89 namespace std
90 {
operator ()(const SCH_SHEET_PATH & path) const91 size_t hash<SCH_SHEET_PATH>::operator()( const SCH_SHEET_PATH& path ) const
92 {
93 return path.GetCurrentHash();
94 }
95 }
96
97
SCH_SHEET_PATH()98 SCH_SHEET_PATH::SCH_SHEET_PATH()
99 {
100 m_virtualPageNumber = 0;
101 m_current_hash = 0;
102 }
103
104
SCH_SHEET_PATH(const SCH_SHEET_PATH & aOther)105 SCH_SHEET_PATH::SCH_SHEET_PATH( const SCH_SHEET_PATH& aOther )
106 {
107 initFromOther( aOther );
108 }
109
110
operator =(const SCH_SHEET_PATH & aOther)111 SCH_SHEET_PATH& SCH_SHEET_PATH::operator=( const SCH_SHEET_PATH& aOther )
112 {
113 initFromOther( aOther );
114 return *this;
115 }
116
117
initFromOther(const SCH_SHEET_PATH & aOther)118 void SCH_SHEET_PATH::initFromOther( const SCH_SHEET_PATH& aOther )
119 {
120 m_sheets = aOther.m_sheets;
121 m_virtualPageNumber = aOther.m_virtualPageNumber;
122 m_current_hash = aOther.m_current_hash;
123
124 // Note: don't copy m_recursion_test_cache as it is slow and we want SCH_SHEET_PATHS to be
125 // very fast to construct for use in the connectivity algorithm.
126 }
127
128
IsFullPath() const129 bool SCH_SHEET_PATH::IsFullPath() const
130 {
131 return GetSheet( 0 ) && GetSheet( 0 )->IsRootSheet();
132 }
133
134
Rehash()135 void SCH_SHEET_PATH::Rehash()
136 {
137 m_current_hash = 0;
138
139 for( auto sheet : m_sheets )
140 boost::hash_combine( m_current_hash, sheet->m_Uuid.Hash() );
141 }
142
143
Cmp(const SCH_SHEET_PATH & aSheetPathToTest) const144 int SCH_SHEET_PATH::Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const
145 {
146 if( size() > aSheetPathToTest.size() )
147 return 1;
148
149 if( size() < aSheetPathToTest.size() )
150 return -1;
151
152 //otherwise, same number of sheets.
153 for( unsigned i = 0; i < size(); i++ )
154 {
155 if( at( i )->m_Uuid < aSheetPathToTest.at( i )->m_Uuid )
156 return -1;
157
158 if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
159 return 1;
160 }
161
162 return 0;
163 }
164
165
ComparePageNumAndName(const SCH_SHEET_PATH & aSheetPathToTest) const166 int SCH_SHEET_PATH::ComparePageNumAndName( const SCH_SHEET_PATH& aSheetPathToTest ) const
167 {
168 wxString pageA = GetPageNumber();
169 wxString pageB = aSheetPathToTest.GetPageNumber();
170
171 int pageNumComp = SCH_SHEET::ComparePageNum( pageA, pageB );
172
173 if( pageNumComp == 0 )
174 {
175 wxString nameA = Last()->GetName();
176 wxString nameB = aSheetPathToTest.Last()->GetName();
177
178 return nameA.Cmp( nameB );
179 }
180 else
181 {
182 return pageNumComp;
183 }
184 }
185
186
IsContainedWithin(const SCH_SHEET_PATH & aSheetPathToTest) const187 bool SCH_SHEET_PATH::IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const
188 {
189 if( aSheetPathToTest.size() > size() )
190 return false;
191
192 for( size_t i = 0; i < aSheetPathToTest.size(); ++i )
193 {
194 if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
195 return false;
196 }
197
198 return true;
199 }
200
201
Last() const202 SCH_SHEET* SCH_SHEET_PATH::Last() const
203 {
204 if( !empty() )
205 return m_sheets.back();
206
207 return nullptr;
208 }
209
210
LastScreen()211 SCH_SCREEN* SCH_SHEET_PATH::LastScreen()
212 {
213 SCH_SHEET* lastSheet = Last();
214
215 if( lastSheet )
216 return lastSheet->GetScreen();
217
218 return nullptr;
219 }
220
221
LastScreen() const222 SCH_SCREEN* SCH_SHEET_PATH::LastScreen() const
223 {
224 SCH_SHEET* lastSheet = Last();
225
226 if( lastSheet )
227 return lastSheet->GetScreen();
228
229 return nullptr;
230 }
231
232
PathAsString() const233 wxString SCH_SHEET_PATH::PathAsString() const
234 {
235 wxString s;
236
237 s = wxT( "/" ); // This is the root path
238
239 // Start at 1 to avoid the root sheet, which does not need to be added to the path.
240 // Its timestamp changes anyway.
241 for( unsigned i = 1; i < size(); i++ )
242 s += at( i )->m_Uuid.AsString() + "/";
243
244 return s;
245 }
246
247
Path() const248 KIID_PATH SCH_SHEET_PATH::Path() const
249 {
250 KIID_PATH path;
251
252 for( const SCH_SHEET* sheet : m_sheets )
253 path.push_back( sheet->m_Uuid );
254
255 return path;
256 }
257
258
PathWithoutRootUuid() const259 KIID_PATH SCH_SHEET_PATH::PathWithoutRootUuid() const
260 {
261 KIID_PATH path;
262
263 for( size_t i = 1; i < size(); i++ )
264 path.push_back( at( i )->m_Uuid );
265
266 return path;
267 }
268
269
PathHumanReadable(bool aUseShortRootName) const270 wxString SCH_SHEET_PATH::PathHumanReadable( bool aUseShortRootName ) const
271 {
272 wxString s;
273 wxString fileName;
274
275 if( !empty() && at( 0 )->GetScreen() )
276 fileName = at( 0 )->GetScreen()->GetFileName();
277
278 wxFileName fn = fileName;
279
280 if( aUseShortRootName )
281 s = wxT( "/" ); // Use only the short name in netlists
282 else
283 s = fn.GetName() + wxT( "/" );
284
285 // Start at 1 since we've already processed the root sheet.
286 for( unsigned i = 1; i < size(); i++ )
287 s = s + at( i )->GetFields()[ SHEETNAME ].GetShownText() + wxT( "/" );
288
289 return s;
290 }
291
292
UpdateAllScreenReferences()293 void SCH_SHEET_PATH::UpdateAllScreenReferences()
294 {
295 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
296 {
297 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
298 symbol->GetField( REFERENCE_FIELD )->SetText( symbol->GetRef( this ) );
299 symbol->GetField( VALUE_FIELD )->SetText( symbol->GetValue( this, false ) );
300 symbol->GetField( FOOTPRINT_FIELD )->SetText( symbol->GetFootprint( this, false ) );
301 symbol->UpdateUnit( symbol->GetUnitSelection( this ) );
302 }
303 }
304
305
306
GetSymbols(SCH_REFERENCE_LIST & aReferences,bool aIncludePowerSymbols,bool aForceIncludeOrphanSymbols) const307 void SCH_SHEET_PATH::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
308 bool aForceIncludeOrphanSymbols ) const
309 {
310 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
311 {
312 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
313 AppendSymbol( aReferences, symbol, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
314 }
315 }
316
317
AppendSymbol(SCH_REFERENCE_LIST & aReferences,SCH_SYMBOL * aSymbol,bool aIncludePowerSymbols,bool aForceIncludeOrphanSymbols) const318 void SCH_SHEET_PATH::AppendSymbol( SCH_REFERENCE_LIST& aReferences, SCH_SYMBOL* aSymbol,
319 bool aIncludePowerSymbols,
320 bool aForceIncludeOrphanSymbols ) const
321 {
322 // Skip pseudo-symbols, which have a reference starting with #. This mainly
323 // affects power symbols.
324 if( aIncludePowerSymbols || aSymbol->GetRef( this )[0] != wxT( '#' ) )
325 {
326 LIB_SYMBOL* symbol = aSymbol->GetLibSymbolRef().get();
327
328 if( symbol || aForceIncludeOrphanSymbols )
329 {
330 SCH_REFERENCE schReference( aSymbol, symbol, *this );
331
332 schReference.SetSheetNumber( m_virtualPageNumber );
333 aReferences.AddItem( schReference );
334 }
335 }
336 }
337
338
GetMultiUnitSymbols(SCH_MULTI_UNIT_REFERENCE_MAP & aRefList,bool aIncludePowerSymbols) const339 void SCH_SHEET_PATH::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList,
340 bool aIncludePowerSymbols ) const
341 {
342 for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
343 {
344 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
345 AppendMultiUnitSymbol( aRefList, symbol, aIncludePowerSymbols );
346 }
347 }
348
349
AppendMultiUnitSymbol(SCH_MULTI_UNIT_REFERENCE_MAP & aRefList,SCH_SYMBOL * aSymbol,bool aIncludePowerSymbols) const350 void SCH_SHEET_PATH::AppendMultiUnitSymbol( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList,
351 SCH_SYMBOL* aSymbol,
352 bool aIncludePowerSymbols ) const
353 {
354 // Skip pseudo-symbols, which have a reference starting with #. This mainly
355 // affects power symbols.
356 if( !aIncludePowerSymbols && aSymbol->GetRef( this )[0] == wxT( '#' ) )
357 return;
358
359 LIB_SYMBOL* symbol = aSymbol->GetLibSymbolRef().get();
360
361 if( symbol && symbol->GetUnitCount() > 1 )
362 {
363 SCH_REFERENCE schReference = SCH_REFERENCE( aSymbol, symbol, *this );
364 schReference.SetSheetNumber( m_virtualPageNumber );
365 wxString reference_str = schReference.GetRef();
366
367 // Never lock unassigned references
368 if( reference_str[reference_str.Len() - 1] == '?' )
369 return;
370
371 aRefList[reference_str].AddItem( schReference );
372 }
373 }
374
375
operator ==(const SCH_SHEET_PATH & d1) const376 bool SCH_SHEET_PATH::operator==( const SCH_SHEET_PATH& d1 ) const
377 {
378 return m_current_hash == d1.GetCurrentHash();
379 }
380
381
TestForRecursion(const wxString & aSrcFileName,const wxString & aDestFileName)382 bool SCH_SHEET_PATH::TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName )
383 {
384 auto pair = std::make_pair( aSrcFileName, aDestFileName );
385
386 if( m_recursion_test_cache.count( pair ) )
387 return m_recursion_test_cache.at( pair );
388
389 SCHEMATIC* sch = LastScreen()->Schematic();
390
391 wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_PATH::TestForRecursion!" );
392
393 wxFileName rootFn = sch->GetFileName();
394 wxFileName srcFn = aSrcFileName;
395 wxFileName destFn = aDestFileName;
396
397 if( srcFn.IsRelative() )
398 srcFn.MakeAbsolute( rootFn.GetPath() );
399
400 if( destFn.IsRelative() )
401 destFn.MakeAbsolute( rootFn.GetPath() );
402
403
404 // The source and destination sheet file names cannot be the same.
405 if( srcFn == destFn )
406 {
407 m_recursion_test_cache[pair] = true;
408 return true;
409 }
410
411 /// @todo Store sheet file names with full path, either relative to project path
412 /// or absolute path. The current design always assumes subsheet files are
413 /// located in the project folder which may or may not be desirable.
414 unsigned i = 0;
415
416 while( i < size() )
417 {
418 wxFileName cmpFn = at( i )->GetFileName();
419
420 if( cmpFn.IsRelative() )
421 cmpFn.MakeAbsolute( rootFn.GetPath() );
422
423 // Test if the file name of the destination sheet is in anywhere in this sheet path.
424 if( cmpFn == destFn )
425 break;
426
427 i++;
428 }
429
430 // The destination sheet file name was not found in the sheet path or the destination
431 // sheet file name is the root sheet so no recursion is possible.
432 if( i >= size() || i == 0 )
433 {
434 m_recursion_test_cache[pair] = false;
435 return false;
436 }
437
438 // Walk back up to the root sheet to see if the source file name is already a parent in
439 // the sheet path. If so, recursion will occur.
440 do
441 {
442 i -= 1;
443
444 wxFileName cmpFn = at( i )->GetFileName();
445
446 if( cmpFn.IsRelative() )
447 cmpFn.MakeAbsolute( rootFn.GetPath() );
448
449 if( cmpFn == srcFn )
450 {
451 m_recursion_test_cache[pair] = true;
452 return true;
453 }
454
455 } while( i != 0 );
456
457 // The source sheet file name is not a parent of the destination sheet file name.
458 m_recursion_test_cache[pair] = false;
459 return false;
460 }
461
462
GetPageNumber() const463 wxString SCH_SHEET_PATH::GetPageNumber() const
464 {
465 SCH_SHEET* sheet = Last();
466
467 wxCHECK( sheet, wxEmptyString );
468
469 return sheet->GetPageNumber( *this );
470 }
471
472
SetPageNumber(const wxString & aPageNumber)473 void SCH_SHEET_PATH::SetPageNumber( const wxString& aPageNumber )
474 {
475 SCH_SHEET* sheet = Last();
476
477 wxCHECK( sheet, /* void */ );
478
479 sheet->SetPageNumber( *this, aPageNumber );
480 }
481
482
MakeFilePathRelativeToParentSheet()483 void SCH_SHEET_PATH::MakeFilePathRelativeToParentSheet()
484 {
485 wxCHECK( m_sheets.size() > 1, /* void */ );
486
487 wxFileName sheetFileName = Last()->GetFileName();
488
489 // If the sheet file name is absolute, then the user requested is so don't make it relative.
490 if( sheetFileName.IsAbsolute() )
491 return;
492
493 SCH_SCREEN* screen = LastScreen();
494 SCH_SCREEN* parentScreen = m_sheets[ m_sheets.size() - 2 ]->GetScreen();
495
496 wxCHECK( screen && parentScreen, /* void */ );
497
498 wxFileName fileName = screen->GetFileName();
499 wxFileName parentFileName = parentScreen->GetFileName();
500
501 // SCH_SCREEN file names must be absolute. If they are not, someone set them incorrectly
502 // on load or on creation.
503 wxCHECK( fileName.IsAbsolute() && parentFileName.IsAbsolute(), /* void */ );
504
505 if( fileName.GetPath() == parentFileName.GetPath() )
506 {
507 Last()->SetFileName( fileName.GetFullName() );
508 }
509 else if( fileName.MakeRelativeTo( parentFileName.GetPath() ) )
510 {
511 Last()->SetFileName( fileName.GetFullPath() );
512 }
513 else
514 {
515 Last()->SetFileName( screen->GetFileName() );
516 }
517
518 wxLogTrace( tracePathsAndFiles,
519 wxT( "\n File name: '%s'"
520 "\n parent file name '%s',"
521 "\n sheet '%s' file name '%s'." ),
522 screen->GetFileName(), parentScreen->GetFileName(), PathHumanReadable(),
523 Last()->GetFileName() );
524 }
525
526
SCH_SHEET_LIST(SCH_SHEET * aSheet,bool aCheckIntegrity)527 SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet, bool aCheckIntegrity )
528 {
529 if( aSheet != nullptr )
530 {
531 BuildSheetList( aSheet, aCheckIntegrity );
532
533 if( aSheet->IsRootSheet() )
534 SortByPageNumbers();
535 }
536 }
537
538
BuildSheetList(SCH_SHEET * aSheet,bool aCheckIntegrity)539 void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
540 {
541 wxCHECK_RET( aSheet != nullptr, wxT( "Cannot build sheet list from undefined sheet." ) );
542
543 std::vector<SCH_SHEET*> badSheets;
544
545 m_currentSheetPath.push_back( aSheet );
546 m_currentSheetPath.SetVirtualPageNumber( static_cast<int>( size() ) + 1 );
547 push_back( m_currentSheetPath );
548
549 if( m_currentSheetPath.LastScreen() )
550 {
551 wxString parentFileName = aSheet->GetFileName();
552 std::vector<SCH_ITEM*> childSheets;
553 m_currentSheetPath.LastScreen()->GetSheets( &childSheets );
554
555 for( SCH_ITEM* item : childSheets )
556 {
557 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
558
559 if( aCheckIntegrity )
560 {
561 if( !m_currentSheetPath.TestForRecursion( sheet->GetFileName(), parentFileName ) )
562 BuildSheetList( sheet, true );
563 else
564 badSheets.push_back( sheet );
565 }
566 else
567 {
568 BuildSheetList( sheet, false );
569 }
570 }
571 }
572
573 if( aCheckIntegrity )
574 {
575 for( SCH_SHEET* sheet : badSheets )
576 {
577 m_currentSheetPath.LastScreen()->Remove( sheet );
578 m_currentSheetPath.LastScreen()->SetContentModified();
579 }
580 }
581
582 m_currentSheetPath.pop_back();
583 }
584
585
SortByPageNumbers(bool aUpdateVirtualPageNums)586 void SCH_SHEET_LIST::SortByPageNumbers( bool aUpdateVirtualPageNums )
587 {
588 std::sort( begin(), end(),
589 []( SCH_SHEET_PATH a, SCH_SHEET_PATH b ) -> bool
590 {
591 return a.ComparePageNumAndName( b ) < 0;
592 } );
593
594 if( aUpdateVirtualPageNums )
595 {
596 int virtualPageNum = 1;
597
598 for( SCH_SHEET_PATH& sheet : *this )
599 {
600 sheet.SetVirtualPageNumber( virtualPageNum++ );
601 }
602 }
603 }
604
605
NameExists(const wxString & aSheetName) const606 bool SCH_SHEET_LIST::NameExists( const wxString& aSheetName ) const
607 {
608 for( const SCH_SHEET_PATH& sheet : *this )
609 {
610 if( sheet.Last()->GetName() == aSheetName )
611 return true;
612 }
613
614 return false;
615 }
616
617
PageNumberExists(const wxString & aPageNumber) const618 bool SCH_SHEET_LIST::PageNumberExists( const wxString& aPageNumber ) const
619 {
620 for( const SCH_SHEET_PATH& sheet : *this )
621 {
622 if( sheet.GetPageNumber() == aPageNumber )
623 return true;
624 }
625
626 return false;
627 }
628
629
IsModified() const630 bool SCH_SHEET_LIST::IsModified() const
631 {
632 for( const SCH_SHEET_PATH& sheet : *this )
633 {
634 if( sheet.LastScreen() && sheet.LastScreen()->IsContentModified() )
635 return true;
636 }
637
638 return false;
639 }
640
641
ClearModifyStatus()642 void SCH_SHEET_LIST::ClearModifyStatus()
643 {
644 for( const SCH_SHEET_PATH& sheet : *this )
645 {
646 if( sheet.LastScreen() )
647 sheet.LastScreen()->SetContentModified( false );
648 }
649 }
650
651
GetItem(const KIID & aID,SCH_SHEET_PATH * aPathOut) const652 SCH_ITEM* SCH_SHEET_LIST::GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut ) const
653 {
654 for( const SCH_SHEET_PATH& sheet : *this )
655 {
656 SCH_SCREEN* screen = sheet.LastScreen();
657
658 for( SCH_ITEM* aItem : screen->Items() )
659 {
660 if( aItem->m_Uuid == aID )
661 {
662 if( aPathOut )
663 *aPathOut = sheet;
664
665 return aItem;
666 }
667
668 SCH_ITEM* childMatch = nullptr;
669
670 aItem->RunOnChildren(
671 [&]( SCH_ITEM* aChild )
672 {
673 if( aChild->m_Uuid == aID )
674 childMatch = aChild;
675 } );
676
677 if( childMatch )
678 {
679 if( aPathOut )
680 *aPathOut = sheet;
681
682 return childMatch;
683 }
684 }
685 }
686
687 // Not found; weak reference has been deleted.
688 return DELETED_SHEET_ITEM::GetInstance();
689 }
690
691
FillItemMap(std::map<KIID,EDA_ITEM * > & aMap)692 void SCH_SHEET_LIST::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
693 {
694 for( const SCH_SHEET_PATH& sheet : *this )
695 {
696 SCH_SCREEN* screen = sheet.LastScreen();
697
698 for( SCH_ITEM* aItem : screen->Items() )
699 {
700 aMap[ aItem->m_Uuid ] = aItem;
701
702 aItem->RunOnChildren(
703 [&]( SCH_ITEM* aChild )
704 {
705 aMap[ aChild->m_Uuid ] = aChild;
706 } );
707 }
708 }
709 }
710
711
AnnotatePowerSymbols()712 void SCH_SHEET_LIST::AnnotatePowerSymbols()
713 {
714 // List of reference for power symbols
715 SCH_REFERENCE_LIST references;
716 SCH_REFERENCE_LIST additionalreferences; // Todo: add as a parameter to this function
717
718 // Map of locked symbols (not used, but needed by Annotate()
719 SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
720
721 // Build the list of power symbols:
722 for( SCH_SHEET_PATH& sheet : *this )
723 {
724 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
725 {
726 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
727 LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
728
729 if( libSymbol && libSymbol->IsPower() )
730 {
731 SCH_REFERENCE schReference( symbol, libSymbol, sheet );
732 references.AddItem( schReference );
733 }
734 }
735 }
736
737 // Find duplicate, and silently clear annotation of duplicate
738 std::map<wxString, int> ref_list; // stores the existing references
739
740 for( unsigned ii = 0; ii< references.GetCount(); ++ii )
741 {
742 wxString curr_ref = references[ii].GetRef();
743
744 if( ref_list.find( curr_ref ) == ref_list.end() )
745 {
746 ref_list[curr_ref] = ii;
747 continue;
748 }
749
750 // Possible duplicate, if the ref ends by a number:
751 if( curr_ref.Last() < '0' && curr_ref.Last() > '9' )
752 continue; // not annotated
753
754 // Duplicate: clear annotation by removing the number ending the ref
755 while( curr_ref.Last() >= '0' && curr_ref.Last() <= '9' )
756 curr_ref.RemoveLast();
757
758 references[ii].SetRef( curr_ref );
759 }
760
761 // Break full symbol reference into name (prefix) and number:
762 // example: IC1 become IC, and 1
763 references.SplitReferences();
764
765 // Ensure all power symbols have the reference starting by '#'
766 // (Not sure this is really useful)
767 for( unsigned ii = 0; ii< references.GetCount(); ++ii )
768 {
769 if( references[ii].GetRef()[0] != '#' )
770 {
771 wxString new_ref = "#" + references[ii].GetRef();
772 references[ii].SetRef( new_ref );
773 }
774 }
775
776 // Recalculate and update reference numbers in schematic
777 references.Annotate( false, 0, 100, lockedSymbols, additionalreferences );
778 references.UpdateAnnotation();
779 }
780
781
GetSymbols(SCH_REFERENCE_LIST & aReferences,bool aIncludePowerSymbols,bool aForceIncludeOrphanSymbols) const782 void SCH_SHEET_LIST::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
783 bool aForceIncludeOrphanSymbols ) const
784 {
785 for( const SCH_SHEET_PATH& sheet : *this )
786 sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
787 }
788
789
GetSymbolsWithinPath(SCH_REFERENCE_LIST & aReferences,const SCH_SHEET_PATH & aSheetPath,bool aIncludePowerSymbols,bool aForceIncludeOrphanSymbols) const790 void SCH_SHEET_LIST::GetSymbolsWithinPath( SCH_REFERENCE_LIST& aReferences,
791 const SCH_SHEET_PATH& aSheetPath,
792 bool aIncludePowerSymbols,
793 bool aForceIncludeOrphanSymbols ) const
794 {
795 for( const SCH_SHEET_PATH& sheet : *this )
796 {
797 if( sheet.IsContainedWithin( aSheetPath ) )
798 sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
799 }
800 }
801
802
GetSheetsWithinPath(SCH_SHEET_PATHS & aSheets,const SCH_SHEET_PATH & aSheetPath) const803 void SCH_SHEET_LIST::GetSheetsWithinPath( SCH_SHEET_PATHS& aSheets,
804 const SCH_SHEET_PATH& aSheetPath ) const
805 {
806 for( const SCH_SHEET_PATH& sheet : *this )
807 {
808 if( sheet.IsContainedWithin( aSheetPath ) )
809 aSheets.push_back( sheet );
810 }
811 }
812
813
GetMultiUnitSymbols(SCH_MULTI_UNIT_REFERENCE_MAP & aRefList,bool aIncludePowerSymbols) const814 void SCH_SHEET_LIST::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
815 bool aIncludePowerSymbols ) const
816 {
817 for( SCH_SHEET_PATHS::const_iterator it = begin(); it != end(); ++it )
818 {
819 SCH_MULTI_UNIT_REFERENCE_MAP tempMap;
820 ( *it ).GetMultiUnitSymbols( tempMap );
821
822 for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : tempMap )
823 {
824 // Merge this list into the main one
825 unsigned n_refs = pair.second.GetCount();
826
827 for( unsigned thisRef = 0; thisRef < n_refs; ++thisRef )
828 aRefList[pair.first].AddItem( pair.second[thisRef] );
829 }
830 }
831 }
832
833
TestForRecursion(const SCH_SHEET_LIST & aSrcSheetHierarchy,const wxString & aDestFileName)834 bool SCH_SHEET_LIST::TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
835 const wxString& aDestFileName )
836 {
837 if( empty() )
838 return false;
839
840 SCHEMATIC* sch = at( 0 ).LastScreen()->Schematic();
841
842 wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_LIST::TestForRecursion!" );
843
844 wxFileName rootFn = sch->GetFileName();
845 wxFileName destFn = aDestFileName;
846
847 if( destFn.IsRelative() )
848 destFn.MakeAbsolute( rootFn.GetPath() );
849
850 // Test each SCH_SHEET_PATH in this SCH_SHEET_LIST for potential recursion.
851 for( unsigned i = 0; i < size(); i++ )
852 {
853 // Test each SCH_SHEET_PATH in the source sheet.
854 for( unsigned j = 0; j < aSrcSheetHierarchy.size(); j++ )
855 {
856 const SCH_SHEET_PATH* sheetPath = &aSrcSheetHierarchy[j];
857
858 for( unsigned k = 0; k < sheetPath->size(); k++ )
859 {
860 if( at( i ).TestForRecursion( sheetPath->GetSheet( k )->GetFileName(),
861 aDestFileName ) )
862 {
863 return true;
864 }
865 }
866 }
867 }
868
869 // The source sheet file can safely be added to the destination sheet file.
870 return false;
871 }
872
873
FindSheetForScreen(const SCH_SCREEN * aScreen)874 SCH_SHEET_PATH* SCH_SHEET_LIST::FindSheetForScreen( const SCH_SCREEN* aScreen )
875 {
876 for( SCH_SHEET_PATH& sheetpath : *this )
877 {
878 if( sheetpath.LastScreen() == aScreen )
879 return &sheetpath;
880 }
881
882 return nullptr;
883 }
884
885
FindAllSheetsForScreen(const SCH_SCREEN * aScreen) const886 SCH_SHEET_LIST SCH_SHEET_LIST::FindAllSheetsForScreen( const SCH_SCREEN* aScreen ) const
887 {
888 SCH_SHEET_LIST retval;
889
890 for( const SCH_SHEET_PATH& sheetpath : *this )
891 {
892 if( sheetpath.LastScreen() == aScreen )
893 retval.push_back( sheetpath );
894 }
895
896 return retval;
897 }
898
899
UpdateSymbolInstances(const std::vector<SYMBOL_INSTANCE_REFERENCE> & aSymbolInstances)900 void SCH_SHEET_LIST::UpdateSymbolInstances(
901 const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbolInstances )
902 {
903 SCH_REFERENCE_LIST symbolInstances;
904
905 GetSymbols( symbolInstances, true, true );
906
907 std::map<KIID_PATH, wxString> pathNameCache;
908
909 // Calculating the name of a path is somewhat expensive; on large designs with many symbols
910 // this can blow up to a serious amount of time when loading the schematic
911 auto getName = [&pathNameCache]( const KIID_PATH& aPath ) -> const wxString&
912 {
913 if( pathNameCache.count( aPath ) )
914 return pathNameCache.at( aPath );
915
916 pathNameCache[aPath] = aPath.AsString();
917 return pathNameCache[aPath];
918 };
919
920 for( size_t i = 0; i < symbolInstances.GetCount(); i++ )
921 {
922 // The instance paths are stored in the file sans root path so the comparison
923 // should not include the root path.
924 wxString path = symbolInstances[i].GetPath();
925
926 auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
927 [ path, &getName ]( const SYMBOL_INSTANCE_REFERENCE& r ) -> bool
928 {
929 return path == getName( r.m_Path );
930 } );
931
932 if( it == aSymbolInstances.end() )
933 {
934 wxLogTrace( traceSchSheetPaths, "No symbol instance found for path '%s'", path );
935 continue;
936 }
937
938 SCH_SYMBOL* symbol = symbolInstances[ i ].GetSymbol();
939
940 wxCHECK2( symbol, continue );
941
942 // Symbol instance paths are stored and looked up in memory with the root path so use
943 // the full path here.
944 symbol->AddHierarchicalReference( symbolInstances[i].GetSheetPath().Path(),
945 it->m_Reference, it->m_Unit, it->m_Value,
946 it->m_Footprint );
947 symbol->GetField( REFERENCE_FIELD )->SetText( it->m_Reference );
948 }
949 }
950
951
UpdateSheetInstances(const std::vector<SCH_SHEET_INSTANCE> & aSheetInstances)952 void SCH_SHEET_LIST::UpdateSheetInstances( const std::vector<SCH_SHEET_INSTANCE>& aSheetInstances )
953 {
954
955 for( const SCH_SHEET_PATH& path : *this )
956 {
957 SCH_SHEET* sheet = path.Last();
958
959 wxCHECK2( sheet, continue );
960
961 auto it = std::find_if( aSheetInstances.begin(), aSheetInstances.end(),
962 [ path ]( const SCH_SHEET_INSTANCE& r ) -> bool
963 {
964 return path.PathWithoutRootUuid() == r.m_Path;
965 } );
966
967 if( it == aSheetInstances.end() )
968 {
969 wxLogTrace( traceSchSheetPaths, "No sheet instance found for path '%s'",
970 path.PathWithoutRootUuid().AsString() );
971 continue;
972 }
973
974 wxLogTrace( traceSchSheetPaths, "Setting sheet '%s' instance '%s' page number '%s'",
975 ( sheet->GetName().IsEmpty() ) ? wxT( "root" ) : sheet->GetName(),
976 path.PathWithoutRootUuid().AsString(), it->m_PageNumber );
977 sheet->AddInstance( path );
978 sheet->SetPageNumber( path, it->m_PageNumber );
979 }
980 }
981
982
GetPaths() const983 std::vector<KIID_PATH> SCH_SHEET_LIST::GetPaths() const
984 {
985 std::vector<KIID_PATH> paths;
986
987 for( const SCH_SHEET_PATH& sheetPath : *this )
988 paths.emplace_back( sheetPath.Path() );
989
990 return paths;
991 }
992
993
GetSheetInstances() const994 std::vector<SCH_SHEET_INSTANCE> SCH_SHEET_LIST::GetSheetInstances() const
995 {
996 std::vector<SCH_SHEET_INSTANCE> retval;
997
998 for( const SCH_SHEET_PATH& path : *this )
999 {
1000 const SCH_SHEET* sheet = path.Last();
1001
1002 wxCHECK2( sheet, continue );
1003
1004 SCH_SHEET_INSTANCE instance;
1005 instance.m_Path = path.PathWithoutRootUuid();
1006 instance.m_PageNumber = sheet->GetPageNumber( path );
1007
1008 retval.push_back( instance );
1009 }
1010
1011 return retval;
1012 }
1013
1014
AllSheetPageNumbersEmpty() const1015 bool SCH_SHEET_LIST::AllSheetPageNumbersEmpty() const
1016 {
1017 for( const SCH_SHEET_PATH& instance : *this )
1018 {
1019 const SCH_SHEET* sheet = instance.Last();
1020
1021 wxCHECK2( sheet, continue );
1022
1023 if( !sheet->GetPageNumber( instance ).IsEmpty() )
1024 return false;
1025 }
1026
1027 return true;
1028 }
1029
1030
SetInitialPageNumbers()1031 void SCH_SHEET_LIST::SetInitialPageNumbers()
1032 {
1033 // Don't accidentally renumber existing sheets.
1034 wxCHECK( AllSheetPageNumbersEmpty(), /* void */ );
1035
1036 wxString tmp;
1037 int pageNumber = 1;
1038
1039 for( const SCH_SHEET_PATH& instance : *this )
1040 {
1041 SCH_SHEET* sheet = instance.Last();
1042
1043 wxCHECK2( sheet, continue );
1044
1045 sheet->AddInstance( instance );
1046 tmp.Printf( "%d", pageNumber );
1047 sheet->SetPageNumber( instance, tmp );
1048 pageNumber += 1;
1049 }
1050 }
1051