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.

769 lines
19 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2009 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
  5. * Copyright (C) 2011-2016 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file sch_sheet_path.cpp
  27. * @brief SCH_SHEET_PATH class implementation.
  28. */
  29. #include <fctsys.h>
  30. #include <general.h>
  31. #include <dlist.h>
  32. #include <class_sch_screen.h>
  33. #include <sch_item_struct.h>
  34. #include <sch_reference_list.h>
  35. #include <class_library.h>
  36. #include <sch_sheet_path.h>
  37. #include <sch_component.h>
  38. #include <template_fieldnames.h>
  39. #include <dialogs/dialog_schematic_find.h>
  40. #include <boost/foreach.hpp>
  41. #include <wx/filename.h>
  42. SCH_SHEET_PATH::SCH_SHEET_PATH()
  43. {
  44. m_pageNumber = 0;
  45. }
  46. int SCH_SHEET_PATH::Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const
  47. {
  48. if( size() > aSheetPathToTest.size() )
  49. return 1;
  50. if( size() < aSheetPathToTest.size() )
  51. return -1;
  52. //otherwise, same number of sheets.
  53. for( unsigned i = 0; i < size(); i++ )
  54. {
  55. if( at( i )->GetTimeStamp() > aSheetPathToTest.at( i )->GetTimeStamp() )
  56. return 1;
  57. if( at( i )->GetTimeStamp() < aSheetPathToTest.at( i )->GetTimeStamp() )
  58. return -1;
  59. }
  60. return 0;
  61. }
  62. SCH_SHEET* SCH_SHEET_PATH::Last() const
  63. {
  64. if( !empty() )
  65. return at( size() - 1 );
  66. return NULL;
  67. }
  68. SCH_SCREEN* SCH_SHEET_PATH::LastScreen() const
  69. {
  70. SCH_SHEET* lastSheet = Last();
  71. if( lastSheet )
  72. return lastSheet->GetScreen();
  73. return NULL;
  74. }
  75. SCH_ITEM* SCH_SHEET_PATH::LastDrawList() const
  76. {
  77. SCH_SHEET* lastSheet = Last();
  78. if( lastSheet && lastSheet->GetScreen() )
  79. return lastSheet->GetScreen()->GetDrawItems();
  80. return NULL;
  81. }
  82. SCH_ITEM* SCH_SHEET_PATH::FirstDrawList() const
  83. {
  84. SCH_ITEM* item = NULL;
  85. if( !empty() && at( 0 )->GetScreen() )
  86. item = at( 0 )->GetScreen()->GetDrawItems();
  87. /* @fixme - These lists really should be one of the boost pointer containers. This
  88. * is a brain dead hack to allow reverse iteration of EDA_ITEM linked
  89. * list.
  90. */
  91. SCH_ITEM* lastItem = NULL;
  92. while( item )
  93. {
  94. lastItem = item;
  95. item = item->Next();
  96. }
  97. return lastItem;
  98. }
  99. wxString SCH_SHEET_PATH::Path() const
  100. {
  101. wxString s, t;
  102. s = wxT( "/" ); // This is the root path
  103. // start at 1 to avoid the root sheet,
  104. // which does not need to be added to the path
  105. // it's timestamp changes anyway.
  106. for( unsigned i = 1; i < size(); i++ )
  107. {
  108. t.Printf( _( "%8.8lX/" ), (long unsigned) at( i )->GetTimeStamp() );
  109. s = s + t;
  110. }
  111. return s;
  112. }
  113. wxString SCH_SHEET_PATH::PathHumanReadable() const
  114. {
  115. wxString s;
  116. s = wxT( "/" );
  117. // start at 1 to avoid the root sheet, as above.
  118. for( unsigned i = 1; i < size(); i++ )
  119. {
  120. s = s + at( i )->GetName() + wxT( "/" );
  121. }
  122. return s;
  123. }
  124. void SCH_SHEET_PATH::UpdateAllScreenReferences()
  125. {
  126. EDA_ITEM* t = LastDrawList();
  127. while( t )
  128. {
  129. if( t->Type() == SCH_COMPONENT_T )
  130. {
  131. SCH_COMPONENT* component = (SCH_COMPONENT*) t;
  132. component->GetField( REFERENCE )->SetText( component->GetRef( this ) );
  133. component->UpdateUnit( component->GetUnitSelection( this ) );
  134. }
  135. t = t->Next();
  136. }
  137. }
  138. void SCH_SHEET_PATH::AnnotatePowerSymbols( PART_LIBS* aLibs, int* aReference )
  139. {
  140. int ref = 1;
  141. if( aReference )
  142. ref = *aReference;
  143. for( EDA_ITEM* item = LastDrawList(); item; item = item->Next() )
  144. {
  145. if( item->Type() != SCH_COMPONENT_T )
  146. continue;
  147. SCH_COMPONENT* component = (SCH_COMPONENT*) item;
  148. LIB_PART* part = aLibs->FindLibPart( component->GetPartName() );
  149. if( !part || !part->IsPower() )
  150. continue;
  151. wxString refstr = component->GetPrefix();
  152. //str will be "C?" or so after the ClearAnnotation call.
  153. while( refstr.Last() == '?' )
  154. refstr.RemoveLast();
  155. if( !refstr.StartsWith( wxT( "#" ) ) )
  156. refstr = wxT( "#" ) + refstr;
  157. refstr << wxT( "0" ) << ref;
  158. component->SetRef( this, refstr );
  159. ref++;
  160. }
  161. if( aReference )
  162. *aReference = ref;
  163. }
  164. void SCH_SHEET_PATH::GetComponents( PART_LIBS* aLibs, SCH_REFERENCE_LIST& aReferences,
  165. bool aIncludePowerSymbols )
  166. {
  167. for( SCH_ITEM* item = LastDrawList(); item; item = item->Next() )
  168. {
  169. if( item->Type() == SCH_COMPONENT_T )
  170. {
  171. SCH_COMPONENT* component = (SCH_COMPONENT*) item;
  172. // Skip pseudo components, which have a reference starting with #. This mainly
  173. // affects power symbols.
  174. if( !aIncludePowerSymbols && component->GetRef( this )[0] == wxT( '#' ) )
  175. continue;
  176. LIB_PART* part = aLibs->FindLibPart( component->GetPartName() );
  177. if( part )
  178. {
  179. SCH_REFERENCE reference = SCH_REFERENCE( component, part, *this );
  180. reference.SetSheetNumber( m_pageNumber );
  181. aReferences.AddItem( reference );
  182. }
  183. }
  184. }
  185. }
  186. void SCH_SHEET_PATH::GetMultiUnitComponents( PART_LIBS* aLibs,
  187. SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
  188. bool aIncludePowerSymbols )
  189. {
  190. for( SCH_ITEM* item = LastDrawList(); item; item = item->Next() )
  191. {
  192. if( item->Type() != SCH_COMPONENT_T )
  193. continue;
  194. SCH_COMPONENT* component = (SCH_COMPONENT*) item;
  195. // Skip pseudo components, which have a reference starting with #. This mainly
  196. // affects power symbols.
  197. if( !aIncludePowerSymbols && component->GetRef( this )[0] == wxT( '#' ) )
  198. continue;
  199. LIB_PART* part = aLibs->FindLibPart( component->GetPartName() );
  200. if( part && part->GetUnitCount() > 1 )
  201. {
  202. SCH_REFERENCE reference = SCH_REFERENCE( component, part, *this );
  203. reference.SetSheetNumber( m_pageNumber );
  204. wxString reference_str = reference.GetRef();
  205. // Never lock unassigned references
  206. if( reference_str[reference_str.Len() - 1] == '?' )
  207. continue;
  208. aRefList[reference_str].AddItem( reference );
  209. }
  210. }
  211. }
  212. SCH_ITEM* SCH_SHEET_PATH::FindNextItem( KICAD_T aType, SCH_ITEM* aLastItem, bool aWrap ) const
  213. {
  214. bool hasWrapped = false;
  215. bool firstItemFound = false;
  216. SCH_ITEM* drawItem = LastDrawList();
  217. while( drawItem )
  218. {
  219. if( drawItem->Type() == aType )
  220. {
  221. if( !aLastItem || firstItemFound )
  222. {
  223. return drawItem;
  224. }
  225. else if( !firstItemFound && drawItem == aLastItem )
  226. {
  227. firstItemFound = true;
  228. }
  229. }
  230. drawItem = drawItem->Next();
  231. if( !drawItem && aLastItem && aWrap && !hasWrapped )
  232. {
  233. hasWrapped = true;
  234. drawItem = LastDrawList();
  235. }
  236. }
  237. return NULL;
  238. }
  239. SCH_ITEM* SCH_SHEET_PATH::FindPreviousItem( KICAD_T aType, SCH_ITEM* aLastItem, bool aWrap ) const
  240. {
  241. bool hasWrapped = false;
  242. bool firstItemFound = false;
  243. SCH_ITEM* drawItem = FirstDrawList();
  244. while( drawItem )
  245. {
  246. if( drawItem->Type() == aType )
  247. {
  248. if( aLastItem == NULL || firstItemFound )
  249. {
  250. return drawItem;
  251. }
  252. else if( !firstItemFound && drawItem == aLastItem )
  253. {
  254. firstItemFound = true;
  255. }
  256. }
  257. drawItem = drawItem->Back();
  258. if( drawItem == NULL && aLastItem && aWrap && !hasWrapped )
  259. {
  260. hasWrapped = true;
  261. drawItem = FirstDrawList();
  262. }
  263. }
  264. return NULL;
  265. }
  266. bool SCH_SHEET_PATH::SetComponentFootprint( const wxString& aReference, const wxString& aFootPrint,
  267. bool aSetVisible )
  268. {
  269. SCH_SCREEN* screen = LastScreen();
  270. if( screen == NULL )
  271. return false;
  272. return screen->SetComponentFootprint( this, aReference, aFootPrint, aSetVisible );
  273. }
  274. bool SCH_SHEET_PATH::operator==( const SCH_SHEET_PATH& d1 ) const
  275. {
  276. if( size() != d1.size() )
  277. return false;
  278. for( unsigned i = 0; i < size(); i++ )
  279. {
  280. if( at( i ) != d1[i] )
  281. return false;
  282. }
  283. return true;
  284. }
  285. bool SCH_SHEET_PATH::TestForRecursion( const wxString& aSrcFileName,
  286. const wxString& aDestFileName ) const
  287. {
  288. wxFileName rootFn = g_RootSheet->GetFileName();
  289. wxFileName srcFn = aSrcFileName;
  290. wxFileName destFn = aDestFileName;
  291. if( srcFn.IsRelative() )
  292. srcFn.MakeAbsolute( rootFn.GetPath() );
  293. if( destFn.IsRelative() )
  294. destFn.MakeAbsolute( rootFn.GetPath() );
  295. // The source and destination sheet file names cannot be the same.
  296. if( srcFn == destFn )
  297. return true;
  298. /// @todo Store sheet file names with full path, either relative to project path
  299. /// or absolute path. The current design always assumes subsheet files are
  300. /// located in the project folder which may or may not be desirable.
  301. unsigned i = 0;
  302. while( i < size() )
  303. {
  304. wxFileName cmpFn = at( i )->GetFileName();
  305. if( cmpFn.IsRelative() )
  306. cmpFn.MakeAbsolute( rootFn.GetPath() );
  307. // Test if the file name of the destination sheet is in anywhere in this sheet path.
  308. if( cmpFn == destFn )
  309. break;
  310. i++;
  311. }
  312. // The destination sheet file name was not found in the sheet path or the destination
  313. // sheet file name is the root sheet so no recursion is possible.
  314. if( i >= size() || i == 0 )
  315. return false;
  316. // Walk back up to the root sheet to see if the source file name is already a parent in
  317. // the sheet path. If so, recursion will occur.
  318. do
  319. {
  320. i -= 1;
  321. wxFileName cmpFn = at( i )->GetFileName();
  322. if( cmpFn.IsRelative() )
  323. cmpFn.MakeAbsolute( rootFn.GetPath() );
  324. if( cmpFn == srcFn )
  325. return true;
  326. } while( i != 0 );
  327. // The source sheet file name is not a parent of the destination sheet file name.
  328. return false;
  329. }
  330. int SCH_SHEET_PATH::FindSheet( const wxString& aFileName ) const
  331. {
  332. for( unsigned i = 0; i < size(); i++ )
  333. {
  334. if( at( i )->GetFileName().CmpNoCase( aFileName ) == 0 )
  335. return (int)i;
  336. }
  337. return SHEET_NOT_FOUND;
  338. }
  339. SCH_SHEET* SCH_SHEET_PATH::FindSheetByName( const wxString& aSheetName )
  340. {
  341. for( unsigned i = 0; i < size(); i++ )
  342. {
  343. if( at( i )->GetName().CmpNoCase( aSheetName ) == 0 )
  344. return at( i );
  345. }
  346. return NULL;
  347. }
  348. /********************************************************************/
  349. /* Class SCH_SHEET_LIST to handle the list of Sheets in a hierarchy */
  350. /********************************************************************/
  351. SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet )
  352. {
  353. m_isRootSheet = false;
  354. if( aSheet != NULL )
  355. BuildSheetList( aSheet );
  356. }
  357. SCH_SHEET_PATH* SCH_SHEET_LIST::GetSheetByPath( const wxString aPath, bool aHumanReadable )
  358. {
  359. wxString sheetPath;
  360. for( unsigned i = 0; i < size(); i++ )
  361. {
  362. sheetPath = ( aHumanReadable ) ? at( i ).PathHumanReadable() : at( i ).Path();
  363. if( sheetPath == aPath )
  364. return &at( i );
  365. }
  366. return NULL;
  367. }
  368. void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet )
  369. {
  370. wxCHECK_RET( aSheet != NULL, wxT( "Cannot build sheet list from undefined sheet." ) );
  371. if( aSheet == g_RootSheet )
  372. m_isRootSheet = true;
  373. m_currentSheetPath.push_back( aSheet );
  374. /**
  375. * @todo: Schematic page number is currently a left over relic and is generated as
  376. * SCH_SHEET_PATH object is pushed to the list. This only has meaning when
  377. * entire hierarchy is created from the root sheet down.
  378. */
  379. m_currentSheetPath.SetPageNumber( size() + 1 );
  380. push_back( m_currentSheetPath );
  381. if( aSheet->GetScreen() )
  382. {
  383. EDA_ITEM* item = m_currentSheetPath.LastDrawList();
  384. while( item )
  385. {
  386. if( item->Type() == SCH_SHEET_T )
  387. {
  388. SCH_SHEET* sheet = (SCH_SHEET*) item;
  389. BuildSheetList( sheet );
  390. }
  391. item = item->Next();
  392. }
  393. }
  394. m_currentSheetPath.pop_back();
  395. }
  396. bool SCH_SHEET_LIST::IsModified()
  397. {
  398. for( SCH_SHEET_PATHS_ITER it = begin(); it != end(); ++it )
  399. {
  400. if( (*it).LastScreen() && (*it).LastScreen()->IsModify() )
  401. return true;
  402. }
  403. return false;
  404. }
  405. bool SCH_SHEET_LIST::IsAutoSaveRequired()
  406. {
  407. for( SCH_SHEET_PATHS_ITER it = begin(); it != end(); ++it )
  408. {
  409. if( (*it).LastScreen() && (*it).LastScreen()->IsSave() )
  410. return true;
  411. }
  412. return false;
  413. }
  414. void SCH_SHEET_LIST::ClearModifyStatus()
  415. {
  416. for( SCH_SHEET_PATHS_ITER it = begin(); it != end(); ++it )
  417. {
  418. if( (*it).LastScreen() )
  419. (*it).LastScreen()->ClrModify();
  420. }
  421. }
  422. void SCH_SHEET_LIST::AnnotatePowerSymbols( PART_LIBS* aLibs )
  423. {
  424. int ref = 1;
  425. for( SCH_SHEET_PATHS_ITER it = begin(); it != end(); ++it )
  426. (*it).AnnotatePowerSymbols( aLibs, &ref );
  427. }
  428. void SCH_SHEET_LIST::GetComponents( PART_LIBS* aLibs, SCH_REFERENCE_LIST& aReferences,
  429. bool aIncludePowerSymbols )
  430. {
  431. for( SCH_SHEET_PATHS_ITER it = begin(); it != end(); ++it )
  432. (*it).GetComponents( aLibs, aReferences, aIncludePowerSymbols );
  433. }
  434. void SCH_SHEET_LIST::GetMultiUnitComponents( PART_LIBS* aLibs,
  435. SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
  436. bool aIncludePowerSymbols )
  437. {
  438. for( SCH_SHEET_PATHS_ITER it = begin(); it != end(); ++it )
  439. {
  440. SCH_MULTI_UNIT_REFERENCE_MAP tempMap;
  441. (*it).GetMultiUnitComponents( aLibs, tempMap );
  442. BOOST_FOREACH( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair, tempMap )
  443. {
  444. // Merge this list into the main one
  445. unsigned n_refs = pair.second.GetCount();
  446. for( unsigned thisRef = 0; thisRef < n_refs; ++thisRef )
  447. {
  448. aRefList[pair.first].AddItem( pair.second[thisRef] );
  449. }
  450. }
  451. }
  452. }
  453. SCH_ITEM* SCH_SHEET_LIST::FindNextItem( KICAD_T aType, SCH_SHEET_PATH** aSheetFoundIn,
  454. SCH_ITEM* aLastItem, bool aWrap )
  455. {
  456. bool hasWrapped = false;
  457. bool firstItemFound = false;
  458. SCH_ITEM* drawItem = NULL;
  459. SCH_SHEET_PATHS_ITER it = begin();
  460. while( it != end() )
  461. {
  462. drawItem = (*it).LastDrawList();
  463. while( drawItem )
  464. {
  465. if( drawItem->Type() == aType )
  466. {
  467. if( aLastItem == NULL || firstItemFound )
  468. {
  469. if( aSheetFoundIn )
  470. *aSheetFoundIn = &(*it);
  471. return drawItem;
  472. }
  473. else if( !firstItemFound && drawItem == aLastItem )
  474. {
  475. firstItemFound = true;
  476. }
  477. }
  478. drawItem = drawItem->Next();
  479. }
  480. ++it;
  481. if( it == end() && aLastItem && aWrap && !hasWrapped )
  482. {
  483. hasWrapped = true;
  484. it = begin();
  485. }
  486. }
  487. return NULL;
  488. }
  489. SCH_ITEM* SCH_SHEET_LIST::FindPreviousItem( KICAD_T aType, SCH_SHEET_PATH** aSheetFoundIn,
  490. SCH_ITEM* aLastItem, bool aWrap )
  491. {
  492. bool hasWrapped = false;
  493. bool firstItemFound = false;
  494. SCH_ITEM* drawItem = NULL;
  495. SCH_SHEET_PATHS_RITER it = rbegin();
  496. while( it != rend() )
  497. {
  498. drawItem = (*it).FirstDrawList();
  499. while( drawItem )
  500. {
  501. if( drawItem->Type() == aType )
  502. {
  503. if( aLastItem == NULL || firstItemFound )
  504. {
  505. if( aSheetFoundIn )
  506. *aSheetFoundIn = &(*it);
  507. return drawItem;
  508. }
  509. else if( !firstItemFound && drawItem == aLastItem )
  510. {
  511. firstItemFound = true;
  512. }
  513. }
  514. drawItem = drawItem->Back();
  515. }
  516. ++it;
  517. if( it == rend() && aLastItem && aWrap && !hasWrapped )
  518. {
  519. hasWrapped = true;
  520. it = rbegin();
  521. }
  522. }
  523. return NULL;
  524. }
  525. bool SCH_SHEET_LIST::SetComponentFootprint( const wxString& aReference,
  526. const wxString& aFootPrint, bool aSetVisible )
  527. {
  528. bool found = false;
  529. for( SCH_SHEET_PATHS_ITER it = begin(); it != end(); ++it )
  530. found = (*it).SetComponentFootprint( aReference, aFootPrint, aSetVisible );
  531. return found;
  532. }
  533. bool SCH_SHEET_LIST::IsComplexHierarchy() const
  534. {
  535. wxString fileName;
  536. for( unsigned i = 0; i < size(); i++ )
  537. {
  538. fileName = at( i ).Last()->GetFileName();
  539. for( unsigned j = 0; j < size(); j++ )
  540. {
  541. if( i == j )
  542. continue;
  543. if( fileName == at( j ).Last()->GetFileName() )
  544. return true;
  545. }
  546. }
  547. return false;
  548. }
  549. bool SCH_SHEET_LIST::TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
  550. const wxString& aDestFileName ) const
  551. {
  552. wxFileName rootFn = g_RootSheet->GetFileName();
  553. wxFileName destFn = aDestFileName;
  554. if( destFn.IsRelative() )
  555. destFn.MakeAbsolute( rootFn.GetPath() );
  556. // Test each SCH_SHEET_PATH in this SCH_SHEET_LIST for potential recursion.
  557. for( unsigned i = 0; i < size(); i++ )
  558. {
  559. // Test each SCH_SHEET_PATH in the source sheet.
  560. for( unsigned j = 0; j < aSrcSheetHierarchy.size(); j++ )
  561. {
  562. const SCH_SHEET_PATH* sheetPath = &aSrcSheetHierarchy[j];
  563. for( unsigned k = 0; k < sheetPath->size(); k++ )
  564. {
  565. if( at( i ).TestForRecursion( sheetPath->GetSheet( k )->GetFileName(),
  566. aDestFileName ) )
  567. return true;
  568. }
  569. }
  570. }
  571. // The source sheet file can safely be added to the destination sheet file.
  572. return false;
  573. }
  574. SCH_SHEET* SCH_SHEET_LIST::FindSheetByName( const wxString& aSheetName )
  575. {
  576. for( unsigned i = 0; i < size(); i++ )
  577. {
  578. SCH_SHEET* sheet = at( i ).FindSheetByName( aSheetName );
  579. if( sheet )
  580. return sheet;
  581. }
  582. return NULL;
  583. }