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.

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