You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1552 lines
44 KiB

6 years ago
  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 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. #include <refdes_utils.h>
  26. #include <hash.h>
  27. #include <sch_screen.h>
  28. #include <sch_marker.h>
  29. #include <sch_label.h>
  30. #include <sch_shape.h>
  31. #include <symbol_library.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. #include <wx/filename.h>
  39. #include <wx/log.h>
  40. /**
  41. * A singleton item of this class is returned for a weak reference that no longer exists.
  42. *
  43. * Its sole purpose is to flag the item as having been deleted.
  44. */
  45. class DELETED_SHEET_ITEM : public SCH_ITEM
  46. {
  47. public:
  48. DELETED_SHEET_ITEM() :
  49. SCH_ITEM( nullptr, NOT_USED )
  50. {}
  51. wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const override
  52. {
  53. return _( "(Deleted Item)" );
  54. }
  55. wxString GetClass() const override
  56. {
  57. return wxT( "DELETED_SHEET_ITEM" );
  58. }
  59. static DELETED_SHEET_ITEM* GetInstance()
  60. {
  61. static DELETED_SHEET_ITEM* item = nullptr;
  62. if( !item )
  63. item = new DELETED_SHEET_ITEM();
  64. return item;
  65. }
  66. // pure virtuals:
  67. void SetPosition( const VECTOR2I& ) override {}
  68. void Move( const VECTOR2I& aMoveVector ) override {}
  69. void MirrorHorizontally( int aCenter ) override {}
  70. void MirrorVertically( int aCenter ) override {}
  71. void Rotate( const VECTOR2I& aCenter, bool aRotateCCW ) override {}
  72. double Similarity( const SCH_ITEM& aOther ) const override
  73. {
  74. return 0.0;
  75. }
  76. bool operator==( const SCH_ITEM& aOther ) const override
  77. {
  78. return false;
  79. }
  80. #if defined(DEBUG)
  81. void Show( int , std::ostream& ) const override {}
  82. #endif
  83. };
  84. namespace std
  85. {
  86. size_t hash<SCH_SHEET_PATH>::operator()( const SCH_SHEET_PATH& path ) const
  87. {
  88. return path.GetCurrentHash();
  89. }
  90. }
  91. SCH_SHEET_PATH::SCH_SHEET_PATH()
  92. {
  93. m_virtualPageNumber = 1;
  94. m_current_hash = 0;
  95. }
  96. SCH_SHEET_PATH::SCH_SHEET_PATH( const SCH_SHEET_PATH& aOther )
  97. {
  98. initFromOther( aOther );
  99. }
  100. SCH_SHEET_PATH& SCH_SHEET_PATH::operator=( const SCH_SHEET_PATH& aOther )
  101. {
  102. initFromOther( aOther );
  103. return *this;
  104. }
  105. SCH_SHEET_PATH SCH_SHEET_PATH::operator+( const SCH_SHEET_PATH& aOther )
  106. {
  107. SCH_SHEET_PATH retv = *this;
  108. size_t size = aOther.size();
  109. for( size_t i = 0; i < size; i++ )
  110. retv.push_back( aOther.at( i ) );
  111. return retv;
  112. }
  113. void SCH_SHEET_PATH::initFromOther( const SCH_SHEET_PATH& aOther )
  114. {
  115. m_sheets = aOther.m_sheets;
  116. m_virtualPageNumber = aOther.m_virtualPageNumber;
  117. m_current_hash = aOther.m_current_hash;
  118. m_cached_page_number = aOther.m_cached_page_number;
  119. // Note: don't copy m_recursion_test_cache as it is slow and we want
  120. // std::vector<SCH_SHEET_PATH> to be very fast to construct for use in
  121. // the connectivity algorithm.
  122. }
  123. bool SCH_SHEET_PATH::IsFullPath() const
  124. {
  125. // The root sheet path is empty. All other sheet paths must start with the root sheet path.
  126. return ( m_sheets.size() == 0 ) || ( GetSheet( 0 )->IsRootSheet() );
  127. }
  128. void SCH_SHEET_PATH::Rehash()
  129. {
  130. m_current_hash = 0;
  131. for( SCH_SHEET* sheet : m_sheets )
  132. hash_combine( m_current_hash, sheet->m_Uuid.Hash() );
  133. }
  134. int SCH_SHEET_PATH::Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const
  135. {
  136. if( size() > aSheetPathToTest.size() )
  137. return 1;
  138. if( size() < aSheetPathToTest.size() )
  139. return -1;
  140. // otherwise, same number of sheets.
  141. for( unsigned i = 0; i < size(); i++ )
  142. {
  143. if( at( i )->m_Uuid < aSheetPathToTest.at( i )->m_Uuid )
  144. return -1;
  145. if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
  146. return 1;
  147. }
  148. return 0;
  149. }
  150. int SCH_SHEET_PATH::ComparePageNum( const SCH_SHEET_PATH& aSheetPathToTest ) const
  151. {
  152. wxString pageA = this->GetPageNumber();
  153. wxString pageB = aSheetPathToTest.GetPageNumber();
  154. int pageNumComp = SCH_SHEET::ComparePageNum( pageA, pageB );
  155. if( pageNumComp == 0 )
  156. {
  157. int virtualPageA = GetVirtualPageNumber();
  158. int virtualPageB = aSheetPathToTest.GetVirtualPageNumber();
  159. if( virtualPageA > virtualPageB )
  160. pageNumComp = 1;
  161. else if( virtualPageA < virtualPageB )
  162. pageNumComp = -1;
  163. }
  164. return pageNumComp;
  165. }
  166. bool SCH_SHEET_PATH::IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const
  167. {
  168. if( aSheetPathToTest.size() > size() )
  169. return false;
  170. for( size_t i = 0; i < aSheetPathToTest.size(); ++i )
  171. {
  172. if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
  173. {
  174. wxLogTrace( traceSchSheetPaths, "Sheet path '%s' is not within path '%s'.",
  175. aSheetPathToTest.Path().AsString(), Path().AsString() );
  176. return false;
  177. }
  178. }
  179. wxLogTrace( traceSchSheetPaths, "Sheet path '%s' is within path '%s'.",
  180. aSheetPathToTest.Path().AsString(), Path().AsString() );
  181. return true;
  182. }
  183. SCH_SHEET* SCH_SHEET_PATH::Last() const
  184. {
  185. if( !empty() )
  186. return m_sheets.back();
  187. return nullptr;
  188. }
  189. SCH_SCREEN* SCH_SHEET_PATH::LastScreen()
  190. {
  191. SCH_SHEET* lastSheet = Last();
  192. if( lastSheet )
  193. return lastSheet->GetScreen();
  194. return nullptr;
  195. }
  196. SCH_SCREEN* SCH_SHEET_PATH::LastScreen() const
  197. {
  198. SCH_SHEET* lastSheet = Last();
  199. if( lastSheet )
  200. return lastSheet->GetScreen();
  201. return nullptr;
  202. }
  203. bool SCH_SHEET_PATH::GetExcludedFromSim() const
  204. {
  205. for( SCH_SHEET* sheet : m_sheets )
  206. {
  207. if( sheet->GetExcludedFromSim() )
  208. return true;
  209. }
  210. return false;
  211. }
  212. bool SCH_SHEET_PATH::GetExcludedFromBOM() const
  213. {
  214. for( SCH_SHEET* sheet : m_sheets )
  215. {
  216. if( sheet->GetExcludedFromBOM() )
  217. return true;
  218. }
  219. return false;
  220. }
  221. bool SCH_SHEET_PATH::GetExcludedFromBoard() const
  222. {
  223. for( SCH_SHEET* sheet : m_sheets )
  224. {
  225. if( sheet->GetExcludedFromBoard() )
  226. return true;
  227. }
  228. return false;
  229. }
  230. bool SCH_SHEET_PATH::GetDNP() const
  231. {
  232. for( SCH_SHEET* sheet : m_sheets )
  233. {
  234. if( sheet->GetDNP() )
  235. return true;
  236. }
  237. return false;
  238. }
  239. wxString SCH_SHEET_PATH::PathAsString() const
  240. {
  241. wxString s;
  242. s = wxT( "/" ); // This is the root path
  243. // Start at 1 to avoid the root sheet, which does not need to be added to the path.
  244. // Its timestamp changes anyway.
  245. for( unsigned i = 1; i < size(); i++ )
  246. s += at( i )->m_Uuid.AsString() + "/";
  247. return s;
  248. }
  249. KIID_PATH SCH_SHEET_PATH::Path() const
  250. {
  251. KIID_PATH path;
  252. path.reserve( m_sheets.size() );
  253. for( const SCH_SHEET* sheet : m_sheets )
  254. path.push_back( sheet->m_Uuid );
  255. return path;
  256. }
  257. wxString SCH_SHEET_PATH::PathHumanReadable( bool aUseShortRootName,
  258. bool aStripTrailingSeparator ) const
  259. {
  260. wxString s;
  261. if( aUseShortRootName )
  262. {
  263. s = wxS( "/" ); // Use only the short name in netlists
  264. }
  265. else
  266. {
  267. wxString fileName;
  268. if( !empty() && at( 0 )->GetScreen() )
  269. fileName = at( 0 )->GetScreen()->GetFileName();
  270. wxFileName fn = fileName;
  271. s = fn.GetName() + wxS( "/" );
  272. }
  273. // Start at 1 since we've already processed the root sheet.
  274. for( unsigned i = 1; i < size(); i++ )
  275. s << at( i )->GetField( FIELD_T::SHEET_NAME )->GetShownText( false ) << wxS( "/" );
  276. if( aStripTrailingSeparator && s.EndsWith( "/" ) )
  277. s = s.Left( s.length() - 1 );
  278. return s;
  279. }
  280. void SCH_SHEET_PATH::UpdateAllScreenReferences() const
  281. {
  282. std::vector<SCH_ITEM*> items;
  283. std::copy_if( LastScreen()->Items().begin(), LastScreen()->Items().end(),
  284. std::back_inserter( items ),
  285. []( SCH_ITEM* aItem )
  286. {
  287. return ( aItem->Type() == SCH_SYMBOL_T
  288. || aItem->Type() == SCH_GLOBAL_LABEL_T
  289. || aItem->Type() == SCH_SHAPE_T );
  290. } );
  291. for( SCH_ITEM* item : items )
  292. {
  293. if( item->Type() == SCH_SYMBOL_T )
  294. {
  295. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  296. symbol->GetField( FIELD_T::REFERENCE )->SetText( symbol->GetRef( this ) );
  297. symbol->SetUnit( symbol->GetUnitSelection( this ) );
  298. LastScreen()->Update( item, false );
  299. }
  300. else if( item->Type() == SCH_GLOBAL_LABEL_T )
  301. {
  302. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( item );
  303. if( label->GetFields().size() > 0 ) // Possible when reading a legacy .sch schematic
  304. {
  305. SCH_FIELD* intersheetRefs = label->GetField( FIELD_T::INTERSHEET_REFS );
  306. // Fixup for legacy files which didn't store a position for the intersheet refs
  307. // unless they were shown.
  308. if( intersheetRefs->GetPosition() == VECTOR2I() && !intersheetRefs->IsVisible() )
  309. label->AutoplaceFields( LastScreen(), AUTOPLACE_AUTO );
  310. intersheetRefs->SetVisible( label->Schematic()->Settings().m_IntersheetRefsShow );
  311. LastScreen()->Update( intersheetRefs );
  312. }
  313. }
  314. else if( item->Type() == SCH_SHAPE_T )
  315. {
  316. SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
  317. shape->UpdateHatching();
  318. }
  319. }
  320. }
  321. void SCH_SHEET_PATH::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
  322. bool aForceIncludeOrphanSymbols ) const
  323. {
  324. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  325. {
  326. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  327. AppendSymbol( aReferences, symbol, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
  328. }
  329. }
  330. void SCH_SHEET_PATH::AppendSymbol( SCH_REFERENCE_LIST& aReferences, SCH_SYMBOL* aSymbol,
  331. bool aIncludePowerSymbols,
  332. bool aForceIncludeOrphanSymbols ) const
  333. {
  334. // Skip pseudo-symbols, which have a reference starting with #. This mainly
  335. // affects power symbols.
  336. if( aIncludePowerSymbols || aSymbol->GetRef( this )[0] != wxT( '#' ) )
  337. {
  338. if( aSymbol->GetLibSymbolRef() || aForceIncludeOrphanSymbols )
  339. {
  340. SCH_REFERENCE schReference( aSymbol, *this );
  341. schReference.SetSheetNumber( m_virtualPageNumber );
  342. aReferences.AddItem( schReference );
  343. }
  344. }
  345. }
  346. void SCH_SHEET_PATH::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList,
  347. bool aIncludePowerSymbols ) const
  348. {
  349. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  350. {
  351. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  352. AppendMultiUnitSymbol( aRefList, symbol, aIncludePowerSymbols );
  353. }
  354. }
  355. void SCH_SHEET_PATH::AppendMultiUnitSymbol( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList,
  356. SCH_SYMBOL* aSymbol,
  357. bool aIncludePowerSymbols ) const
  358. {
  359. // Skip pseudo-symbols, which have a reference starting with #. This mainly
  360. // affects power symbols.
  361. if( !aIncludePowerSymbols && aSymbol->GetRef( this )[0] == wxT( '#' ) )
  362. return;
  363. LIB_SYMBOL* symbol = aSymbol->GetLibSymbolRef().get();
  364. if( symbol && symbol->GetUnitCount() > 1 )
  365. {
  366. SCH_REFERENCE schReference = SCH_REFERENCE( aSymbol, *this );
  367. schReference.SetSheetNumber( m_virtualPageNumber );
  368. wxString reference_str = schReference.GetRef();
  369. // Never lock unassigned references
  370. if( reference_str[reference_str.Len() - 1] == '?' )
  371. return;
  372. aRefList[reference_str].AddItem( schReference );
  373. }
  374. }
  375. bool SCH_SHEET_PATH::operator==( const SCH_SHEET_PATH& d1 ) const
  376. {
  377. return m_current_hash == d1.GetCurrentHash();
  378. }
  379. bool SCH_SHEET_PATH::TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName )
  380. {
  381. auto pair = std::make_pair( aSrcFileName, aDestFileName );
  382. if( m_recursion_test_cache.count( pair ) )
  383. return m_recursion_test_cache.at( pair );
  384. SCHEMATIC* sch = LastScreen()->Schematic();
  385. wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_PATH::TestForRecursion!" );
  386. wxFileName rootFn = sch->GetFileName();
  387. wxFileName srcFn = aSrcFileName;
  388. wxFileName destFn = aDestFileName;
  389. if( srcFn.IsRelative() )
  390. srcFn.MakeAbsolute( rootFn.GetPath() );
  391. if( destFn.IsRelative() )
  392. destFn.MakeAbsolute( rootFn.GetPath() );
  393. // The source and destination sheet file names cannot be the same.
  394. if( srcFn == destFn )
  395. {
  396. m_recursion_test_cache[pair] = true;
  397. return true;
  398. }
  399. /// @todo Store sheet file names with full path, either relative to project path
  400. /// or absolute path. The current design always assumes subsheet files are
  401. /// located in the project folder which may or may not be desirable.
  402. unsigned i = 0;
  403. while( i < size() )
  404. {
  405. wxFileName cmpFn = at( i )->GetFileName();
  406. if( cmpFn.IsRelative() )
  407. cmpFn.MakeAbsolute( rootFn.GetPath() );
  408. // Test if the file name of the destination sheet is in anywhere in this sheet path.
  409. if( cmpFn == destFn )
  410. break;
  411. i++;
  412. }
  413. // The destination sheet file name was not found in the sheet path or the destination
  414. // sheet file name is the root sheet so no recursion is possible.
  415. if( i >= size() || i == 0 )
  416. {
  417. m_recursion_test_cache[pair] = false;
  418. return false;
  419. }
  420. // Walk back up to the root sheet to see if the source file name is already a parent in
  421. // the sheet path. If so, recursion will occur.
  422. do
  423. {
  424. i -= 1;
  425. wxFileName cmpFn = at( i )->GetFileName();
  426. if( cmpFn.IsRelative() )
  427. cmpFn.MakeAbsolute( rootFn.GetPath() );
  428. if( cmpFn == srcFn )
  429. {
  430. m_recursion_test_cache[pair] = true;
  431. return true;
  432. }
  433. } while( i != 0 );
  434. // The source sheet file name is not a parent of the destination sheet file name.
  435. m_recursion_test_cache[pair] = false;
  436. return false;
  437. }
  438. wxString SCH_SHEET_PATH::GetPageNumber() const
  439. {
  440. SCH_SHEET* sheet = Last();
  441. wxCHECK( sheet, wxEmptyString );
  442. KIID_PATH tmpPath = Path();
  443. tmpPath.pop_back();
  444. return sheet->getPageNumber( tmpPath );
  445. }
  446. void SCH_SHEET_PATH::SetPageNumber( const wxString& aPageNumber )
  447. {
  448. SCH_SHEET* sheet = Last();
  449. wxCHECK( sheet, /* void */ );
  450. KIID_PATH tmpPath = Path();
  451. tmpPath.pop_back();
  452. sheet->addInstance( tmpPath );
  453. sheet->setPageNumber( tmpPath, aPageNumber );
  454. }
  455. void SCH_SHEET_PATH::AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath,
  456. const wxString& aProjectName )
  457. {
  458. wxCHECK( !aProjectName.IsEmpty(), /* void */ );
  459. SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
  460. SCH_SHEET_PATH currentSheetPath( *this );
  461. // Prefix the new hierarchical path.
  462. newSheetPath = newSheetPath + currentSheetPath;
  463. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  464. {
  465. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  466. wxCHECK2( symbol, continue );
  467. SCH_SYMBOL_INSTANCE newSymbolInstance;
  468. if( symbol->GetInstance( newSymbolInstance, Path(), true ) )
  469. {
  470. newSymbolInstance.m_ProjectName = aProjectName;
  471. // Use an existing symbol instance for this path if it exists.
  472. newSymbolInstance.m_Path = newSheetPath.Path();
  473. symbol->AddHierarchicalReference( newSymbolInstance );
  474. }
  475. else if( !symbol->GetInstances().empty() )
  476. {
  477. newSymbolInstance.m_ProjectName = aProjectName;
  478. // Use the first symbol instance if any symbol instance data exists.
  479. newSymbolInstance = symbol->GetInstances()[0];
  480. newSymbolInstance.m_Path = newSheetPath.Path();
  481. symbol->AddHierarchicalReference( newSymbolInstance );
  482. }
  483. else
  484. {
  485. newSymbolInstance.m_ProjectName = aProjectName;
  486. // Fall back to the last saved symbol field and unit settings if there is no
  487. // instance data.
  488. newSymbolInstance.m_Path = newSheetPath.Path();
  489. newSymbolInstance.m_Reference = symbol->GetField( FIELD_T::REFERENCE )->GetText();
  490. newSymbolInstance.m_Unit = symbol->GetUnit();
  491. symbol->AddHierarchicalReference( newSymbolInstance );
  492. }
  493. }
  494. }
  495. void SCH_SHEET_PATH::RemoveSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath )
  496. {
  497. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  498. {
  499. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  500. wxCHECK2( symbol, continue );
  501. SCH_SHEET_PATH fullSheetPath( aPrefixSheetPath );
  502. SCH_SHEET_PATH currentSheetPath( *this );
  503. // Prefix the hierarchical path of the symbol instance to be removed.
  504. fullSheetPath = fullSheetPath + currentSheetPath;
  505. symbol->RemoveInstance( fullSheetPath );
  506. }
  507. }
  508. void SCH_SHEET_PATH::CheckForMissingSymbolInstances( const wxString& aProjectName )
  509. {
  510. wxCHECK( !aProjectName.IsEmpty() && LastScreen(), /* void */ );
  511. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  512. {
  513. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  514. wxCHECK2( symbol, continue );
  515. SCH_SYMBOL_INSTANCE symbolInstance;
  516. if( !symbol->GetInstance( symbolInstance, Path() ) )
  517. {
  518. wxLogTrace( traceSchSheetPaths, "Adding missing symbol \"%s\" instance data for "
  519. "sheet path '%s'.",
  520. symbol->m_Uuid.AsString(), PathHumanReadable( false ) );
  521. // Legacy schematics that are not shared do not contain separate instance data.
  522. // The symbol reference and unit are saved in the reference field and unit entries.
  523. if( ( LastScreen()->GetRefCount() <= 1 ) &&
  524. ( LastScreen()->GetFileFormatVersionAtLoad() <= 20200310 ) )
  525. {
  526. SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
  527. symbolInstance.m_Reference = refField->GetShownText( this, true );
  528. symbolInstance.m_Unit = symbol->GetUnit();
  529. }
  530. else
  531. {
  532. // When schematics are shared, we cannot know which instance the current symbol
  533. // reference field and unit belong to. In this case, we clear the reference
  534. // annotation and set the unit to 1.
  535. symbolInstance.m_Reference = UTIL::GetRefDesUnannotated( symbol->GetPrefix() );
  536. }
  537. symbolInstance.m_ProjectName = aProjectName;
  538. symbolInstance.m_Path = Path();
  539. symbol->AddHierarchicalReference( symbolInstance );
  540. }
  541. }
  542. }
  543. void SCH_SHEET_PATH::MakeFilePathRelativeToParentSheet()
  544. {
  545. wxCHECK( m_sheets.size() > 1, /* void */ );
  546. wxFileName sheetFileName = Last()->GetFileName();
  547. // If the sheet file name is absolute, then the user requested is so don't make it relative.
  548. if( sheetFileName.IsAbsolute() )
  549. return;
  550. SCH_SCREEN* screen = LastScreen();
  551. SCH_SCREEN* parentScreen = m_sheets[ m_sheets.size() - 2 ]->GetScreen();
  552. wxCHECK( screen && parentScreen, /* void */ );
  553. wxFileName fileName = screen->GetFileName();
  554. wxFileName parentFileName = parentScreen->GetFileName();
  555. // SCH_SCREEN file names must be absolute. If they are not, someone set them incorrectly
  556. // on load or on creation.
  557. wxCHECK( fileName.IsAbsolute() && parentFileName.IsAbsolute(), /* void */ );
  558. if( fileName.GetPath() == parentFileName.GetPath() )
  559. {
  560. Last()->SetFileName( fileName.GetFullName() );
  561. }
  562. else if( fileName.MakeRelativeTo( parentFileName.GetPath() ) )
  563. {
  564. Last()->SetFileName( fileName.GetFullPath() );
  565. }
  566. else
  567. {
  568. Last()->SetFileName( screen->GetFileName() );
  569. }
  570. wxLogTrace( tracePathsAndFiles,
  571. wxT( "\n File name: '%s'"
  572. "\n parent file name '%s',"
  573. "\n sheet '%s' file name '%s'." ),
  574. screen->GetFileName(), parentScreen->GetFileName(), PathHumanReadable(),
  575. Last()->GetFileName() );
  576. }
  577. SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet )
  578. {
  579. if( aSheet != nullptr )
  580. BuildSheetList( aSheet, false );
  581. }
  582. void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
  583. {
  584. if( !aSheet )
  585. return;
  586. std::vector<SCH_SHEET*> badSheets;
  587. m_currentSheetPath.push_back( aSheet );
  588. m_currentSheetPath.SetVirtualPageNumber( static_cast<int>( size() ) + 1 );
  589. push_back( m_currentSheetPath );
  590. if( m_currentSheetPath.LastScreen() )
  591. {
  592. wxString parentFileName = aSheet->GetFileName();
  593. std::vector<SCH_ITEM*> childSheets;
  594. m_currentSheetPath.LastScreen()->GetSheets( &childSheets );
  595. for( SCH_ITEM* item : childSheets )
  596. {
  597. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  598. if( aCheckIntegrity )
  599. {
  600. if( !m_currentSheetPath.TestForRecursion( sheet->GetFileName(), parentFileName ) )
  601. BuildSheetList( sheet, true );
  602. else
  603. badSheets.push_back( sheet );
  604. }
  605. else
  606. {
  607. // If we are not performing a full recursion test, at least check if we are in
  608. // a simple recursion scenario to prevent stack overflow crashes
  609. wxCHECK2_MSG( sheet->GetFileName() != aSheet->GetFileName(), continue,
  610. wxT( "Recursion prevented in SCH_SHEET_LIST::BuildSheetList" ) );
  611. BuildSheetList( sheet, false );
  612. }
  613. }
  614. }
  615. if( aCheckIntegrity )
  616. {
  617. for( SCH_SHEET* sheet : badSheets )
  618. {
  619. m_currentSheetPath.LastScreen()->Remove( sheet );
  620. m_currentSheetPath.LastScreen()->SetContentModified();
  621. }
  622. }
  623. m_currentSheetPath.pop_back();
  624. }
  625. void SCH_SHEET_LIST::SortByHierarchicalPageNumbers( bool aUpdateVirtualPageNums )
  626. {
  627. for( const SCH_SHEET_PATH& path : *this )
  628. path.CachePageNumber();
  629. std::sort( begin(), end(),
  630. []( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b ) -> bool
  631. {
  632. // Find the divergence point in the paths
  633. size_t common_len = 0;
  634. size_t min_len = std::min( a.size(), b.size() );
  635. while( common_len < min_len && a.at( common_len )->m_Uuid == b.at( common_len )->m_Uuid )
  636. common_len++;
  637. // If one path is a prefix of the other, the shorter one comes first
  638. // This ensures parents come before children
  639. if( common_len == a.size() )
  640. return true; // a is a prefix of b - a is the parent
  641. if( common_len == b.size() )
  642. return false; // b is a prefix of a - b is the parent
  643. // Paths diverge at common_len
  644. // If they share the same parent, sort by page number
  645. // This ensures siblings are sorted by page number
  646. SCH_SHEET* sheet_a = a.at( common_len );
  647. SCH_SHEET* sheet_b = b.at( common_len );
  648. // Create partial paths to get to these sheets for page number comparison
  649. KIID_PATH path_a, path_b;
  650. for( size_t i = 0; i <= common_len; i++ )
  651. {
  652. path_a.push_back( a.at( i )->m_Uuid );
  653. path_b.push_back( b.at( i )->m_Uuid );
  654. }
  655. // Compare page numbers - use the last sheet's page number
  656. wxString page_a = sheet_a->getPageNumber( path_a );
  657. wxString page_b = sheet_b->getPageNumber( path_b );
  658. int retval = SCH_SHEET::ComparePageNum( page_a, page_b );
  659. if( retval != 0 )
  660. return retval < 0;
  661. // If page numbers are the same, use virtual page numbers as a tie-breaker
  662. if( a.GetVirtualPageNumber() < b.GetVirtualPageNumber() )
  663. return true;
  664. else if( a.GetVirtualPageNumber() > b.GetVirtualPageNumber() )
  665. return false;
  666. // Finally, use UUIDs for stable ordering when everything else is equal
  667. return a.GetCurrentHash() < b.GetCurrentHash();
  668. } );
  669. if( aUpdateVirtualPageNums )
  670. {
  671. int virtualPageNum = 1;
  672. for( SCH_SHEET_PATH& sheet : *this )
  673. sheet.SetVirtualPageNumber( virtualPageNum++ );
  674. }
  675. }
  676. void SCH_SHEET_LIST::SortByPageNumbers( bool aUpdateVirtualPageNums )
  677. {
  678. for( const SCH_SHEET_PATH& path : *this )
  679. path.CachePageNumber();
  680. std::sort( begin(), end(),
  681. []( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b ) -> bool
  682. {
  683. int retval = SCH_SHEET::ComparePageNum( a.GetCachedPageNumber(),
  684. b.GetCachedPageNumber() );
  685. if( retval < 0 )
  686. return true;
  687. else if( retval > 0 )
  688. return false;
  689. if( a.GetVirtualPageNumber() < b.GetVirtualPageNumber() )
  690. return true;
  691. else if( a.GetVirtualPageNumber() > b.GetVirtualPageNumber() )
  692. return false;
  693. // Enforce strict ordering. If the page numbers are the same, use UUIDs
  694. return a.GetCurrentHash() < b.GetCurrentHash();
  695. } );
  696. if( aUpdateVirtualPageNums )
  697. {
  698. int virtualPageNum = 1;
  699. for( SCH_SHEET_PATH& sheet : *this )
  700. sheet.SetVirtualPageNumber( virtualPageNum++ );
  701. }
  702. }
  703. bool SCH_SHEET_LIST::NameExists( const wxString& aSheetName ) const
  704. {
  705. for( const SCH_SHEET_PATH& sheet : *this )
  706. {
  707. if( sheet.Last()->GetName() == aSheetName )
  708. return true;
  709. }
  710. return false;
  711. }
  712. bool SCH_SHEET_LIST::PageNumberExists( const wxString& aPageNumber ) const
  713. {
  714. for( const SCH_SHEET_PATH& sheet : *this )
  715. {
  716. if( sheet.GetPageNumber() == aPageNumber )
  717. return true;
  718. }
  719. return false;
  720. }
  721. void SCH_SHEET_LIST::TrimToPageNumbers( const std::vector<wxString>& aPageInclusions )
  722. {
  723. auto it = std::remove_if( begin(), end(),
  724. [&]( SCH_SHEET_PATH sheet )
  725. {
  726. return std::find( aPageInclusions.begin(), aPageInclusions.end(),
  727. sheet.GetPageNumber() )
  728. == aPageInclusions.end();
  729. } );
  730. erase( it, end() );
  731. }
  732. bool SCH_SHEET_LIST::IsModified() const
  733. {
  734. for( const SCH_SHEET_PATH& sheet : *this )
  735. {
  736. if( sheet.LastScreen() && sheet.LastScreen()->IsContentModified() )
  737. return true;
  738. }
  739. return false;
  740. }
  741. void SCH_SHEET_LIST::ClearModifyStatus()
  742. {
  743. for( const SCH_SHEET_PATH& sheet : *this )
  744. {
  745. if( sheet.LastScreen() )
  746. sheet.LastScreen()->SetContentModified( false );
  747. }
  748. }
  749. SCH_ITEM* SCH_SHEET_LIST::ResolveItem( const KIID& aID, SCH_SHEET_PATH* aPathOut, bool aAllowNullptrReturn ) const
  750. {
  751. for( const SCH_SHEET_PATH& sheet : *this )
  752. {
  753. SCH_ITEM* item = sheet.ResolveItem( aID );
  754. if( item )
  755. {
  756. if( aPathOut )
  757. *aPathOut = sheet;
  758. return item;
  759. }
  760. }
  761. // Not found; weak reference has been deleted.
  762. if( aAllowNullptrReturn )
  763. return nullptr;
  764. else
  765. return DELETED_SHEET_ITEM::GetInstance();
  766. }
  767. SCH_ITEM* SCH_SHEET_PATH::ResolveItem( const KIID& aID ) const
  768. {
  769. for( SCH_ITEM* aItem : LastScreen()->Items() )
  770. {
  771. if( aItem->m_Uuid == aID )
  772. return aItem;
  773. SCH_ITEM* childMatch = nullptr;
  774. aItem->RunOnChildren(
  775. [&]( SCH_ITEM* aChild )
  776. {
  777. if( aChild->m_Uuid == aID )
  778. childMatch = aChild;
  779. },
  780. RECURSE_MODE::NO_RECURSE );
  781. if( childMatch )
  782. return childMatch;
  783. }
  784. return nullptr;
  785. }
  786. void SCH_SHEET_LIST::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
  787. {
  788. for( const SCH_SHEET_PATH& sheet : *this )
  789. {
  790. SCH_SCREEN* screen = sheet.LastScreen();
  791. for( SCH_ITEM* aItem : screen->Items() )
  792. {
  793. aMap[ aItem->m_Uuid ] = aItem;
  794. aItem->RunOnChildren(
  795. [&]( SCH_ITEM* aChild )
  796. {
  797. aMap[ aChild->m_Uuid ] = aChild;
  798. },
  799. RECURSE_MODE::NO_RECURSE );
  800. }
  801. }
  802. }
  803. void SCH_SHEET_LIST::AnnotatePowerSymbols()
  804. {
  805. // List of reference for power symbols
  806. SCH_REFERENCE_LIST references;
  807. SCH_REFERENCE_LIST additionalreferences; // Todo: add as a parameter to this function
  808. // Map of locked symbols (not used, but needed by Annotate()
  809. SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
  810. // Build the list of power symbols:
  811. for( SCH_SHEET_PATH& sheet : *this )
  812. {
  813. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  814. {
  815. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  816. LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
  817. if( libSymbol && libSymbol->IsPower() )
  818. {
  819. SCH_REFERENCE schReference( symbol, sheet );
  820. references.AddItem( schReference );
  821. }
  822. }
  823. }
  824. // Find duplicate, and silently clear annotation of duplicate
  825. std::map<wxString, int> ref_list; // stores the existing references
  826. for( unsigned ii = 0; ii< references.GetCount(); ++ii )
  827. {
  828. wxString curr_ref = references[ii].GetRef();
  829. if( ref_list.find( curr_ref ) == ref_list.end() )
  830. {
  831. ref_list[curr_ref] = ii;
  832. continue;
  833. }
  834. // Possible duplicate, if the ref ends by a number:
  835. if( curr_ref.Last() < '0' && curr_ref.Last() > '9' )
  836. continue; // not annotated
  837. // Duplicate: clear annotation by removing the number ending the ref
  838. while( curr_ref.Last() >= '0' && curr_ref.Last() <= '9' )
  839. curr_ref.RemoveLast();
  840. references[ii].SetRef( curr_ref );
  841. }
  842. // Break full symbol reference into name (prefix) and number:
  843. // example: IC1 become IC, and 1
  844. references.SplitReferences();
  845. // Ensure all power symbols have the reference starting by '#'
  846. // (Not sure this is really useful)
  847. for( unsigned ii = 0; ii< references.GetCount(); ++ii )
  848. {
  849. if( references[ii].GetRef()[0] != '#' )
  850. {
  851. wxString new_ref = "#" + references[ii].GetRef();
  852. references[ii].SetRef( new_ref );
  853. }
  854. }
  855. // Recalculate and update reference numbers in schematic
  856. references.Annotate( false, 0, 100, lockedSymbols, additionalreferences );
  857. references.UpdateAnnotation();
  858. }
  859. void SCH_SHEET_LIST::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
  860. bool aForceIncludeOrphanSymbols ) const
  861. {
  862. for( const SCH_SHEET_PATH& sheet : *this )
  863. sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
  864. }
  865. void SCH_SHEET_LIST::GetSymbolsWithinPath( SCH_REFERENCE_LIST& aReferences,
  866. const SCH_SHEET_PATH& aSheetPath,
  867. bool aIncludePowerSymbols,
  868. bool aForceIncludeOrphanSymbols ) const
  869. {
  870. for( const SCH_SHEET_PATH& sheet : *this )
  871. {
  872. if( sheet.IsContainedWithin( aSheetPath ) )
  873. sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
  874. }
  875. }
  876. void SCH_SHEET_LIST::GetSheetsWithinPath( std::vector<SCH_SHEET_PATH>& aSheets,
  877. const SCH_SHEET_PATH& aSheetPath ) const
  878. {
  879. for( const SCH_SHEET_PATH& sheet : *this )
  880. {
  881. if( sheet.IsContainedWithin( aSheetPath ) )
  882. aSheets.push_back( sheet );
  883. }
  884. }
  885. std::optional<SCH_SHEET_PATH> SCH_SHEET_LIST::GetSheetPathByKIIDPath( const KIID_PATH& aPath,
  886. bool aIncludeLastSheet ) const
  887. {
  888. for( const SCH_SHEET_PATH& sheet : *this )
  889. {
  890. KIID_PATH testPath = sheet.Path();
  891. if( !aIncludeLastSheet )
  892. testPath.pop_back();
  893. if( testPath == aPath )
  894. return SCH_SHEET_PATH( sheet );
  895. }
  896. return std::nullopt;
  897. }
  898. void SCH_SHEET_LIST::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
  899. bool aIncludePowerSymbols ) const
  900. {
  901. for( auto it = begin(); it != end(); ++it )
  902. {
  903. SCH_MULTI_UNIT_REFERENCE_MAP tempMap;
  904. ( *it ).GetMultiUnitSymbols( tempMap, aIncludePowerSymbols );
  905. for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : tempMap )
  906. {
  907. // Merge this list into the main one
  908. unsigned n_refs = pair.second.GetCount();
  909. for( unsigned thisRef = 0; thisRef < n_refs; ++thisRef )
  910. aRefList[pair.first].AddItem( pair.second[thisRef] );
  911. }
  912. }
  913. }
  914. bool SCH_SHEET_LIST::TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
  915. const wxString& aDestFileName )
  916. {
  917. if( empty() )
  918. return false;
  919. SCHEMATIC* sch = at( 0 ).LastScreen()->Schematic();
  920. wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_LIST::TestForRecursion!" );
  921. wxFileName rootFn = sch->GetFileName();
  922. wxFileName destFn = aDestFileName;
  923. if( destFn.IsRelative() )
  924. destFn.MakeAbsolute( rootFn.GetPath() );
  925. // Test each SCH_SHEET_PATH in this SCH_SHEET_LIST for potential recursion.
  926. for( unsigned i = 0; i < size(); i++ )
  927. {
  928. // Test each SCH_SHEET_PATH in the source sheet.
  929. for( unsigned j = 0; j < aSrcSheetHierarchy.size(); j++ )
  930. {
  931. const SCH_SHEET_PATH* sheetPath = &aSrcSheetHierarchy[j];
  932. for( unsigned k = 0; k < sheetPath->size(); k++ )
  933. {
  934. if( at( i ).TestForRecursion( sheetPath->GetSheet( k )->GetFileName(),
  935. aDestFileName ) )
  936. {
  937. return true;
  938. }
  939. }
  940. }
  941. }
  942. // The source sheet file can safely be added to the destination sheet file.
  943. return false;
  944. }
  945. SCH_SHEET_PATH* SCH_SHEET_LIST::FindSheetForPath( const SCH_SHEET_PATH* aPath )
  946. {
  947. for( SCH_SHEET_PATH& path : *this )
  948. {
  949. if( path.Path() == aPath->Path() )
  950. return &path;
  951. }
  952. return nullptr;
  953. }
  954. SCH_SHEET_PATH SCH_SHEET_LIST::FindSheetForScreen( const SCH_SCREEN* aScreen )
  955. {
  956. for( SCH_SHEET_PATH& sheetpath : *this )
  957. {
  958. if( sheetpath.LastScreen() == aScreen )
  959. return sheetpath;
  960. }
  961. return SCH_SHEET_PATH();
  962. }
  963. SCH_SHEET_LIST SCH_SHEET_LIST::FindAllSheetsForScreen( const SCH_SCREEN* aScreen ) const
  964. {
  965. SCH_SHEET_LIST retval;
  966. for( const SCH_SHEET_PATH& sheetpath : *this )
  967. {
  968. if( sheetpath.LastScreen() == aScreen )
  969. retval.push_back( sheetpath );
  970. }
  971. return retval;
  972. }
  973. void SCH_SHEET_LIST::UpdateSymbolInstanceData(
  974. const std::vector<SCH_SYMBOL_INSTANCE>& aSymbolInstances )
  975. {
  976. for( SCH_SHEET_PATH& sheetPath : *this )
  977. {
  978. for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  979. {
  980. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  981. wxCHECK2( symbol, continue );
  982. KIID_PATH sheetPathWithSymbolUuid = sheetPath.Path();
  983. sheetPathWithSymbolUuid.push_back( symbol->m_Uuid );
  984. auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
  985. [ sheetPathWithSymbolUuid ]( const SCH_SYMBOL_INSTANCE& r ) -> bool
  986. {
  987. return sheetPathWithSymbolUuid == r.m_Path;
  988. } );
  989. if( it == aSymbolInstances.end() )
  990. {
  991. wxLogTrace( traceSchSheetPaths, "No symbol instance found for symbol '%s'",
  992. sheetPathWithSymbolUuid.AsString() );
  993. continue;
  994. }
  995. // Symbol instance paths are stored and looked up in memory with the root path so use
  996. // the full path here.
  997. symbol->AddHierarchicalReference( sheetPath.Path(), it->m_Reference, it->m_Unit );
  998. symbol->GetField( FIELD_T::REFERENCE )->SetText( it->m_Reference );
  999. if( !it->m_Value.IsEmpty() )
  1000. symbol->SetValueFieldText( it->m_Value );
  1001. if( !it->m_Footprint.IsEmpty() )
  1002. symbol->SetFootprintFieldText( it->m_Footprint );
  1003. symbol->UpdatePrefix();
  1004. }
  1005. }
  1006. }
  1007. void SCH_SHEET_LIST::UpdateSheetInstanceData( const std::vector<SCH_SHEET_INSTANCE>& aSheetInstances )
  1008. {
  1009. for( SCH_SHEET_PATH& path : *this )
  1010. {
  1011. SCH_SHEET* sheet = path.Last();
  1012. wxCHECK2( sheet && path.Last(), continue );
  1013. auto it = std::find_if( aSheetInstances.begin(), aSheetInstances.end(),
  1014. [ path ]( const SCH_SHEET_INSTANCE& r ) -> bool
  1015. {
  1016. return path.Path() == r.m_Path;
  1017. } );
  1018. if( it == aSheetInstances.end() )
  1019. {
  1020. wxLogTrace( traceSchSheetPaths, "No sheet instance found for path '%s'",
  1021. path.Path().AsString() );
  1022. continue;
  1023. }
  1024. wxLogTrace( traceSchSheetPaths, "Setting sheet '%s' instance '%s' page number '%s'",
  1025. ( sheet->GetName().IsEmpty() ) ? wxString( wxT( "root" ) ) : sheet->GetName(),
  1026. path.Path().AsString(), it->m_PageNumber );
  1027. path.SetPageNumber( it->m_PageNumber );
  1028. }
  1029. }
  1030. std::vector<KIID_PATH> SCH_SHEET_LIST::GetPaths() const
  1031. {
  1032. std::vector<KIID_PATH> paths;
  1033. for( const SCH_SHEET_PATH& sheetPath : *this )
  1034. paths.emplace_back( sheetPath.Path() );
  1035. return paths;
  1036. }
  1037. std::vector<SCH_SHEET_INSTANCE> SCH_SHEET_LIST::GetSheetInstances() const
  1038. {
  1039. std::vector<SCH_SHEET_INSTANCE> retval;
  1040. for( const SCH_SHEET_PATH& path : *this )
  1041. {
  1042. const SCH_SHEET* sheet = path.Last();
  1043. wxCHECK2( sheet, continue );
  1044. SCH_SHEET_INSTANCE instance;
  1045. SCH_SHEET_PATH tmpPath = path;
  1046. tmpPath.pop_back();
  1047. instance.m_Path = tmpPath.Path();
  1048. instance.m_PageNumber = path.GetPageNumber();
  1049. retval.push_back( std::move( instance ) );
  1050. }
  1051. return retval;
  1052. }
  1053. bool SCH_SHEET_LIST::AllSheetPageNumbersEmpty() const
  1054. {
  1055. for( const SCH_SHEET_PATH& instance : *this )
  1056. {
  1057. if( !instance.GetPageNumber().IsEmpty() )
  1058. return false;
  1059. }
  1060. return true;
  1061. }
  1062. void SCH_SHEET_LIST::SetInitialPageNumbers()
  1063. {
  1064. // Don't accidentally renumber existing sheets.
  1065. wxCHECK( AllSheetPageNumbersEmpty(), /* void */ );
  1066. wxString tmp;
  1067. int pageNumber = 1;
  1068. for( SCH_SHEET_PATH& instance : *this )
  1069. {
  1070. tmp.Printf( "%d", pageNumber );
  1071. instance.SetPageNumber( tmp );
  1072. pageNumber += 1;
  1073. }
  1074. }
  1075. void SCH_SHEET_LIST::AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath,
  1076. const wxString& aProjectName )
  1077. {
  1078. for( SCH_SHEET_PATH& sheetPath : *this )
  1079. sheetPath.AddNewSymbolInstances( aPrefixSheetPath, aProjectName );
  1080. }
  1081. void SCH_SHEET_LIST::RemoveSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath )
  1082. {
  1083. for( SCH_SHEET_PATH& sheetPath : *this )
  1084. sheetPath.RemoveSymbolInstances( aPrefixSheetPath );
  1085. }
  1086. void SCH_SHEET_LIST::AddNewSheetInstances( const SCH_SHEET_PATH& aPrefixSheetPath,
  1087. int aLastVirtualPageNumber )
  1088. {
  1089. wxString pageNumber;
  1090. int lastUsedPageNumber = 1;
  1091. int nextVirtualPageNumber = aLastVirtualPageNumber;
  1092. // Fetch the list of page numbers already in use.
  1093. std::vector< wxString > usedPageNumbers;
  1094. if( aPrefixSheetPath.size() )
  1095. {
  1096. SCH_SHEET_LIST prefixHierarchy( aPrefixSheetPath.at( 0 ) );
  1097. for( const SCH_SHEET_PATH& path : prefixHierarchy )
  1098. {
  1099. pageNumber = path.GetPageNumber();
  1100. if( !pageNumber.IsEmpty() )
  1101. usedPageNumbers.emplace_back( pageNumber );
  1102. }
  1103. }
  1104. for( SCH_SHEET_PATH& sheetPath : *this )
  1105. {
  1106. KIID_PATH tmp = sheetPath.Path();
  1107. SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
  1108. // Prefix the new hierarchical path.
  1109. newSheetPath = newSheetPath + sheetPath;
  1110. // Sheets cannot have themselves in the path.
  1111. tmp.pop_back();
  1112. SCH_SHEET* sheet = sheetPath.Last();
  1113. wxCHECK2( sheet, continue );
  1114. nextVirtualPageNumber += 1;
  1115. SCH_SHEET_INSTANCE instance;
  1116. // Add the instance if it doesn't already exist
  1117. if( !sheet->getInstance( instance, tmp, true ) )
  1118. {
  1119. sheet->addInstance( tmp );
  1120. sheet->getInstance( instance, tmp, true );
  1121. }
  1122. // Get a new page number if we don't have one
  1123. if( instance.m_PageNumber.IsEmpty() )
  1124. {
  1125. // Generate the next available page number.
  1126. do
  1127. {
  1128. pageNumber.Printf( wxT( "%d" ), lastUsedPageNumber );
  1129. lastUsedPageNumber += 1;
  1130. } while( std::find( usedPageNumbers.begin(), usedPageNumbers.end(), pageNumber ) !=
  1131. usedPageNumbers.end() );
  1132. instance.m_PageNumber = pageNumber;
  1133. newSheetPath.SetVirtualPageNumber( nextVirtualPageNumber );
  1134. }
  1135. newSheetPath.SetPageNumber( instance.m_PageNumber );
  1136. usedPageNumbers.push_back( instance.m_PageNumber );
  1137. }
  1138. }
  1139. void SCH_SHEET_LIST::CheckForMissingSymbolInstances( const wxString& aProjectName )
  1140. {
  1141. for( SCH_SHEET_PATH& sheetPath : *this )
  1142. sheetPath.CheckForMissingSymbolInstances( aProjectName );
  1143. }
  1144. int SCH_SHEET_LIST::GetLastVirtualPageNumber() const
  1145. {
  1146. int lastVirtualPageNumber = 1;
  1147. for( const SCH_SHEET_PATH& sheetPath : *this )
  1148. {
  1149. if( sheetPath.GetVirtualPageNumber() > lastVirtualPageNumber )
  1150. lastVirtualPageNumber = sheetPath.GetVirtualPageNumber();
  1151. }
  1152. return lastVirtualPageNumber;
  1153. }
  1154. bool SCH_SHEET_LIST::HasPath( const KIID_PATH& aPath ) const
  1155. {
  1156. for( const SCH_SHEET_PATH& path : *this )
  1157. {
  1158. if( path.Path() == aPath )
  1159. return true;
  1160. }
  1161. return false;
  1162. }
  1163. bool SCH_SHEET_LIST::ContainsSheet( const SCH_SHEET* aSheet ) const
  1164. {
  1165. for( const SCH_SHEET_PATH& path : *this )
  1166. {
  1167. for( size_t i = 0; i < path.size(); i++ )
  1168. {
  1169. if( path.at( i ) == aSheet )
  1170. return true;
  1171. }
  1172. }
  1173. return false;
  1174. }
  1175. std::optional<SCH_SHEET_PATH> SCH_SHEET_LIST::GetOrdinalPath( const SCH_SCREEN* aScreen ) const
  1176. {
  1177. // Sheet paths with sheets that do not have a screen object are not valid.
  1178. if( !aScreen )
  1179. return std::nullopt;
  1180. for( const SCH_SHEET_PATH& path: *this )
  1181. {
  1182. if( path.LastScreen() == aScreen )
  1183. return std::optional<SCH_SHEET_PATH>( path );
  1184. }
  1185. return std::nullopt;
  1186. }