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.

704 lines
16 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 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 1992-2011 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 <netlist.h>
  35. #include <class_library.h>
  36. #include <sch_sheet.h>
  37. #include <sch_sheet_path.h>
  38. #include <sch_component.h>
  39. #include <template_fieldnames.h>
  40. #include <dialogs/dialog_schematic_find.h>
  41. SCH_SHEET_PATH::SCH_SHEET_PATH()
  42. {
  43. for( int i = 0; i<DSLSZ; i++ )
  44. m_sheets[i] = NULL;
  45. m_numSheets = 0;
  46. }
  47. bool SCH_SHEET_PATH::BuildSheetPathInfoFromSheetPathValue( const wxString& aPath, bool aFound )
  48. {
  49. if( aFound )
  50. return true;
  51. if( GetSheetsCount() == 0 )
  52. Push( g_RootSheet );
  53. if( aPath == Path() )
  54. return true;
  55. SCH_ITEM* schitem = LastDrawList();
  56. while( schitem && GetSheetsCount() < NB_MAX_SHEET )
  57. {
  58. if( schitem->Type() == SCH_SHEET_T )
  59. {
  60. SCH_SHEET* sheet = (SCH_SHEET*) schitem;
  61. Push( sheet );
  62. if( aPath == Path() )
  63. return true;
  64. if( BuildSheetPathInfoFromSheetPathValue( aPath ) )
  65. return true;
  66. Pop();
  67. }
  68. schitem = schitem->Next();
  69. }
  70. return false;
  71. }
  72. int SCH_SHEET_PATH::Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const
  73. {
  74. if( m_numSheets > aSheetPathToTest.m_numSheets )
  75. return 1;
  76. if( m_numSheets < aSheetPathToTest.m_numSheets )
  77. return -1;
  78. //otherwise, same number of sheets.
  79. for( unsigned i = 0; i<m_numSheets; i++ )
  80. {
  81. if( m_sheets[i]->GetTimeStamp() > aSheetPathToTest.m_sheets[i]->GetTimeStamp() )
  82. return 1;
  83. if( m_sheets[i]->GetTimeStamp() < aSheetPathToTest.m_sheets[i]->GetTimeStamp() )
  84. return -1;
  85. }
  86. return 0;
  87. }
  88. SCH_SHEET* SCH_SHEET_PATH::Last()
  89. {
  90. if( m_numSheets )
  91. return m_sheets[m_numSheets - 1];
  92. return NULL;
  93. }
  94. SCH_SCREEN* SCH_SHEET_PATH::LastScreen()
  95. {
  96. SCH_SHEET* lastSheet = Last();
  97. if( lastSheet )
  98. return lastSheet->GetScreen();
  99. return NULL;
  100. }
  101. SCH_ITEM* SCH_SHEET_PATH::LastDrawList()
  102. {
  103. SCH_SHEET* lastSheet = Last();
  104. if( lastSheet && lastSheet->GetScreen() )
  105. return lastSheet->GetScreen()->GetDrawItems();
  106. return NULL;
  107. }
  108. SCH_ITEM* SCH_SHEET_PATH::FirstDrawList()
  109. {
  110. SCH_ITEM* item = NULL;
  111. if( m_numSheets && m_sheets[0]->GetScreen() )
  112. item = m_sheets[0]->GetScreen()->GetDrawItems();
  113. /* @fixme - These lists really should be one of the boost pointer containers. This
  114. * is a brain dead hack to allow reverse iteration of EDA_ITEM linked
  115. * list.
  116. */
  117. SCH_ITEM* lastItem = NULL;
  118. while( item != NULL )
  119. {
  120. lastItem = item;
  121. item = item->Next();
  122. }
  123. return lastItem;
  124. }
  125. void SCH_SHEET_PATH::Push( SCH_SHEET* aSheet )
  126. {
  127. wxCHECK_RET( m_numSheets < DSLSZ,
  128. wxString::Format( _( "Schematic sheets can only be nested %d levels deep." ),
  129. DSLSZ ) );
  130. m_sheets[ m_numSheets ] = aSheet;
  131. m_numSheets++;
  132. }
  133. SCH_SHEET* SCH_SHEET_PATH::Pop()
  134. {
  135. if( m_numSheets > 0 )
  136. {
  137. m_numSheets--;
  138. return m_sheets[m_numSheets];
  139. }
  140. return NULL;
  141. }
  142. wxString SCH_SHEET_PATH::Path() const
  143. {
  144. wxString s, t;
  145. s = wxT( "/" ); // This is the root path
  146. // start at 1 to avoid the root sheet,
  147. // which does not need to be added to the path
  148. // it's timestamp changes anyway.
  149. for( unsigned i = 1; i < m_numSheets; i++ )
  150. {
  151. t.Printf( _( "%8.8lX/" ), m_sheets[i]->GetTimeStamp() );
  152. s = s + t;
  153. }
  154. return s;
  155. }
  156. wxString SCH_SHEET_PATH::PathHumanReadable() const
  157. {
  158. wxString s, t;
  159. s = wxT( "/" );
  160. // start at 1 to avoid the root sheet, as above.
  161. for( unsigned i = 1; i< m_numSheets; i++ )
  162. {
  163. s = s + m_sheets[i]->GetName() + wxT( "/" );
  164. }
  165. return s;
  166. }
  167. void SCH_SHEET_PATH::UpdateAllScreenReferences()
  168. {
  169. EDA_ITEM* t = LastDrawList();
  170. while( t )
  171. {
  172. if( t->Type() == SCH_COMPONENT_T )
  173. {
  174. SCH_COMPONENT* component = (SCH_COMPONENT*) t;
  175. component->GetField( REFERENCE )->m_Text = component->GetRef( this );
  176. component->SetUnit( component->GetUnitSelection( this ) );
  177. }
  178. t = t->Next();
  179. }
  180. }
  181. void SCH_SHEET_PATH::AnnotatePowerSymbols( int* aReference )
  182. {
  183. int ref = 1;
  184. if( aReference != NULL )
  185. ref = *aReference;
  186. for( EDA_ITEM* item = LastDrawList(); item != NULL; item = item->Next() )
  187. {
  188. if( item->Type() != SCH_COMPONENT_T )
  189. continue;
  190. SCH_COMPONENT* component = (SCH_COMPONENT*) item;
  191. LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( component->GetLibName() );
  192. if( ( entry == NULL ) || !entry->IsPower() )
  193. continue;
  194. wxString refstr = component->GetPrefix();
  195. //str will be "C?" or so after the ClearAnnotation call.
  196. while( refstr.Last() == '?' )
  197. refstr.RemoveLast();
  198. if( !refstr.StartsWith( wxT( "#" ) ) )
  199. refstr = wxT( "#" ) + refstr;
  200. refstr << wxT( "0" ) << ref;
  201. component->SetRef( this, refstr );
  202. ref++;
  203. }
  204. if( aReference != NULL )
  205. *aReference = ref;
  206. }
  207. void SCH_SHEET_PATH::GetComponents( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols )
  208. {
  209. // Search to sheet path number:
  210. int sheetnumber = 1; // 1 = root
  211. SCH_SHEET_LIST sheetList;
  212. for( SCH_SHEET_PATH* path = sheetList.GetFirst(); path != NULL;
  213. path = sheetList.GetNext(), sheetnumber++ )
  214. {
  215. if( Cmp(*path) == 0 )
  216. break;
  217. }
  218. for( SCH_ITEM* item = LastDrawList(); item != NULL; item = item->Next() )
  219. {
  220. if( item->Type() == SCH_COMPONENT_T )
  221. {
  222. SCH_COMPONENT* component = (SCH_COMPONENT*) item;
  223. // Skip pseudo components, which have a reference starting with #. This mainly
  224. // effects power symbols.
  225. if( !aIncludePowerSymbols && component->GetRef( this )[0] == wxT( '#' ) )
  226. continue;
  227. LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( component->GetLibName() );
  228. if( entry == NULL )
  229. continue;
  230. SCH_REFERENCE reference = SCH_REFERENCE( component, entry, *this );
  231. reference.SetSheetNumber( sheetnumber );
  232. aReferences.AddItem( reference );
  233. }
  234. }
  235. }
  236. SCH_ITEM* SCH_SHEET_PATH::FindNextItem( KICAD_T aType, SCH_ITEM* aLastItem, bool aWrap )
  237. {
  238. bool hasWrapped = false;
  239. bool firstItemFound = false;
  240. SCH_ITEM* drawItem = LastDrawList();
  241. while( drawItem != NULL )
  242. {
  243. if( drawItem->Type() == aType )
  244. {
  245. if( aLastItem == NULL || firstItemFound )
  246. {
  247. return drawItem;
  248. }
  249. else if( !firstItemFound && drawItem == aLastItem )
  250. {
  251. firstItemFound = true;
  252. }
  253. }
  254. drawItem = drawItem->Next();
  255. if( drawItem == NULL && aLastItem && aWrap && !hasWrapped )
  256. {
  257. hasWrapped = true;
  258. drawItem = LastDrawList();
  259. }
  260. }
  261. return NULL;
  262. }
  263. SCH_ITEM* SCH_SHEET_PATH::FindPreviousItem( KICAD_T aType, SCH_ITEM* aLastItem, bool aWrap )
  264. {
  265. bool hasWrapped = false;
  266. bool firstItemFound = false;
  267. SCH_ITEM* drawItem = FirstDrawList();
  268. while( drawItem != NULL )
  269. {
  270. if( drawItem->Type() == aType )
  271. {
  272. if( aLastItem == NULL || firstItemFound )
  273. {
  274. return drawItem;
  275. }
  276. else if( !firstItemFound && drawItem == aLastItem )
  277. {
  278. firstItemFound = true;
  279. }
  280. }
  281. drawItem = drawItem->Back();
  282. if( drawItem == NULL && aLastItem && aWrap && !hasWrapped )
  283. {
  284. hasWrapped = true;
  285. drawItem = FirstDrawList();
  286. }
  287. }
  288. return NULL;
  289. }
  290. bool SCH_SHEET_PATH::SetComponentFootprint( const wxString& aReference, const wxString& aFootPrint,
  291. bool aSetVisible )
  292. {
  293. SCH_SCREEN* screen = LastScreen();
  294. if( screen == NULL )
  295. return false;
  296. return screen->SetComponentFootprint( this, aReference, aFootPrint, aSetVisible );
  297. }
  298. SCH_SHEET_PATH& SCH_SHEET_PATH::operator=( const SCH_SHEET_PATH& d1 )
  299. {
  300. if( this == &d1 ) // Self assignment is bad!
  301. return *this;
  302. m_numSheets = d1.m_numSheets;
  303. unsigned i;
  304. for( i = 0; i < m_numSheets; i++ )
  305. m_sheets[i] = d1.m_sheets[i];
  306. for( ; i < DSLSZ; i++ )
  307. m_sheets[i] = 0;
  308. return *this;
  309. }
  310. bool SCH_SHEET_PATH::operator==( const SCH_SHEET_PATH& d1 ) const
  311. {
  312. if( m_numSheets != d1.m_numSheets )
  313. return false;
  314. for( unsigned i = 0; i < m_numSheets; i++ )
  315. {
  316. if( m_sheets[i] != d1.m_sheets[i] )
  317. return false;
  318. }
  319. return true;
  320. }
  321. /********************************************************************/
  322. /* Class SCH_SHEET_LIST to handle the list of Sheets in a hierarchy */
  323. /********************************************************************/
  324. SCH_SHEET_LIST::SCH_SHEET_LIST( SCH_SHEET* aSheet )
  325. {
  326. m_index = 0;
  327. m_count = 0;
  328. m_List = NULL;
  329. if( aSheet == NULL )
  330. aSheet = g_RootSheet;
  331. BuildSheetList( aSheet );
  332. }
  333. SCH_SHEET_PATH* SCH_SHEET_LIST::GetFirst()
  334. {
  335. m_index = 0;
  336. if( GetCount() > 0 )
  337. return &( m_List[0] );
  338. return NULL;
  339. }
  340. SCH_SHEET_PATH* SCH_SHEET_LIST::GetNext()
  341. {
  342. if( m_index < GetCount() )
  343. m_index++;
  344. return GetSheet( m_index );
  345. }
  346. SCH_SHEET_PATH* SCH_SHEET_LIST::GetLast()
  347. {
  348. if( GetCount() == 0 )
  349. return NULL;
  350. m_index = GetCount() - 1;
  351. return GetSheet( m_index );
  352. }
  353. SCH_SHEET_PATH* SCH_SHEET_LIST::GetPrevious()
  354. {
  355. if( m_index == 0 )
  356. return NULL;
  357. m_index -= 1;
  358. return GetSheet( m_index );
  359. }
  360. SCH_SHEET_PATH* SCH_SHEET_LIST::GetSheet( int aIndex )
  361. {
  362. if( aIndex < GetCount() )
  363. return &( m_List[aIndex] );
  364. return NULL;
  365. }
  366. SCH_SHEET_PATH* SCH_SHEET_LIST::GetSheet( const wxString aPath, bool aHumanReadable )
  367. {
  368. SCH_SHEET_PATH* sheet = GetFirst();
  369. wxString sheetPath;
  370. while( sheet != NULL )
  371. {
  372. sheetPath = ( aHumanReadable ) ? sheet->PathHumanReadable() : sheet->Path();
  373. if( sheetPath == aPath )
  374. return sheet;
  375. sheet = GetNext();
  376. }
  377. return NULL;
  378. }
  379. void SCH_SHEET_LIST::BuildSheetList( SCH_SHEET* aSheet )
  380. {
  381. if( m_List == NULL )
  382. {
  383. int count = aSheet->CountSheets();
  384. m_count = count;
  385. m_index = 0;
  386. m_List = new SCH_SHEET_PATH[ count ];
  387. m_currList.Clear();
  388. }
  389. m_currList.Push( aSheet );
  390. m_List[m_index] = m_currList;
  391. m_index++;
  392. if( aSheet->GetScreen() != NULL )
  393. {
  394. EDA_ITEM* strct = m_currList.LastDrawList();
  395. while( strct )
  396. {
  397. if( strct->Type() == SCH_SHEET_T )
  398. {
  399. SCH_SHEET* sheet = (SCH_SHEET*) strct;
  400. BuildSheetList( sheet );
  401. }
  402. strct = strct->Next();
  403. }
  404. }
  405. m_currList.Pop();
  406. }
  407. bool SCH_SHEET_LIST::IsModified()
  408. {
  409. for( SCH_SHEET_PATH* sheet = GetFirst(); sheet != NULL; sheet = GetNext() )
  410. {
  411. if( sheet->LastScreen() && sheet->LastScreen()->IsModify() )
  412. return true;
  413. }
  414. return false;
  415. }
  416. bool SCH_SHEET_LIST::IsAutoSaveRequired()
  417. {
  418. for( SCH_SHEET_PATH* sheet = GetFirst(); sheet != NULL; sheet = GetNext() )
  419. {
  420. if( sheet->LastScreen() && sheet->LastScreen()->IsSave() )
  421. return true;
  422. }
  423. return false;
  424. }
  425. void SCH_SHEET_LIST::ClearModifyStatus()
  426. {
  427. for( SCH_SHEET_PATH* sheet = GetFirst(); sheet != NULL; sheet = GetNext() )
  428. {
  429. if( sheet->LastScreen() )
  430. sheet->LastScreen()->ClrModify();
  431. }
  432. }
  433. void SCH_SHEET_LIST::AnnotatePowerSymbols()
  434. {
  435. int ref = 1;
  436. for( SCH_SHEET_PATH* path = GetFirst(); path != NULL; path = GetNext() )
  437. path->AnnotatePowerSymbols( &ref );
  438. }
  439. void SCH_SHEET_LIST::GetComponents( SCH_REFERENCE_LIST& aReferences,
  440. bool aIncludePowerSymbols )
  441. {
  442. for( SCH_SHEET_PATH* path = GetFirst(); path != NULL; path = GetNext() )
  443. path->GetComponents( aReferences, aIncludePowerSymbols );
  444. }
  445. SCH_ITEM* SCH_SHEET_LIST::FindNextItem( KICAD_T aType, SCH_SHEET_PATH** aSheetFoundIn,
  446. SCH_ITEM* aLastItem, bool aWrap )
  447. {
  448. bool hasWrapped = false;
  449. bool firstItemFound = false;
  450. SCH_ITEM* drawItem = NULL;
  451. SCH_SHEET_PATH* sheet = GetFirst();
  452. while( sheet != NULL )
  453. {
  454. drawItem = sheet->LastDrawList();
  455. while( drawItem != NULL )
  456. {
  457. if( drawItem->Type() == aType )
  458. {
  459. if( aLastItem == NULL || firstItemFound )
  460. {
  461. if( aSheetFoundIn )
  462. *aSheetFoundIn = sheet;
  463. return drawItem;
  464. }
  465. else if( !firstItemFound && drawItem == aLastItem )
  466. {
  467. firstItemFound = true;
  468. }
  469. }
  470. drawItem = drawItem->Next();
  471. }
  472. sheet = GetNext();
  473. if( sheet == NULL && aLastItem && aWrap && !hasWrapped )
  474. {
  475. hasWrapped = true;
  476. sheet = GetFirst();
  477. }
  478. }
  479. return NULL;
  480. }
  481. SCH_ITEM* SCH_SHEET_LIST::FindPreviousItem( KICAD_T aType, SCH_SHEET_PATH** aSheetFoundIn,
  482. SCH_ITEM* aLastItem, bool aWrap )
  483. {
  484. bool hasWrapped = false;
  485. bool firstItemFound = false;
  486. SCH_ITEM* drawItem = NULL;
  487. SCH_SHEET_PATH* sheet = GetLast();
  488. while( sheet != NULL )
  489. {
  490. drawItem = sheet->FirstDrawList();
  491. while( drawItem != NULL )
  492. {
  493. if( drawItem->Type() == aType )
  494. {
  495. if( aLastItem == NULL || firstItemFound )
  496. {
  497. if( aSheetFoundIn )
  498. *aSheetFoundIn = sheet;
  499. return drawItem;
  500. }
  501. else if( !firstItemFound && drawItem == aLastItem )
  502. {
  503. firstItemFound = true;
  504. }
  505. }
  506. drawItem = drawItem->Back();
  507. }
  508. sheet = GetPrevious();
  509. if( sheet == NULL && aLastItem && aWrap && !hasWrapped )
  510. {
  511. hasWrapped = true;
  512. sheet = GetLast();
  513. }
  514. }
  515. return NULL;
  516. }
  517. bool SCH_SHEET_LIST::SetComponentFootprint( const wxString& aReference,
  518. const wxString& aFootPrint, bool aSetVisible )
  519. {
  520. bool found = false;
  521. for( SCH_SHEET_PATH* path = GetFirst(); path != NULL; path = GetNext() )
  522. found = path->SetComponentFootprint( aReference, aFootPrint, aSetVisible );
  523. return found;
  524. }