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.

1039 lines
28 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-2021 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <sch_screen.h>
  26. #include <sch_item.h>
  27. #include <sch_marker.h>
  28. #include <sch_reference_list.h>
  29. #include <symbol_library.h>
  30. #include <sch_sheet_path.h>
  31. #include <sch_symbol.h>
  32. #include <sch_sheet.h>
  33. #include <schematic.h>
  34. #include <template_fieldnames.h>
  35. #include <trace_helpers.h>
  36. #include <boost/functional/hash.hpp>
  37. #include <wx/filename.h>
  38. #include <wx/log.h>
  39. #include "erc_item.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 GetSelectMenuText( EDA_UNITS aUnits ) 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 wxPoint& ) override {}
  67. void Print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset ) override {}
  68. void Move( const wxPoint& aMoveVector ) override {}
  69. void MirrorHorizontally( int aCenter ) override {}
  70. void MirrorVertically( int aCenter ) override {}
  71. void Rotate( const wxPoint& aCenter ) override {}
  72. #if defined(DEBUG)
  73. void Show( int , std::ostream& ) const override {}
  74. #endif
  75. };
  76. namespace std
  77. {
  78. size_t hash<SCH_SHEET_PATH>::operator()( const SCH_SHEET_PATH& path ) const
  79. {
  80. return path.GetCurrentHash();
  81. }
  82. }
  83. SCH_SHEET_PATH::SCH_SHEET_PATH()
  84. {
  85. m_virtualPageNumber = 0;
  86. m_current_hash = 0;
  87. }
  88. SCH_SHEET_PATH::SCH_SHEET_PATH( const SCH_SHEET_PATH& aOther )
  89. {
  90. initFromOther( aOther );
  91. }
  92. SCH_SHEET_PATH& SCH_SHEET_PATH::operator=( const SCH_SHEET_PATH& aOther )
  93. {
  94. initFromOther( aOther );
  95. return *this;
  96. }
  97. void SCH_SHEET_PATH::initFromOther( const SCH_SHEET_PATH& aOther )
  98. {
  99. m_sheets = aOther.m_sheets;
  100. m_virtualPageNumber = aOther.m_virtualPageNumber;
  101. m_current_hash = aOther.m_current_hash;
  102. // Note: don't copy m_recursion_test_cache as it is slow and we want SCH_SHEET_PATHS to be
  103. // very fast to construct for use in the connectivity algorithm.
  104. }
  105. void SCH_SHEET_PATH::Rehash()
  106. {
  107. m_current_hash = 0;
  108. for( auto sheet : m_sheets )
  109. boost::hash_combine( m_current_hash, sheet->m_Uuid.Hash() );
  110. }
  111. int SCH_SHEET_PATH::Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const
  112. {
  113. if( size() > aSheetPathToTest.size() )
  114. return 1;
  115. if( size() < aSheetPathToTest.size() )
  116. return -1;
  117. //otherwise, same number of sheets.
  118. for( unsigned i = 0; i < size(); i++ )
  119. {
  120. if( at( i )->m_Uuid < aSheetPathToTest.at( i )->m_Uuid )
  121. return -1;
  122. if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
  123. return 1;
  124. }
  125. return 0;
  126. }
  127. int SCH_SHEET_PATH::ComparePageNumAndName( const SCH_SHEET_PATH& aSheetPathToTest ) const
  128. {
  129. wxString pageA = GetPageNumber();
  130. wxString pageB = aSheetPathToTest.GetPageNumber();
  131. int pageNumComp = SCH_SHEET::ComparePageNum( pageA, pageB );
  132. if( pageNumComp == 0 )
  133. {
  134. wxString nameA = Last()->GetName();
  135. wxString nameB = aSheetPathToTest.Last()->GetName();
  136. return nameA.Cmp( nameB );
  137. }
  138. else
  139. {
  140. return pageNumComp;
  141. }
  142. }
  143. bool SCH_SHEET_PATH::IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const
  144. {
  145. if( aSheetPathToTest.size() > size() )
  146. return false;
  147. for( size_t i = 0; i < aSheetPathToTest.size(); ++i )
  148. {
  149. if( at( i )->m_Uuid != aSheetPathToTest.at( i )->m_Uuid )
  150. return false;
  151. }
  152. return true;
  153. }
  154. SCH_SHEET* SCH_SHEET_PATH::Last() const
  155. {
  156. if( !empty() )
  157. return m_sheets.back();
  158. return nullptr;
  159. }
  160. SCH_SCREEN* SCH_SHEET_PATH::LastScreen()
  161. {
  162. SCH_SHEET* lastSheet = Last();
  163. if( lastSheet )
  164. return lastSheet->GetScreen();
  165. return nullptr;
  166. }
  167. SCH_SCREEN* SCH_SHEET_PATH::LastScreen() const
  168. {
  169. SCH_SHEET* lastSheet = Last();
  170. if( lastSheet )
  171. return lastSheet->GetScreen();
  172. return nullptr;
  173. }
  174. wxString SCH_SHEET_PATH::PathAsString() const
  175. {
  176. wxString s;
  177. s = wxT( "/" ); // This is the root path
  178. // Start at 1 to avoid the root sheet, which does not need to be added to the path.
  179. // Its timestamp changes anyway.
  180. for( unsigned i = 1; i < size(); i++ )
  181. s += at( i )->m_Uuid.AsString() + "/";
  182. return s;
  183. }
  184. KIID_PATH SCH_SHEET_PATH::Path() const
  185. {
  186. KIID_PATH path;
  187. for( const SCH_SHEET* sheet : m_sheets )
  188. path.push_back( sheet->m_Uuid );
  189. return path;
  190. }
  191. KIID_PATH SCH_SHEET_PATH::PathWithoutRootUuid() const
  192. {
  193. KIID_PATH path;
  194. for( size_t i = 1; i < size(); i++ )
  195. path.push_back( at( i )->m_Uuid );
  196. return path;
  197. }
  198. wxString SCH_SHEET_PATH::PathHumanReadable( bool aUseShortRootName ) const
  199. {
  200. wxString s;
  201. wxString fileName;
  202. if( !empty() && at( 0 )->GetScreen() )
  203. fileName = at( 0 )->GetScreen()->GetFileName();
  204. wxFileName fn = fileName;
  205. if( aUseShortRootName )
  206. s = wxT( "/" ); // Use only the short name in netlists
  207. else
  208. s = fn.GetName() + wxT( "/" );
  209. // Start at 1 since we've already processed the root sheet.
  210. for( unsigned i = 1; i < size(); i++ )
  211. s = s + at( i )->GetFields()[ SHEETNAME ].GetShownText() + wxT( "/" );
  212. return s;
  213. }
  214. void SCH_SHEET_PATH::UpdateAllScreenReferences()
  215. {
  216. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  217. {
  218. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  219. symbol->GetField( REFERENCE_FIELD )->SetText( symbol->GetRef( this ) );
  220. symbol->GetField( VALUE_FIELD )->SetText( symbol->GetValue( this, false ) );
  221. symbol->GetField( FOOTPRINT_FIELD )->SetText( symbol->GetFootprint( this, false ) );
  222. symbol->UpdateUnit( symbol->GetUnitSelection( this ) );
  223. }
  224. }
  225. void SCH_SHEET_PATH::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
  226. bool aForceIncludeOrphanSymbols ) const
  227. {
  228. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  229. {
  230. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  231. AppendSymbol( aReferences, symbol, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
  232. }
  233. }
  234. void SCH_SHEET_PATH::AppendSymbol( SCH_REFERENCE_LIST& aReferences, SCH_SYMBOL* aSymbol,
  235. bool aIncludePowerSymbols,
  236. bool aForceIncludeOrphanSymbols ) const
  237. {
  238. // Skip pseudo-symbols, which have a reference starting with #. This mainly
  239. // affects power symbols.
  240. if( aIncludePowerSymbols || aSymbol->GetRef( this )[0] != wxT( '#' ) )
  241. {
  242. LIB_SYMBOL* symbol = aSymbol->GetLibSymbolRef().get();
  243. if( symbol || aForceIncludeOrphanSymbols )
  244. {
  245. SCH_REFERENCE schReference( aSymbol, symbol, *this );
  246. schReference.SetSheetNumber( m_virtualPageNumber );
  247. aReferences.AddItem( schReference );
  248. }
  249. }
  250. }
  251. void SCH_SHEET_PATH::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList,
  252. bool aIncludePowerSymbols ) const
  253. {
  254. for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  255. {
  256. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  257. AppendMultiUnitSymbol( aRefList, symbol, aIncludePowerSymbols );
  258. }
  259. }
  260. void SCH_SHEET_PATH::AppendMultiUnitSymbol( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList,
  261. SCH_SYMBOL* aSymbol,
  262. bool aIncludePowerSymbols ) const
  263. {
  264. // Skip pseudo-symbols, which have a reference starting with #. This mainly
  265. // affects power symbols.
  266. if( !aIncludePowerSymbols && aSymbol->GetRef( this )[0] == wxT( '#' ) )
  267. return;
  268. LIB_SYMBOL* symbol = aSymbol->GetLibSymbolRef().get();
  269. if( symbol && symbol->GetUnitCount() > 1 )
  270. {
  271. SCH_REFERENCE schReference = SCH_REFERENCE( aSymbol, symbol, *this );
  272. schReference.SetSheetNumber( m_virtualPageNumber );
  273. wxString reference_str = schReference.GetRef();
  274. // Never lock unassigned references
  275. if( reference_str[reference_str.Len() - 1] == '?' )
  276. return;
  277. aRefList[reference_str].AddItem( schReference );
  278. }
  279. }
  280. bool SCH_SHEET_PATH::operator==( const SCH_SHEET_PATH& d1 ) const
  281. {
  282. return m_current_hash == d1.GetCurrentHash();
  283. }
  284. bool SCH_SHEET_PATH::TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName )
  285. {
  286. auto pair = std::make_pair( aSrcFileName, aDestFileName );
  287. if( m_recursion_test_cache.count( pair ) )
  288. return m_recursion_test_cache.at( pair );
  289. SCHEMATIC* sch = LastScreen()->Schematic();
  290. wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_PATH::TestForRecursion!" );
  291. wxFileName rootFn = sch->GetFileName();
  292. wxFileName srcFn = aSrcFileName;
  293. wxFileName destFn = aDestFileName;
  294. if( srcFn.IsRelative() )
  295. srcFn.MakeAbsolute( rootFn.GetPath() );
  296. if( destFn.IsRelative() )
  297. destFn.MakeAbsolute( rootFn.GetPath() );
  298. // The source and destination sheet file names cannot be the same.
  299. if( srcFn == destFn )
  300. {
  301. m_recursion_test_cache[pair] = true;
  302. return true;
  303. }
  304. /// @todo Store sheet file names with full path, either relative to project path
  305. /// or absolute path. The current design always assumes subsheet files are
  306. /// located in the project folder which may or may not be desirable.
  307. unsigned i = 0;
  308. while( i < size() )
  309. {
  310. wxFileName cmpFn = at( i )->GetFileName();
  311. if( cmpFn.IsRelative() )
  312. cmpFn.MakeAbsolute( rootFn.GetPath() );
  313. // Test if the file name of the destination sheet is in anywhere in this sheet path.
  314. if( cmpFn == destFn )
  315. break;
  316. i++;
  317. }
  318. // The destination sheet file name was not found in the sheet path or the destination
  319. // sheet file name is the root sheet so no recursion is possible.
  320. if( i >= size() || i == 0 )
  321. {
  322. m_recursion_test_cache[pair] = false;
  323. return false;
  324. }
  325. // Walk back up to the root sheet to see if the source file name is already a parent in
  326. // the sheet path. If so, recursion will occur.
  327. do
  328. {
  329. i -= 1;
  330. wxFileName cmpFn = at( i )->GetFileName();
  331. if( cmpFn.IsRelative() )
  332. cmpFn.MakeAbsolute( rootFn.GetPath() );
  333. if( cmpFn == srcFn )
  334. {
  335. m_recursion_test_cache[pair] = true;
  336. return true;
  337. }
  338. } while( i != 0 );
  339. // The source sheet file name is not a parent of the destination sheet file name.
  340. m_recursion_test_cache[pair] = false;
  341. return false;
  342. }
  343. wxString SCH_SHEET_PATH::GetPageNumber() const
  344. {
  345. SCH_SHEET* sheet = Last();
  346. wxCHECK( sheet, wxEmptyString );
  347. return sheet->GetPageNumber( *this );
  348. }
  349. void SCH_SHEET_PATH::SetPageNumber( const wxString& aPageNumber )
  350. {
  351. SCH_SHEET* sheet = Last();
  352. wxCHECK( sheet, /* void */ );
  353. sheet->SetPageNumber( *this, aPageNumber );
  354. }
  355. void SCH_SHEET_PATH::MakeFilePathRelativeToParentSheet()
  356. {
  357. wxCHECK( m_sheets.size() > 1, /* void */ );
  358. wxFileName sheetFileName = Last()->GetFileName();
  359. // If the sheet file name is absolute, then the user requested is so don't make it relative.
  360. if( sheetFileName.IsAbsolute() )
  361. return;
  362. SCH_SCREEN* screen = LastScreen();
  363. SCH_SCREEN* parentScreen = m_sheets[ m_sheets.size() - 2 ]->GetScreen();
  364. wxCHECK( screen && parentScreen, /* void */ );
  365. wxFileName fileName = screen->GetFileName();
  366. wxFileName parentFileName = parentScreen->GetFileName();
  367. // SCH_SCREEN file names must be absolute. If they are not, someone set them incorrectly
  368. // on load or on creation.
  369. wxCHECK( fileName.IsAbsolute() && parentFileName.IsAbsolute(), /* void */ );
  370. if( fileName.GetPath() == parentFileName.GetPath() )
  371. {
  372. Last()->SetFileName( fileName.GetFullName() );
  373. }
  374. else if( fileName.MakeRelativeTo( parentFileName.GetPath() ) )
  375. {
  376. Last()->SetFileName( fileName.GetFullPath() );
  377. }
  378. else
  379. {
  380. Last()->SetFileName( screen->GetFileName() );
  381. }
  382. wxLogTrace( tracePathsAndFiles,
  383. wxT( "\n File name: '%s'"
  384. "\n parent file name '%s',"
  385. "\n sheet '%s' file name '%s'." ),
  386. screen->GetFileName(), parentScreen->GetFileName(), PathHumanReadable(),
  387. Last()->GetFileName() );
  388. }
  389. SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet, bool aCheckIntegrity )
  390. {
  391. if( aSheet != nullptr )
  392. {
  393. BuildSheetList( aSheet, aCheckIntegrity );
  394. SortByPageNumbers();
  395. }
  396. }
  397. void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity )
  398. {
  399. wxCHECK_RET( aSheet != nullptr, wxT( "Cannot build sheet list from undefined sheet." ) );
  400. std::vector<SCH_SHEET*> badSheets;
  401. m_currentSheetPath.push_back( aSheet );
  402. m_currentSheetPath.SetVirtualPageNumber( static_cast<int>( size() ) + 1 );
  403. push_back( m_currentSheetPath );
  404. if( m_currentSheetPath.LastScreen() )
  405. {
  406. wxString parentFileName = aSheet->GetFileName();
  407. std::vector<SCH_ITEM*> childSheets;
  408. m_currentSheetPath.LastScreen()->GetSheets( &childSheets );
  409. for( SCH_ITEM* item : childSheets )
  410. {
  411. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  412. if( aCheckIntegrity )
  413. {
  414. if( !m_currentSheetPath.TestForRecursion( sheet->GetFileName(), parentFileName ) )
  415. BuildSheetList( sheet, true );
  416. else
  417. badSheets.push_back( sheet );
  418. }
  419. else
  420. {
  421. BuildSheetList( sheet, false );
  422. }
  423. }
  424. }
  425. if( aCheckIntegrity )
  426. {
  427. for( SCH_SHEET* sheet : badSheets )
  428. {
  429. m_currentSheetPath.LastScreen()->Remove( sheet );
  430. m_currentSheetPath.LastScreen()->SetContentModified();
  431. }
  432. }
  433. m_currentSheetPath.pop_back();
  434. }
  435. void SCH_SHEET_LIST::SortByPageNumbers( bool aUpdateVirtualPageNums )
  436. {
  437. std::sort( begin(), end(),
  438. []( SCH_SHEET_PATH a, SCH_SHEET_PATH b ) -> bool
  439. {
  440. return a.ComparePageNumAndName(b) < 0;
  441. } );
  442. if( aUpdateVirtualPageNums )
  443. {
  444. int virtualPageNum = 1;
  445. for( SCH_SHEET_PATH& sheet : *this )
  446. {
  447. sheet.SetVirtualPageNumber( virtualPageNum++ );
  448. }
  449. }
  450. }
  451. bool SCH_SHEET_LIST::NameExists( const wxString& aSheetName ) const
  452. {
  453. for( const SCH_SHEET_PATH& sheet : *this )
  454. {
  455. if( sheet.Last()->GetName() == aSheetName )
  456. return true;
  457. }
  458. return false;
  459. }
  460. bool SCH_SHEET_LIST::PageNumberExists( const wxString& aPageNumber ) const
  461. {
  462. for( const SCH_SHEET_PATH& sheet : *this )
  463. {
  464. if( sheet.GetPageNumber() == aPageNumber )
  465. return true;
  466. }
  467. return false;
  468. }
  469. bool SCH_SHEET_LIST::IsModified() const
  470. {
  471. for( const SCH_SHEET_PATH& sheet : *this )
  472. {
  473. if( sheet.LastScreen() && sheet.LastScreen()->IsContentModified() )
  474. return true;
  475. }
  476. return false;
  477. }
  478. void SCH_SHEET_LIST::ClearModifyStatus()
  479. {
  480. for( const SCH_SHEET_PATH& sheet : *this )
  481. {
  482. if( sheet.LastScreen() )
  483. sheet.LastScreen()->SetContentModified( false );
  484. }
  485. }
  486. SCH_ITEM* SCH_SHEET_LIST::GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut ) const
  487. {
  488. for( const SCH_SHEET_PATH& sheet : *this )
  489. {
  490. SCH_SCREEN* screen = sheet.LastScreen();
  491. for( SCH_ITEM* aItem : screen->Items() )
  492. {
  493. if( aItem->m_Uuid == aID )
  494. {
  495. if( aPathOut )
  496. *aPathOut = sheet;
  497. return aItem;
  498. }
  499. SCH_ITEM* childMatch = nullptr;
  500. aItem->RunOnChildren(
  501. [&]( SCH_ITEM* aChild )
  502. {
  503. if( aChild->m_Uuid == aID )
  504. childMatch = aChild;
  505. } );
  506. if( childMatch )
  507. {
  508. if( aPathOut )
  509. *aPathOut = sheet;
  510. return childMatch;
  511. }
  512. }
  513. }
  514. // Not found; weak reference has been deleted.
  515. return DELETED_SHEET_ITEM::GetInstance();
  516. }
  517. void SCH_SHEET_LIST::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
  518. {
  519. for( const SCH_SHEET_PATH& sheet : *this )
  520. {
  521. SCH_SCREEN* screen = sheet.LastScreen();
  522. for( SCH_ITEM* aItem : screen->Items() )
  523. {
  524. aMap[ aItem->m_Uuid ] = aItem;
  525. aItem->RunOnChildren(
  526. [&]( SCH_ITEM* aChild )
  527. {
  528. aMap[ aChild->m_Uuid ] = aChild;
  529. } );
  530. }
  531. }
  532. }
  533. void SCH_SHEET_LIST::AnnotatePowerSymbols()
  534. {
  535. // List of reference for power symbols
  536. SCH_REFERENCE_LIST references;
  537. SCH_REFERENCE_LIST additionalreferences; // Todo: add as a parameter to this function
  538. // Map of locked symbols (not used, but needed by Annotate()
  539. SCH_MULTI_UNIT_REFERENCE_MAP lockedSymbols;
  540. // Build the list of power symbols:
  541. for( SCH_SHEET_PATH& sheet : *this )
  542. {
  543. for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
  544. {
  545. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
  546. LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
  547. if( libSymbol && libSymbol->IsPower() )
  548. {
  549. SCH_REFERENCE schReference( symbol, libSymbol, sheet );
  550. references.AddItem( schReference );
  551. }
  552. }
  553. }
  554. // Find duplicate, and silently clear annotation of duplicate
  555. std::map<wxString, int> ref_list; // stores the existing references
  556. for( unsigned ii = 0; ii< references.GetCount(); ++ii )
  557. {
  558. wxString curr_ref = references[ii].GetRef();
  559. if( ref_list.find( curr_ref ) == ref_list.end() )
  560. {
  561. ref_list[curr_ref] = ii;
  562. continue;
  563. }
  564. // Possible duplicate, if the ref ends by a number:
  565. if( curr_ref.Last() < '0' && curr_ref.Last() > '9' )
  566. continue; // not annotated
  567. // Duplicate: clear annotation by removing the number ending the ref
  568. while( curr_ref.Last() >= '0' && curr_ref.Last() <= '9' )
  569. curr_ref.RemoveLast();
  570. references[ii].SetRef( curr_ref );
  571. }
  572. // Break full symbol reference into name (prefix) and number:
  573. // example: IC1 become IC, and 1
  574. references.SplitReferences();
  575. // Ensure all power symbols have the reference starting by '#'
  576. // (Not sure this is really useful)
  577. for( unsigned ii = 0; ii< references.GetCount(); ++ii )
  578. {
  579. if( references[ii].GetRef()[0] != '#' )
  580. {
  581. wxString new_ref = "#" + references[ii].GetRef();
  582. references[ii].SetRef( new_ref );
  583. }
  584. }
  585. // Recalculate and update reference numbers in schematic
  586. references.Annotate( false, 0, 100, lockedSymbols, additionalreferences );
  587. references.UpdateAnnotation();
  588. }
  589. void SCH_SHEET_LIST::GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols,
  590. bool aForceIncludeOrphanSymbols ) const
  591. {
  592. for( const SCH_SHEET_PATH& sheet : *this )
  593. sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
  594. }
  595. void SCH_SHEET_LIST::GetSymbolsWithinPath( SCH_REFERENCE_LIST& aReferences,
  596. const SCH_SHEET_PATH& aSheetPath,
  597. bool aIncludePowerSymbols,
  598. bool aForceIncludeOrphanSymbols ) const
  599. {
  600. for( const SCH_SHEET_PATH& sheet : *this )
  601. {
  602. if( sheet.IsContainedWithin( aSheetPath ) )
  603. sheet.GetSymbols( aReferences, aIncludePowerSymbols, aForceIncludeOrphanSymbols );
  604. }
  605. }
  606. void SCH_SHEET_LIST::GetSheetsWithinPath( SCH_SHEET_PATHS& aSheets,
  607. const SCH_SHEET_PATH& aSheetPath ) const
  608. {
  609. for( const SCH_SHEET_PATH& sheet : *this )
  610. {
  611. if( sheet.IsContainedWithin( aSheetPath ) )
  612. aSheets.push_back( sheet );
  613. }
  614. }
  615. void SCH_SHEET_LIST::GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
  616. bool aIncludePowerSymbols ) const
  617. {
  618. for( SCH_SHEET_PATHS::const_iterator it = begin(); it != end(); ++it )
  619. {
  620. SCH_MULTI_UNIT_REFERENCE_MAP tempMap;
  621. ( *it ).GetMultiUnitSymbols( tempMap );
  622. for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : tempMap )
  623. {
  624. // Merge this list into the main one
  625. unsigned n_refs = pair.second.GetCount();
  626. for( unsigned thisRef = 0; thisRef < n_refs; ++thisRef )
  627. aRefList[pair.first].AddItem( pair.second[thisRef] );
  628. }
  629. }
  630. }
  631. bool SCH_SHEET_LIST::TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
  632. const wxString& aDestFileName )
  633. {
  634. if( empty() )
  635. return false;
  636. SCHEMATIC* sch = at( 0 ).LastScreen()->Schematic();
  637. wxCHECK_MSG( sch, false, "No SCHEMATIC found in SCH_SHEET_LIST::TestForRecursion!" );
  638. wxFileName rootFn = sch->GetFileName();
  639. wxFileName destFn = aDestFileName;
  640. if( destFn.IsRelative() )
  641. destFn.MakeAbsolute( rootFn.GetPath() );
  642. // Test each SCH_SHEET_PATH in this SCH_SHEET_LIST for potential recursion.
  643. for( unsigned i = 0; i < size(); i++ )
  644. {
  645. // Test each SCH_SHEET_PATH in the source sheet.
  646. for( unsigned j = 0; j < aSrcSheetHierarchy.size(); j++ )
  647. {
  648. const SCH_SHEET_PATH* sheetPath = &aSrcSheetHierarchy[j];
  649. for( unsigned k = 0; k < sheetPath->size(); k++ )
  650. {
  651. if( at( i ).TestForRecursion( sheetPath->GetSheet( k )->GetFileName(),
  652. aDestFileName ) )
  653. {
  654. return true;
  655. }
  656. }
  657. }
  658. }
  659. // The source sheet file can safely be added to the destination sheet file.
  660. return false;
  661. }
  662. SCH_SHEET_PATH* SCH_SHEET_LIST::FindSheetForScreen( const SCH_SCREEN* aScreen )
  663. {
  664. for( SCH_SHEET_PATH& sheetpath : *this )
  665. {
  666. if( sheetpath.LastScreen() == aScreen )
  667. return &sheetpath;
  668. }
  669. return nullptr;
  670. }
  671. SCH_SHEET_LIST SCH_SHEET_LIST::FindAllSheetsForScreen( const SCH_SCREEN* aScreen ) const
  672. {
  673. SCH_SHEET_LIST retval;
  674. for( const SCH_SHEET_PATH& sheetpath : *this )
  675. {
  676. if( sheetpath.LastScreen() == aScreen )
  677. retval.push_back( sheetpath );
  678. }
  679. return retval;
  680. }
  681. void SCH_SHEET_LIST::UpdateSymbolInstances(
  682. const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbolInstances )
  683. {
  684. SCH_REFERENCE_LIST symbolInstances;
  685. GetSymbols( symbolInstances, true, true );
  686. std::map<KIID_PATH, wxString> pathNameCache;
  687. // Calculating the name of a path is somewhat expensive; on large designs with many symbols
  688. // this can blow up to a serious amount of time when loading the schematic
  689. auto getName = [&pathNameCache]( const KIID_PATH& aPath ) -> const wxString&
  690. {
  691. if( pathNameCache.count( aPath ) )
  692. return pathNameCache.at( aPath );
  693. pathNameCache[aPath] = aPath.AsString();
  694. return pathNameCache[aPath];
  695. };
  696. for( size_t i = 0; i < symbolInstances.GetCount(); i++ )
  697. {
  698. // The instance paths are stored in the file sans root path so the comparison
  699. // should not include the root path.
  700. wxString path = symbolInstances[i].GetPath();
  701. auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
  702. [ path, &getName ]( const SYMBOL_INSTANCE_REFERENCE& r ) -> bool
  703. {
  704. return path == getName( r.m_Path );
  705. } );
  706. if( it == aSymbolInstances.end() )
  707. {
  708. wxLogTrace( traceSchSheetPaths, "No symbol instance found for path '%s'", path );
  709. continue;
  710. }
  711. SCH_SYMBOL* symbol = symbolInstances[ i ].GetSymbol();
  712. wxCHECK2( symbol, continue );
  713. // Symbol instance paths are stored and looked up in memory with the root path so use
  714. // the full path here.
  715. symbol->AddHierarchicalReference( symbolInstances[i].GetSheetPath().Path(),
  716. it->m_Reference, it->m_Unit, it->m_Value,
  717. it->m_Footprint );
  718. symbol->GetField( REFERENCE_FIELD )->SetText( it->m_Reference );
  719. }
  720. }
  721. void SCH_SHEET_LIST::UpdateSheetInstances( const std::vector<SCH_SHEET_INSTANCE>& aSheetInstances )
  722. {
  723. for( const SCH_SHEET_PATH& instance : *this )
  724. {
  725. auto it = std::find_if( aSheetInstances.begin(), aSheetInstances.end(),
  726. [ instance ]( const SCH_SHEET_INSTANCE& r ) -> bool
  727. {
  728. return instance.PathWithoutRootUuid() == r.m_Path;
  729. } );
  730. if( it == aSheetInstances.end() )
  731. {
  732. wxLogTrace( traceSchSheetPaths, "No sheet instance found for path '%s'",
  733. instance.PathWithoutRootUuid().AsString() );
  734. continue;
  735. }
  736. SCH_SHEET* sheet = instance.Last();
  737. wxCHECK2( sheet, continue );
  738. sheet->AddInstance( instance.Path() );
  739. sheet->SetPageNumber( instance, it->m_PageNumber );
  740. }
  741. }
  742. std::vector<KIID_PATH> SCH_SHEET_LIST::GetPaths() const
  743. {
  744. std::vector<KIID_PATH> paths;
  745. for( const SCH_SHEET_PATH& sheetPath : *this )
  746. paths.emplace_back( sheetPath.Path() );
  747. return paths;
  748. }
  749. std::vector<SCH_SHEET_INSTANCE> SCH_SHEET_LIST::GetSheetInstances() const
  750. {
  751. std::vector<SCH_SHEET_INSTANCE> retval;
  752. for( const SCH_SHEET_PATH& path : *this )
  753. {
  754. SCH_SHEET_INSTANCE instance;
  755. const SCH_SHEET* sheet = path.Last();
  756. wxCHECK2( sheet, continue );
  757. instance.m_Path = path.PathWithoutRootUuid();
  758. instance.m_PageNumber = sheet->GetPageNumber( path );
  759. retval.push_back( instance );
  760. }
  761. return retval;
  762. }
  763. bool SCH_SHEET_LIST::AllSheetPageNumbersEmpty() const
  764. {
  765. for( const SCH_SHEET_PATH& instance : *this )
  766. {
  767. const SCH_SHEET* sheet = instance.Last();
  768. wxCHECK2( sheet, continue );
  769. if( !sheet->GetPageNumber( instance ).IsEmpty() )
  770. return false;
  771. }
  772. return true;
  773. }
  774. void SCH_SHEET_LIST::SetInitialPageNumbers()
  775. {
  776. // Don't accidentally renumber existing sheets.
  777. wxCHECK( AllSheetPageNumbersEmpty(), /* void */ );
  778. wxString tmp;
  779. int pageNumber = 1;
  780. for( const SCH_SHEET_PATH& instance : *this )
  781. {
  782. SCH_SHEET* sheet = instance.Last();
  783. wxCHECK2( sheet, continue );
  784. sheet->AddInstance( instance.Path() );
  785. tmp.Printf( "%d", pageNumber );
  786. sheet->SetPageNumber( instance, tmp );
  787. pageNumber += 1;
  788. }
  789. }