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.

1546 lines
44 KiB

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