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.

653 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  5. * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file sch_collectors.cpp
  26. */
  27. #include <macros.h>
  28. #include <trace_helpers.h>
  29. #include <sch_sheet_path.h>
  30. #include <transform.h>
  31. #include <sch_collectors.h>
  32. #include <sch_component.h>
  33. #include <sch_line.h>
  34. #include <sch_bus_entry.h>
  35. const KICAD_T SCH_COLLECTOR::AllItems[] = {
  36. SCH_MARKER_T,
  37. SCH_JUNCTION_T,
  38. SCH_NO_CONNECT_T,
  39. SCH_BUS_BUS_ENTRY_T,
  40. SCH_BUS_WIRE_ENTRY_T,
  41. SCH_LINE_T,
  42. SCH_BITMAP_T,
  43. SCH_TEXT_T,
  44. SCH_LABEL_T,
  45. SCH_GLOBAL_LABEL_T,
  46. SCH_HIER_LABEL_T,
  47. SCH_FIELD_T,
  48. SCH_COMPONENT_T,
  49. LIB_PIN_T,
  50. SCH_SHEET_PIN_T,
  51. SCH_SHEET_T,
  52. EOT
  53. };
  54. const KICAD_T SCH_COLLECTOR::AllItemsButPins[] = {
  55. SCH_MARKER_T,
  56. SCH_JUNCTION_T,
  57. SCH_NO_CONNECT_T,
  58. SCH_BUS_BUS_ENTRY_T,
  59. SCH_BUS_WIRE_ENTRY_T,
  60. SCH_LINE_T,
  61. SCH_BITMAP_T,
  62. SCH_TEXT_T,
  63. SCH_LABEL_T,
  64. SCH_GLOBAL_LABEL_T,
  65. SCH_HIER_LABEL_T,
  66. SCH_FIELD_T,
  67. SCH_COMPONENT_T,
  68. SCH_SHEET_PIN_T,
  69. SCH_SHEET_T,
  70. EOT
  71. };
  72. const KICAD_T SCH_COLLECTOR::EditableItems[] = {
  73. SCH_TEXT_T,
  74. SCH_LABEL_T,
  75. SCH_GLOBAL_LABEL_T,
  76. SCH_HIER_LABEL_T,
  77. SCH_FIELD_T,
  78. SCH_COMPONENT_T,
  79. SCH_SHEET_PIN_T,
  80. SCH_SHEET_T,
  81. SCH_BITMAP_T,
  82. SCH_LINE_T,
  83. EOT
  84. };
  85. const KICAD_T SCH_COLLECTOR::CmpFieldValueOnly[] = {
  86. SCH_FIELD_LOCATE_VALUE_T,
  87. EOT
  88. };
  89. const KICAD_T SCH_COLLECTOR::CmpFieldReferenceOnly[] = {
  90. SCH_FIELD_LOCATE_REFERENCE_T,
  91. EOT
  92. };
  93. const KICAD_T SCH_COLLECTOR::CmpFieldFootprintOnly[] = {
  94. SCH_FIELD_LOCATE_FOOTPRINT_T,
  95. EOT
  96. };
  97. const KICAD_T SCH_COLLECTOR::CmpFieldDatasheetOnly[] = {
  98. SCH_FIELD_LOCATE_DATASHEET_T,
  99. EOT
  100. };
  101. const KICAD_T SCH_COLLECTOR::MovableItems[] = {
  102. SCH_MARKER_T,
  103. SCH_JUNCTION_T,
  104. SCH_NO_CONNECT_T,
  105. SCH_BUS_BUS_ENTRY_T,
  106. SCH_BUS_WIRE_ENTRY_T,
  107. // SCH_LINE_T,
  108. SCH_BITMAP_T,
  109. SCH_TEXT_T,
  110. SCH_LABEL_T,
  111. SCH_GLOBAL_LABEL_T,
  112. SCH_HIER_LABEL_T,
  113. SCH_FIELD_T,
  114. SCH_COMPONENT_T,
  115. SCH_SHEET_PIN_T,
  116. SCH_SHEET_T,
  117. EOT
  118. };
  119. const KICAD_T SCH_COLLECTOR::DraggableItems[] = {
  120. SCH_JUNCTION_T,
  121. SCH_NO_CONNECT_T,
  122. SCH_BUS_BUS_ENTRY_T,
  123. SCH_BUS_WIRE_ENTRY_T,
  124. SCH_LINE_T,
  125. SCH_LABEL_T,
  126. SCH_GLOBAL_LABEL_T,
  127. SCH_HIER_LABEL_T,
  128. SCH_COMPONENT_T,
  129. SCH_SHEET_T,
  130. SCH_TEXT_T,
  131. EOT
  132. };
  133. const KICAD_T SCH_COLLECTOR::RotatableItems[] = {
  134. SCH_TEXT_T,
  135. SCH_LABEL_T,
  136. SCH_GLOBAL_LABEL_T,
  137. SCH_HIER_LABEL_T,
  138. SCH_FIELD_T,
  139. SCH_COMPONENT_T,
  140. SCH_SHEET_T,
  141. SCH_BITMAP_T,
  142. SCH_BUS_BUS_ENTRY_T,
  143. SCH_BUS_WIRE_ENTRY_T,
  144. EOT
  145. };
  146. const KICAD_T SCH_COLLECTOR::ParentItems[] = {
  147. SCH_MARKER_T,
  148. SCH_JUNCTION_T,
  149. SCH_NO_CONNECT_T,
  150. SCH_BUS_BUS_ENTRY_T,
  151. SCH_BUS_WIRE_ENTRY_T,
  152. SCH_LINE_T,
  153. SCH_TEXT_T,
  154. SCH_LABEL_T,
  155. SCH_GLOBAL_LABEL_T,
  156. SCH_HIER_LABEL_T,
  157. SCH_COMPONENT_T,
  158. SCH_SHEET_PIN_T,
  159. SCH_SHEET_T,
  160. SCH_BITMAP_T,
  161. EOT
  162. };
  163. const KICAD_T SCH_COLLECTOR::ComponentsOnly[] = {
  164. SCH_COMPONENT_T,
  165. EOT
  166. };
  167. const KICAD_T SCH_COLLECTOR::SheetsOnly[] = {
  168. SCH_SHEET_T,
  169. EOT
  170. };
  171. const KICAD_T SCH_COLLECTOR::SheetsAndSheetLabels[] = {
  172. SCH_SHEET_PIN_T,
  173. SCH_SHEET_T,
  174. EOT
  175. };
  176. const KICAD_T SCH_COLLECTOR::OrientableItems[] = {
  177. SCH_BUS_BUS_ENTRY_T,
  178. SCH_BUS_WIRE_ENTRY_T,
  179. SCH_COMPONENT_T,
  180. SCH_BITMAP_T,
  181. SCH_SHEET_T,
  182. EOT
  183. };
  184. const KICAD_T SCH_COLLECTOR::CopyableItems[] = {
  185. SCH_TEXT_T,
  186. SCH_LABEL_T,
  187. SCH_GLOBAL_LABEL_T,
  188. SCH_HIER_LABEL_T,
  189. SCH_COMPONENT_T,
  190. EOT
  191. };
  192. const KICAD_T SCH_COLLECTOR::DoubleClickItems[] = {
  193. SCH_TEXT_T,
  194. SCH_LABEL_T,
  195. SCH_GLOBAL_LABEL_T,
  196. SCH_HIER_LABEL_T,
  197. SCH_COMPONENT_T,
  198. SCH_SHEET_T,
  199. SCH_BITMAP_T,
  200. SCH_FIELD_T,
  201. SCH_MARKER_T,
  202. EOT
  203. };
  204. SEARCH_RESULT SCH_COLLECTOR::Inspect( EDA_ITEM* aItem, void* aTestData )
  205. {
  206. if( aItem->Type() != LIB_PIN_T && !aItem->HitTest( m_RefPos ) )
  207. return SEARCH_CONTINUE;
  208. // Pins have special hit testing requirements that are relative to their parent
  209. // SCH_COMPONENT item.
  210. if( aItem->Type() == LIB_PIN_T )
  211. {
  212. wxCHECK_MSG( aTestData && ( (EDA_ITEM*) aTestData )->Type() == SCH_COMPONENT_T,
  213. SEARCH_CONTINUE, wxT( "Cannot inspect invalid data. Bad programmer!" ) );
  214. // Pin hit testing is relative to the components position and orientation in the
  215. // schematic. The hit test position must be converted to library coordinates.
  216. SCH_COMPONENT* component = (SCH_COMPONENT*) aTestData;
  217. TRANSFORM transform = component->GetTransform().InverseTransform();
  218. wxPoint position = transform.TransformCoordinate( m_RefPos - component->GetPosition() );
  219. position.y *= -1; // Y axis polarity in schematic is inverted from library.
  220. if( !aItem->HitTest( position ) )
  221. return SEARCH_CONTINUE;
  222. }
  223. Append( aItem );
  224. return SEARCH_CONTINUE;
  225. }
  226. void SCH_COLLECTOR::Collect( SCH_ITEM* aItem, const KICAD_T aFilterList[],
  227. const wxPoint& aPosition )
  228. {
  229. Empty(); // empty the collection just in case
  230. SetScanTypes( aFilterList );
  231. // remember where the snapshot was taken from and pass refPos to the Inspect() function.
  232. SetRefPos( aPosition );
  233. EDA_ITEM::IterateForward( aItem, m_inspector, NULL, m_ScanTypes );
  234. }
  235. bool SCH_COLLECTOR::IsCorner() const
  236. {
  237. if( GetCount() != 2 )
  238. return false;
  239. bool is_busentry0 = (dynamic_cast<SCH_BUS_ENTRY_BASE*>( m_List[0] ) != NULL);
  240. bool is_busentry1 = (dynamic_cast<SCH_BUS_ENTRY_BASE*>( m_List[1] ) != NULL);
  241. if( (m_List[0]->Type() == SCH_LINE_T) && (m_List[1]->Type() == SCH_LINE_T) )
  242. return ( ( SCH_LINE* ) m_List[0])->GetLayer() == ( ( SCH_LINE* ) m_List[1])->GetLayer();
  243. if( (m_List[0]->Type() == SCH_LINE_T) && is_busentry1 )
  244. return true;
  245. if( is_busentry0 && (m_List[1]->Type() == SCH_LINE_T) )
  246. return true;
  247. return false;
  248. }
  249. bool SCH_COLLECTOR::IsNode( bool aIncludePins ) const
  250. {
  251. for( size_t i = 0; i < m_List.size(); i++ )
  252. {
  253. SCH_ITEM* item = (SCH_ITEM*) m_List[ i ];
  254. KICAD_T type = item->Type();
  255. if( type == SCH_JUNCTION_T )
  256. continue;
  257. if( type == SCH_LINE_T )
  258. {
  259. if( item->GetLayer() != LAYER_WIRE )
  260. return false;
  261. continue;
  262. }
  263. if( type == LIB_PIN_T )
  264. {
  265. if( !aIncludePins )
  266. return false;
  267. continue;
  268. }
  269. // Any other item types indicate that this collection is not a node.
  270. return false;
  271. }
  272. return true;
  273. }
  274. bool SCH_COLLECTOR::IsDraggableJunction() const
  275. {
  276. for( size_t i = 0; i < m_List.size(); i++ )
  277. if( ( (SCH_ITEM*) m_List[ i ] )->Type() == SCH_JUNCTION_T )
  278. return true;
  279. return false;
  280. }
  281. /**
  282. * A singleton item of this class is returned for a weak reference that no longer exists.
  283. * Its sole purpose is to flag the item as having been deleted.
  284. */
  285. class DELETED_SCH_ITEM : public SCH_ITEM
  286. {
  287. public:
  288. DELETED_SCH_ITEM() :
  289. SCH_ITEM( nullptr, NOT_USED )
  290. {}
  291. wxString GetSelectMenuText( EDA_UNITS_T aUnits ) const override
  292. {
  293. return _( "(Deleted Item)" );
  294. }
  295. wxString GetClass() const override
  296. {
  297. return wxT( "DELETED_SCH_ITEM" );
  298. }
  299. // define pure virtuals:
  300. wxPoint GetPosition() const override { return wxPoint(); }
  301. void SetPosition( const wxPoint& ) override {}
  302. void Draw( EDA_DRAW_PANEL* , wxDC* , const wxPoint& ) override {}
  303. #if defined(DEBUG)
  304. void Show( int , std::ostream& ) const override {}
  305. #endif
  306. void Move( const wxPoint& ) override {}
  307. void MirrorY( int ) override {}
  308. void MirrorX( int ) override {}
  309. void Rotate( wxPoint ) override {}
  310. };
  311. DELETED_SCH_ITEM g_DeletedSchItem;
  312. SCH_ITEM* SCH_FIND_COLLECTOR::GetItem( int ndx ) const
  313. {
  314. if( (unsigned)ndx >= (unsigned)GetCount() )
  315. return NULL;
  316. // Do not simply return m_List[ ndx ] as it might have been deleted. Instead
  317. // treat it as a weak reference and search the sheets for an item with the same
  318. // pointer value.
  319. void* weakRef = m_List[ ndx ];
  320. SCH_ITEM* item = &g_DeletedSchItem;
  321. INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* candidate, void* testData )
  322. {
  323. if( (void*) candidate == weakRef )
  324. {
  325. item = (SCH_ITEM*) candidate;
  326. return SEARCH_QUIT;
  327. }
  328. return SEARCH_CONTINUE;
  329. };
  330. for( unsigned i = 0; i < m_sheetPaths.size(); i++ )
  331. {
  332. EDA_ITEM::IterateForward( m_sheetPaths[ i ].LastDrawList(),
  333. inspector, nullptr, SCH_COLLECTOR::AllItems );
  334. }
  335. return item;
  336. }
  337. SCH_ITEM* SCH_FIND_COLLECTOR::operator[]( int ndx ) const
  338. {
  339. return GetItem( ndx );
  340. }
  341. bool SCH_FIND_COLLECTOR::PassedEnd() const
  342. {
  343. bool retv = false;
  344. wxUint32 flags = m_findReplaceData.GetFlags();
  345. if( GetCount() == 0 )
  346. return true;
  347. if( !(flags & FR_SEARCH_WRAP) || (flags & FR_SEARCH_REPLACE) )
  348. {
  349. if( flags & wxFR_DOWN )
  350. {
  351. if( m_foundIndex >= GetCount() )
  352. retv = true;
  353. }
  354. else
  355. {
  356. if( m_foundIndex < 0 )
  357. retv = true;
  358. }
  359. }
  360. return retv;
  361. }
  362. #if defined(DEBUG)
  363. void SCH_FIND_COLLECTOR::dump()
  364. {
  365. int tmp = m_foundIndex;
  366. wxLogTrace( traceFindReplace, wxT( "%d items found to replace %s with %s." ),
  367. GetCount(), GetChars( m_findReplaceData.GetFindString() ),
  368. GetChars( m_findReplaceData.GetReplaceString() ) );
  369. for( m_foundIndex = 0; m_foundIndex < GetCount(); m_foundIndex++ )
  370. wxLogTrace( traceFindReplace, wxT( " " ) + GetText( MILLIMETRES ) );
  371. m_foundIndex = tmp;
  372. }
  373. #endif
  374. void SCH_FIND_COLLECTOR::UpdateIndex()
  375. {
  376. wxUint32 flags = m_findReplaceData.GetFlags();
  377. if( flags & wxFR_DOWN )
  378. {
  379. if( m_foundIndex < GetCount() )
  380. m_foundIndex += 1;
  381. if( (m_foundIndex >= GetCount()) && (flags & FR_SEARCH_WRAP) )
  382. m_foundIndex = 0;
  383. }
  384. else
  385. {
  386. if( m_foundIndex >= 0 )
  387. m_foundIndex -= 1;
  388. if( (m_foundIndex < 0) && (flags & FR_SEARCH_WRAP) )
  389. m_foundIndex = GetCount() - 1;
  390. }
  391. }
  392. SCH_FIND_COLLECTOR_DATA SCH_FIND_COLLECTOR::GetFindData( int aIndex )
  393. {
  394. wxCHECK_MSG( (unsigned) aIndex < m_data.size(), SCH_FIND_COLLECTOR_DATA(),
  395. wxT( "Attempt to get find data outside of list boundary." ) );
  396. return m_data[ aIndex ];
  397. }
  398. wxString SCH_FIND_COLLECTOR::GetText( EDA_UNITS_T aUnits )
  399. {
  400. wxCHECK_MSG( (GetCount() != 0) && IsValidIndex( m_foundIndex ), wxEmptyString,
  401. wxT( "Cannot get found item at invalid index." ) );
  402. SCH_FIND_COLLECTOR_DATA data = m_data[ m_foundIndex ];
  403. EDA_ITEM* foundItem = GetItem( m_foundIndex );
  404. if( data.GetParent() )
  405. {
  406. return wxString::Format( _( "Match %i of %i: %s of %s in sheet %s" ),
  407. m_foundIndex + 1,
  408. GetCount(),
  409. foundItem->GetSelectMenuText( aUnits ),
  410. data.GetParent()->GetSelectMenuText( aUnits ),
  411. data.GetSheetPath() );
  412. }
  413. else
  414. {
  415. return wxString::Format( _( "Match %i of %i: %s in sheet %s" ),
  416. m_foundIndex + 1,
  417. GetCount(),
  418. foundItem->GetSelectMenuText( aUnits ),
  419. data.GetSheetPath() );
  420. }
  421. }
  422. EDA_ITEM* SCH_FIND_COLLECTOR::GetItem( SCH_FIND_COLLECTOR_DATA& aData )
  423. {
  424. if( PassedEnd() )
  425. return NULL;
  426. aData = m_data[ m_foundIndex ];
  427. return GetItem( m_foundIndex );
  428. }
  429. bool SCH_FIND_COLLECTOR::ReplaceItem( SCH_SHEET_PATH* aSheetPath )
  430. {
  431. if( PassedEnd() )
  432. return false;
  433. wxCHECK_MSG( IsValidIndex( m_foundIndex ), false,
  434. wxT( "Invalid replace list index in SCH_FIND_COLLECTOR." ) );
  435. EDA_ITEM* item = GetItem( m_foundIndex );
  436. bool replaced = item->Replace( m_findReplaceData, aSheetPath );
  437. return replaced;
  438. }
  439. SEARCH_RESULT SCH_FIND_COLLECTOR::Inspect( EDA_ITEM* aItem, void* aTestData )
  440. {
  441. wxPoint position;
  442. if( aItem->Matches( m_findReplaceData, m_currentSheetPath, &position ) )
  443. {
  444. if( aItem->Type() == LIB_PIN_T )
  445. {
  446. wxCHECK_MSG( aTestData && ( (EDA_ITEM*) aTestData )->Type() == SCH_COMPONENT_T,
  447. SEARCH_CONTINUE, wxT( "Cannot inspect invalid data. Bad programmer!" ) );
  448. // Pin positions are relative to their parent component's position and
  449. // orientation in the schematic. The pin's position must be converted
  450. // schematic coordinates.
  451. SCH_COMPONENT* component = (SCH_COMPONENT*) aTestData;
  452. TRANSFORM transform = component->GetTransform();
  453. position.y = -position.y;
  454. position = transform.TransformCoordinate( position ) + component->GetPosition();
  455. }
  456. Append( aItem );
  457. m_data.push_back( SCH_FIND_COLLECTOR_DATA( position,
  458. m_currentSheetPath->PathHumanReadable(),
  459. (SCH_ITEM*) aTestData ) );
  460. }
  461. return SEARCH_CONTINUE;
  462. }
  463. void SCH_FIND_COLLECTOR::SetReplaceString( const wxString &aReplaceString )
  464. {
  465. m_findReplaceData.SetReplaceString( aReplaceString );
  466. }
  467. void SCH_FIND_COLLECTOR::Collect( SCH_FIND_REPLACE_DATA& aFindReplaceData,
  468. SCH_SHEET_PATH* aSheetPath )
  469. {
  470. if( !IsSearchRequired( aFindReplaceData ) && !m_List.empty() && !m_forceSearch )
  471. return;
  472. m_findReplaceData = aFindReplaceData;
  473. Empty(); // empty the collection just in case
  474. m_data.clear();
  475. m_foundIndex = 0;
  476. m_sheetPaths.clear();
  477. SetForceSearch( false );
  478. if( aSheetPath )
  479. {
  480. m_currentSheetPath = aSheetPath;
  481. m_sheetPaths.push_back( *m_currentSheetPath );
  482. EDA_ITEM::IterateForward( aSheetPath->LastDrawList(), m_inspector, NULL, m_ScanTypes );
  483. }
  484. else
  485. {
  486. SCH_SHEET_LIST schematic( g_RootSheet );
  487. for( unsigned i = 0; i < schematic.size(); i++ )
  488. {
  489. m_currentSheetPath = &schematic[i];
  490. m_sheetPaths.push_back( *m_currentSheetPath );
  491. EDA_ITEM::IterateForward( m_currentSheetPath->LastDrawList(), m_inspector, NULL, m_ScanTypes );
  492. }
  493. }
  494. #if defined(DEBUG)
  495. dump();
  496. #endif
  497. if( m_List.size() != m_data.size() )
  498. {
  499. wxFAIL_MSG( wxT( "List size mismatch." ) );
  500. m_List.clear();
  501. m_data.clear();
  502. }
  503. }
  504. SEARCH_RESULT SCH_TYPE_COLLECTOR::Inspect( EDA_ITEM* aItem, void* testData )
  505. {
  506. // The Vist() function only visits the testItem if its type was in the
  507. // the scanList, so therefore we can collect anything given to us here.
  508. Append( aItem );
  509. return SEARCH_CONTINUE;
  510. }
  511. void SCH_TYPE_COLLECTOR::Collect( SCH_ITEM* aItem, const KICAD_T aFilterList[] )
  512. {
  513. Empty(); // empty the collection
  514. SetScanTypes( aFilterList );
  515. EDA_ITEM::IterateForward( aItem, m_inspector, NULL, m_ScanTypes );
  516. }