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.

861 lines
27 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
  5. * Copyright (C) 2004-2016 KiCad Developers, see change_log.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 bus-wire-junction.cpp
  26. * @brief Code for editing buses, wires, and junctions.
  27. */
  28. #include <fctsys.h>
  29. #include <gr_basic.h>
  30. #include <class_drawpanel.h>
  31. #include <sch_edit_frame.h>
  32. #include <lib_draw_item.h>
  33. #include <lib_pin.h>
  34. #include <general.h>
  35. #include <sch_bus_entry.h>
  36. #include <sch_junction.h>
  37. #include <sch_line.h>
  38. #include <sch_no_connect.h>
  39. #include <sch_text.h>
  40. #include <sch_component.h>
  41. #include <sch_sheet.h>
  42. static void AbortCreateNewLine( EDA_DRAW_PANEL* aPanel, wxDC* aDC );
  43. static void ComputeBreakPoint( SCH_SCREEN* aScreen, SCH_LINE* aSegment, wxPoint& new_pos );
  44. static DLIST< SCH_ITEM > s_wires; // when creating a new set of wires,
  45. // stores here the new wires.
  46. /**
  47. * In a contiguous list of wires, remove wires that backtrack over the previous
  48. * wire. Example:
  49. *
  50. * Wire is added:
  51. * ---------------------------------------->
  52. *
  53. * A second wire backtracks over it:
  54. * -------------------<====================>
  55. *
  56. * RemoveBacktracks is called:
  57. * ------------------->
  58. */
  59. static void RemoveBacktracks( DLIST<SCH_ITEM>& aWires )
  60. {
  61. EDA_ITEM* first = aWires.GetFirst();
  62. std::vector<SCH_LINE*> last_lines;
  63. for( EDA_ITEM* p = first; p; )
  64. {
  65. SCH_LINE *line = static_cast<SCH_LINE*>( p );
  66. p = line->Next();
  67. if( line->IsNull() )
  68. {
  69. delete s_wires.Remove( line );
  70. continue;
  71. }
  72. if( !last_lines.empty() )
  73. {
  74. SCH_LINE* last_line = last_lines[last_lines.size() - 1];
  75. bool contiguous = ( last_line->GetEndPoint() == line->GetStartPoint() );
  76. bool backtracks = IsPointOnSegment( last_line->GetStartPoint(),
  77. last_line->GetEndPoint(), line->GetEndPoint() );
  78. bool total_backtrack = ( last_line->GetStartPoint() == line->GetEndPoint() );
  79. if( contiguous && backtracks )
  80. {
  81. if( total_backtrack )
  82. {
  83. delete s_wires.Remove( last_line );
  84. delete s_wires.Remove( line );
  85. last_lines.pop_back();
  86. }
  87. else
  88. {
  89. last_line->SetEndPoint( line->GetEndPoint() );
  90. delete s_wires.Remove( line );
  91. }
  92. }
  93. else
  94. {
  95. last_lines.push_back( line );
  96. }
  97. }
  98. else
  99. {
  100. last_lines.push_back( line );
  101. }
  102. }
  103. }
  104. /**
  105. * Mouse capture callback for drawing line segments.
  106. */
  107. static void DrawSegment( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition,
  108. bool aErase )
  109. {
  110. SCH_LINE* segment;
  111. if( s_wires.GetCount() == 0 )
  112. return;
  113. segment = (SCH_LINE*) s_wires.begin();
  114. COLOR4D color = GetLayerColor( segment->GetLayer() );
  115. if( aErase )
  116. {
  117. while( segment )
  118. {
  119. if( !segment->IsNull() ) // Redraw if segment length != 0
  120. segment->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, color );
  121. segment = segment->Next();
  122. }
  123. }
  124. SCH_EDIT_FRAME* frame = (SCH_EDIT_FRAME*) aPanel->GetParent();
  125. wxPoint endpos = frame->GetCrossHairPosition();
  126. if( frame->GetForceHVLines() ) /* Coerce the line to vertical or horizontal one: */
  127. ComputeBreakPoint( frame->GetScreen(), (SCH_LINE*) s_wires.GetLast()->Back(), endpos );
  128. else
  129. ( (SCH_LINE*) s_wires.GetLast() )->SetEndPoint( endpos );
  130. segment = (SCH_LINE*) s_wires.begin();
  131. while( segment )
  132. {
  133. if( !segment->IsNull() ) // Redraw if segment length != 0
  134. segment->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, color );
  135. segment = segment->Next();
  136. }
  137. }
  138. void SCH_EDIT_FRAME::BeginSegment( wxDC* DC, int type )
  139. {
  140. SCH_LINE* segment;
  141. SCH_LINE* nextSegment;
  142. wxPoint cursorpos = GetCrossHairPosition();
  143. // We should know if a segment is currently in progress
  144. segment = (SCH_LINE*) GetScreen()->GetCurItem();
  145. if( segment ) // a current item exists, but not necessary a currently edited item
  146. {
  147. if( !segment->GetFlags() || ( segment->Type() != SCH_LINE_T ) )
  148. {
  149. if( segment->GetFlags() )
  150. {
  151. wxLogDebug( wxT( "BeginSegment: item->GetFlags()== %X" ),
  152. segment->GetFlags() );
  153. }
  154. // no wire, bus or graphic line in progress
  155. segment = NULL;
  156. }
  157. }
  158. if( !segment ) // first point : Create the first wire or bus segment
  159. {
  160. switch( type )
  161. {
  162. default:
  163. segment = new SCH_LINE( cursorpos, LAYER_NOTES );
  164. break;
  165. case LAYER_WIRE:
  166. segment = new SCH_LINE( cursorpos, LAYER_WIRE );
  167. /* A junction will be created later, when we'll know the
  168. * segment end position, and if the junction is really needed */
  169. break;
  170. case LAYER_BUS:
  171. segment = new SCH_LINE( cursorpos, LAYER_BUS );
  172. break;
  173. }
  174. segment->SetFlags( IS_NEW );
  175. s_wires.PushBack( segment );
  176. GetScreen()->SetCurItem( segment );
  177. // We need 2 segments to go from a given start pin to an end point when the horizontal
  178. // and vertical lines only switch is on.
  179. if( GetForceHVLines() )
  180. {
  181. nextSegment = new SCH_LINE( *segment );
  182. nextSegment->SetFlags( IS_NEW );
  183. s_wires.PushBack( nextSegment );
  184. GetScreen()->SetCurItem( nextSegment );
  185. }
  186. m_canvas->SetMouseCapture( DrawSegment, AbortCreateNewLine );
  187. SetRepeatItem( NULL );
  188. }
  189. else // A segment is in progress: terminates the current segment and add a new segment.
  190. {
  191. SCH_LINE* prevSegment = segment->Back();
  192. // Be aware prevSegment can be null when the horizontal and vertical lines only switch is off
  193. // when we create the first segment.
  194. if( !GetForceHVLines() )
  195. {
  196. // If only one segment is needed and it has a zero length, do not create a new one.
  197. if( segment->IsNull() )
  198. return;
  199. }
  200. else
  201. {
  202. wxCHECK_RET( prevSegment != NULL, wxT( "Failed to create second line segment." ) );
  203. // If two segments are required and they both have zero length, do not
  204. // create a new one.
  205. if( prevSegment && prevSegment->IsNull() && segment->IsNull() )
  206. return;
  207. }
  208. m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
  209. // Terminate the command if the end point is on a pin, junction, or another wire or bus.
  210. if( GetScreen()->IsTerminalPoint( cursorpos, segment->GetLayer() ) )
  211. {
  212. EndSegment();
  213. return;
  214. }
  215. // Create a new segment, and chain it after the current new segment.
  216. nextSegment = new SCH_LINE( *segment );
  217. nextSegment->SetStartPoint( cursorpos );
  218. s_wires.PushBack( nextSegment );
  219. segment->SetEndPoint( cursorpos );
  220. segment->ClearFlags( IS_NEW );
  221. segment->SetFlags( SELECTED );
  222. nextSegment->SetFlags( IS_NEW );
  223. GetScreen()->SetCurItem( nextSegment );
  224. m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
  225. }
  226. }
  227. void SCH_EDIT_FRAME::GetSchematicConnections( std::vector< wxPoint >& aConnections )
  228. {
  229. for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = item->Next() )
  230. {
  231. // Avoid items that are changing
  232. if( !( item->GetFlags() & ( IS_DRAGGED | IS_MOVED | IS_DELETED ) ) )
  233. item->GetConnectionPoints( aConnections );
  234. }
  235. // We always have some overlapping connection points. Drop duplicates here
  236. std::sort( aConnections.begin(), aConnections.end(),
  237. []( const wxPoint& a, const wxPoint& b ) -> bool
  238. { return a.x < b.x || (a.x == b.x && a.y < b.y); } );
  239. aConnections.erase( unique( aConnections.begin(), aConnections.end() ), aConnections.end() );
  240. }
  241. void SCH_EDIT_FRAME::EndSegment()
  242. {
  243. SCH_SCREEN* screen = GetScreen();
  244. SCH_LINE* segment = (SCH_LINE*) screen->GetCurItem();
  245. PICKED_ITEMS_LIST itemList;
  246. if( segment == NULL || segment->Type() != SCH_LINE_T || !segment->IsNew() )
  247. return;
  248. // Remove segments backtracking over others
  249. RemoveBacktracks( s_wires );
  250. if( s_wires.GetCount() == 0 )
  251. return;
  252. // Collect the possible connection points for the new lines
  253. std::vector< wxPoint > connections;
  254. std::vector< wxPoint > new_ends;
  255. GetSchematicConnections( connections );
  256. // Check each new segment for possible junctions and add/split if needed
  257. for( SCH_ITEM* wire = s_wires.GetFirst(); wire; wire=wire->Next() )
  258. {
  259. SCH_LINE* test_line = (SCH_LINE*) wire;
  260. if( wire->GetFlags() & SKIP_STRUCT )
  261. continue;
  262. wire->GetConnectionPoints( new_ends );
  263. for( auto i : connections )
  264. {
  265. if( IsPointOnSegment( test_line->GetStartPoint(), test_line->GetEndPoint(), i ) )
  266. {
  267. new_ends.push_back( i );
  268. }
  269. }
  270. itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) );
  271. }
  272. // Get the last non-null wire (this is the last created segment).
  273. SetRepeatItem( segment = (SCH_LINE*) s_wires.GetLast() );
  274. // Add the new wires
  275. screen->Append( s_wires );
  276. SaveCopyInUndoList(itemList, UR_NEW);
  277. // Correct and remove segments that need to be merged.
  278. SchematicCleanUp( true );
  279. for( auto i : new_ends )
  280. {
  281. if( screen->IsJunctionNeeded( i, true ) )
  282. AddJunction( i, true );
  283. }
  284. screen->TestDanglingEnds();
  285. screen->ClearDrawingState();
  286. screen->SetCurItem( NULL );
  287. m_canvas->EndMouseCapture( -1, -1, wxEmptyString, false );
  288. OnModify();
  289. }
  290. // A helper function to find any sheet pins at the specified position.
  291. static const SCH_SHEET_PIN* getSheetPin( SCH_SCREEN* aScreen, const wxPoint& aPosition )
  292. {
  293. for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
  294. {
  295. if( item->Type() == SCH_SHEET_T )
  296. {
  297. SCH_SHEET* sheet = (SCH_SHEET*) item;
  298. for( const SCH_SHEET_PIN& pin : sheet->GetPins() )
  299. {
  300. if( pin.GetPosition() == aPosition )
  301. return &pin;
  302. }
  303. }
  304. }
  305. return nullptr;
  306. }
  307. /**
  308. * Function ComputeBreakPoint
  309. * computes the middle coordinate for 2 segments from the start point to \a aPosition
  310. * with the segments kept in the horizontal or vertical axis only.
  311. *
  312. * @param aSegment A pointer to a #SCH_LINE object containing the first line break point
  313. * to compute.
  314. * @param aPosition A reference to a wxPoint object containing the coordinates of the
  315. * position used to calculate the line break point.
  316. */
  317. static void ComputeBreakPoint( SCH_SCREEN* aScreen, SCH_LINE* aSegment, wxPoint& aPosition )
  318. {
  319. wxCHECK_RET( aSegment != nullptr, wxT( "Cannot compute break point of NULL line segment." ) );
  320. SCH_LINE* nextSegment = aSegment->Next();
  321. wxPoint midPoint;
  322. int iDx = aSegment->GetEndPoint().x - aSegment->GetStartPoint().x;
  323. int iDy = aSegment->GetEndPoint().y - aSegment->GetStartPoint().y;
  324. const SCH_SHEET_PIN* connectedPin = getSheetPin( aScreen, aSegment->GetStartPoint() );
  325. auto force = connectedPin ? connectedPin->GetEdge() : SCH_SHEET_PIN::SHEET_UNDEFINED_SIDE;
  326. if( force == SCH_SHEET_PIN::SHEET_LEFT_SIDE || force == SCH_SHEET_PIN::SHEET_RIGHT_SIDE )
  327. {
  328. if( aPosition.x == connectedPin->GetPosition().x ) // push outside sheet boundary
  329. {
  330. int direction = ( force == SCH_SHEET_PIN::SHEET_LEFT_SIDE ) ? -1 : 1;
  331. aPosition.x += aScreen->GetGridSize().x * direction;
  332. }
  333. midPoint.x = aPosition.x;
  334. midPoint.y = aSegment->GetStartPoint().y; // force horizontal
  335. }
  336. else if( iDy != 0 ) // keep the first segment orientation (vertical)
  337. {
  338. midPoint.x = aSegment->GetStartPoint().x;
  339. midPoint.y = aPosition.y;
  340. }
  341. else if( iDx != 0 ) // keep the first segment orientation (horizontal)
  342. {
  343. midPoint.x = aPosition.x;
  344. midPoint.y = aSegment->GetStartPoint().y;
  345. }
  346. else
  347. {
  348. if( std::abs( aPosition.x - aSegment->GetStartPoint().x ) <
  349. std::abs( aPosition.y - aSegment->GetStartPoint().y ) )
  350. {
  351. midPoint.x = aSegment->GetStartPoint().x;
  352. midPoint.y = aPosition.y;
  353. }
  354. else
  355. {
  356. midPoint.x = aPosition.x;
  357. midPoint.y = aSegment->GetStartPoint().y;
  358. }
  359. }
  360. aSegment->SetEndPoint( midPoint );
  361. nextSegment->SetStartPoint( midPoint );
  362. nextSegment->SetEndPoint( aPosition );
  363. }
  364. void SCH_EDIT_FRAME::DeleteCurrentSegment( wxDC* DC )
  365. {
  366. SCH_SCREEN* screen = GetScreen();
  367. SetRepeatItem( NULL );
  368. if( ( screen->GetCurItem() == NULL ) || !screen->GetCurItem()->IsNew() )
  369. return;
  370. DrawSegment( m_canvas, DC, wxDefaultPosition, false );
  371. screen->Remove( screen->GetCurItem() );
  372. m_canvas->SetMouseCaptureCallback( NULL );
  373. screen->SetCurItem( NULL );
  374. }
  375. void SCH_EDIT_FRAME::SaveWireImage()
  376. {
  377. DLIST< SCH_ITEM > oldWires;
  378. oldWires.SetOwnership( false ); // Prevent DLIST for deleting items in destructor.
  379. GetScreen()->ExtractWires( oldWires, true );
  380. if( oldWires.GetCount() != 0 )
  381. {
  382. PICKED_ITEMS_LIST oldItems;
  383. oldItems.m_Status = UR_WIRE_IMAGE;
  384. while( oldWires.GetCount() != 0 )
  385. {
  386. ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
  387. oldItems.PushItem( picker );
  388. }
  389. SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );
  390. }
  391. }
  392. bool SCH_EDIT_FRAME::TrimWire( const wxPoint& aStart, const wxPoint& aEnd, bool aAppend )
  393. {
  394. SCH_LINE* line;
  395. SCH_ITEM* next_item = NULL;
  396. bool retval = false;
  397. if( aStart == aEnd )
  398. return retval;
  399. for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = next_item )
  400. {
  401. next_item = item->Next();
  402. // Don't remove wires that are already deleted, are currently being
  403. // dragged or are just created
  404. if( item->GetFlags() &
  405. ( STRUCT_DELETED | IS_DRAGGED | IS_NEW | IS_MOVED | SKIP_STRUCT ) )
  406. continue;
  407. if( item->Type() != SCH_LINE_T || item->GetLayer() != LAYER_WIRE )
  408. continue;
  409. line = (SCH_LINE*) item;
  410. if( !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aStart ) ||
  411. !IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), aEnd ) )
  412. continue;
  413. // Step 1: break the segment on one end. return_line remains line if not broken.
  414. // Ensure that *line points to the segment containing aEnd
  415. SCH_LINE* return_line = line;
  416. aAppend |= BreakSegment( line, aStart, aAppend, &return_line );
  417. if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aEnd ) )
  418. line = return_line;
  419. // Step 2: break the remaining segment. return_line remains line if not broken.
  420. // Ensure that *line _also_ contains aStart. This is our overlapping segment
  421. aAppend |= BreakSegment( line, aEnd, aAppend, &return_line );
  422. if( IsPointOnSegment( return_line->GetStartPoint(), return_line->GetEndPoint(), aStart ) )
  423. line = return_line;
  424. SaveCopyInUndoList( (SCH_ITEM*)line, UR_DELETED, aAppend );
  425. GetScreen()->Remove( (SCH_ITEM*)line );
  426. aAppend = true;
  427. retval = true;
  428. }
  429. return retval;
  430. }
  431. bool SCH_EDIT_FRAME::SchematicCleanUp( bool aAppend )
  432. {
  433. SCH_ITEM* item = NULL;
  434. SCH_ITEM* secondItem = NULL;
  435. PICKED_ITEMS_LIST itemList;
  436. SCH_SCREEN* screen = GetScreen();
  437. auto remove_item = [ &itemList ]( SCH_ITEM* aItem ) -> void
  438. {
  439. aItem->SetFlags( STRUCT_DELETED );
  440. itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
  441. };
  442. BreakSegmentsOnJunctions( true );
  443. for( item = screen->GetDrawItems(); item; item = item->Next() )
  444. {
  445. if( ( item->Type() != SCH_LINE_T )
  446. && ( item->Type() != SCH_JUNCTION_T )
  447. && ( item->Type() != SCH_NO_CONNECT_T ) )
  448. continue;
  449. if( item->GetFlags() & STRUCT_DELETED )
  450. continue;
  451. // Remove unneeded junctions
  452. if( ( item->Type() == SCH_JUNCTION_T )
  453. && ( !screen->IsJunctionNeeded( item->GetPosition() ) ) )
  454. {
  455. remove_item( item );
  456. continue;
  457. }
  458. // Remove zero-length lines
  459. if( item->Type() == SCH_LINE_T
  460. && ( (SCH_LINE*) item )->IsNull() )
  461. {
  462. remove_item( item );
  463. continue;
  464. }
  465. for( secondItem = item->Next(); secondItem; secondItem = secondItem->Next() )
  466. {
  467. if( item->Type() != secondItem->Type() || ( secondItem->GetFlags() & STRUCT_DELETED ) )
  468. continue;
  469. // Merge overlapping lines
  470. if( item->Type() == SCH_LINE_T )
  471. {
  472. SCH_LINE* firstLine = (SCH_LINE*) item;
  473. SCH_LINE* secondLine = (SCH_LINE*) secondItem;
  474. SCH_LINE* line = NULL;
  475. bool needed = false;
  476. if( !secondLine->IsParallel( firstLine ) )
  477. continue;
  478. // Remove identical lines
  479. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  480. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  481. {
  482. remove_item( secondItem );
  483. continue;
  484. }
  485. // If the end points overlap, check if we still need the junction
  486. if( secondLine->IsEndPoint( firstLine->GetStartPoint() ) )
  487. needed = screen->IsJunctionNeeded( firstLine->GetStartPoint() );
  488. else if( secondLine->IsEndPoint( firstLine->GetEndPoint() ) )
  489. needed = screen->IsJunctionNeeded( firstLine->GetEndPoint() );
  490. if( !needed && ( line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) ) )
  491. {
  492. remove_item( item );
  493. remove_item( secondItem );
  494. itemList.PushItem( ITEM_PICKER( line, UR_NEW ) );
  495. screen->Append( (SCH_ITEM*) line );
  496. break;
  497. }
  498. }
  499. // Remove duplicate junctions and no-connects
  500. else if( secondItem->GetPosition() == item->GetPosition() )
  501. remove_item( secondItem );
  502. }
  503. }
  504. for( item = screen->GetDrawItems(); item; item = secondItem )
  505. {
  506. secondItem = item->Next();
  507. if( item->GetFlags() & STRUCT_DELETED )
  508. screen->Remove( item );
  509. }
  510. SaveCopyInUndoList( itemList, UR_CHANGED, aAppend );
  511. return !!( itemList.GetCount() );
  512. }
  513. bool SCH_EDIT_FRAME::BreakSegment( SCH_LINE* aSegment, const wxPoint& aPoint, bool aAppend,
  514. SCH_LINE** aNewSegment )
  515. {
  516. if( !IsPointOnSegment( aSegment->GetStartPoint(), aSegment->GetEndPoint(), aPoint )
  517. || aSegment->IsEndPoint( aPoint ) )
  518. return false;
  519. SaveCopyInUndoList( aSegment, UR_CHANGED, aAppend );
  520. SCH_LINE* newSegment = new SCH_LINE( *aSegment );
  521. SaveCopyInUndoList( newSegment, UR_NEW, true );
  522. newSegment->SetStartPoint( aPoint );
  523. aSegment->SetEndPoint( aPoint );
  524. GetScreen()->Append( newSegment );
  525. if( aNewSegment )
  526. *aNewSegment = newSegment;
  527. return true;
  528. }
  529. bool SCH_EDIT_FRAME::BreakSegments( const wxPoint& aPoint, bool aAppend )
  530. {
  531. bool brokenSegments = false;
  532. for( SCH_ITEM* segment = GetScreen()->GetDrawItems(); segment; segment = segment->Next() )
  533. {
  534. if( ( segment->Type() != SCH_LINE_T ) || ( segment->GetLayer() == LAYER_NOTES ) )
  535. continue;
  536. brokenSegments |= BreakSegment( (SCH_LINE*) segment, aPoint, aAppend || brokenSegments );
  537. }
  538. return brokenSegments;
  539. }
  540. bool SCH_EDIT_FRAME::BreakSegmentsOnJunctions( bool aAppend )
  541. {
  542. bool brokenSegments = false;
  543. for( SCH_ITEM* item = GetScreen()->GetDrawItems(); item; item = item->Next() )
  544. {
  545. if( item->Type() == SCH_JUNCTION_T )
  546. {
  547. SCH_JUNCTION* junction = ( SCH_JUNCTION* ) item;
  548. if( BreakSegments( junction->GetPosition(), brokenSegments || aAppend ) )
  549. brokenSegments = true;
  550. }
  551. else
  552. {
  553. SCH_BUS_ENTRY_BASE* busEntry = dynamic_cast<SCH_BUS_ENTRY_BASE*>( item );
  554. if( busEntry )
  555. {
  556. if( BreakSegments( busEntry->GetPosition(), brokenSegments || aAppend )
  557. || BreakSegments( busEntry->m_End(), brokenSegments || aAppend ) )
  558. brokenSegments = true;
  559. }
  560. }
  561. }
  562. return brokenSegments;
  563. }
  564. void SCH_EDIT_FRAME::DeleteJunction( SCH_ITEM* aJunction, bool aAppend )
  565. {
  566. SCH_SCREEN* screen = GetScreen();
  567. PICKED_ITEMS_LIST itemList;
  568. auto remove_item = [ & ]( SCH_ITEM* aItem ) -> void
  569. {
  570. aItem->SetFlags( STRUCT_DELETED );
  571. itemList.PushItem( ITEM_PICKER( aItem, UR_DELETED ) );
  572. screen->Remove( aItem );
  573. };
  574. remove_item( aJunction );
  575. for( SCH_ITEM* item = screen->GetDrawItems(); item; item = item->Next() )
  576. {
  577. SCH_LINE* firstLine = dynamic_cast<SCH_LINE*>( item );
  578. if( !firstLine || !firstLine->IsEndPoint( aJunction->GetPosition() )
  579. || ( firstLine->GetFlags() & STRUCT_DELETED ) )
  580. continue;
  581. for( SCH_ITEM* secondItem = item->Next(); secondItem; secondItem = secondItem->Next() )
  582. {
  583. SCH_LINE* secondLine = dynamic_cast<SCH_LINE*>( secondItem );
  584. if( !secondLine || !secondLine->IsEndPoint( aJunction->GetPosition() )
  585. || ( secondItem->GetFlags() & STRUCT_DELETED )
  586. || !secondLine->IsParallel( firstLine ) )
  587. continue;
  588. // Remove identical lines
  589. if( firstLine->IsEndPoint( secondLine->GetStartPoint() )
  590. && firstLine->IsEndPoint( secondLine->GetEndPoint() ) )
  591. {
  592. remove_item( secondItem );
  593. continue;
  594. }
  595. // Try to merge the remaining lines
  596. if( SCH_LINE* line = (SCH_LINE*) secondLine->MergeOverlap( firstLine ) )
  597. {
  598. remove_item( item );
  599. remove_item( secondItem );
  600. itemList.PushItem( ITEM_PICKER( line, UR_NEW ) );
  601. screen->Append( (SCH_ITEM*) line );
  602. break;
  603. }
  604. }
  605. }
  606. SaveCopyInUndoList( itemList, UR_DELETED, aAppend );
  607. SCH_ITEM* nextitem;
  608. for( SCH_ITEM* item = screen->GetDrawItems(); item; item = nextitem )
  609. {
  610. nextitem = item->Next();
  611. if( item->GetFlags() & STRUCT_DELETED )
  612. screen->Remove( item );
  613. }
  614. }
  615. SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( const wxPoint& aPosition, bool aAppend )
  616. {
  617. SCH_JUNCTION* junction = new SCH_JUNCTION( aPosition );
  618. SCH_SCREEN* screen = GetScreen();
  619. bool broken_segments = false;
  620. screen->Append( junction );
  621. broken_segments = BreakSegments( aPosition, aAppend );
  622. screen->TestDanglingEnds();
  623. OnModify();
  624. SaveCopyInUndoList( junction, UR_NEW, broken_segments || aAppend );
  625. return junction;
  626. }
  627. SCH_NO_CONNECT* SCH_EDIT_FRAME::AddNoConnect( const wxPoint& aPosition )
  628. {
  629. SCH_NO_CONNECT* no_connect = new SCH_NO_CONNECT( aPosition );
  630. SetRepeatItem( no_connect );
  631. GetScreen()->Append( no_connect );
  632. SchematicCleanUp();
  633. OnModify();
  634. m_canvas->Refresh();
  635. SaveCopyInUndoList( no_connect, UR_NEW );
  636. return no_connect;
  637. }
  638. /* Abort function for wire, bus or line creation
  639. */
  640. static void AbortCreateNewLine( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
  641. {
  642. SCH_SCREEN* screen = (SCH_SCREEN*) aPanel->GetScreen();
  643. if( screen->GetCurItem() )
  644. {
  645. s_wires.DeleteAll(); // Free the list, for a future usage
  646. screen->SetCurItem( NULL );
  647. aPanel->Refresh();
  648. }
  649. else
  650. {
  651. SCH_EDIT_FRAME* parent = ( SCH_EDIT_FRAME* ) aPanel->GetParent();
  652. parent->SetRepeatItem( NULL );
  653. }
  654. // Clear flags used in edit functions.
  655. screen->ClearDrawingState();
  656. }
  657. void SCH_EDIT_FRAME::RepeatDrawItem( wxDC* DC )
  658. {
  659. SCH_ITEM* repeater = GetRepeatItem();
  660. if( !repeater )
  661. return;
  662. //D( repeater>Show( 0, std::cout ); )
  663. // clone the repeater, move it, insert into display list, then save a copy
  664. // via SetRepeatItem();
  665. SCH_ITEM* my_clone = (SCH_ITEM*) repeater->Clone();
  666. // If cloning a component then put into 'move' mode.
  667. if( my_clone->Type() == SCH_COMPONENT_T )
  668. {
  669. wxPoint pos = GetCrossHairPosition() -
  670. ( (SCH_COMPONENT*) my_clone )->GetPosition();
  671. my_clone->SetFlags( IS_NEW );
  672. ( (SCH_COMPONENT*) my_clone )->SetTimeStamp( GetNewTimeStamp() );
  673. my_clone->Move( pos );
  674. my_clone->Draw( m_canvas, DC, wxPoint( 0, 0 ), g_XorMode );
  675. PrepareMoveItem( my_clone, DC );
  676. }
  677. else
  678. {
  679. my_clone->Move( GetRepeatStep() );
  680. if( my_clone->CanIncrementLabel() )
  681. ( (SCH_TEXT*) my_clone )->IncrementLabel( GetRepeatDeltaLabel() );
  682. GetScreen()->Append( my_clone );
  683. if( my_clone->IsConnectable() )
  684. {
  685. GetScreen()->TestDanglingEnds();
  686. m_canvas->Refresh();
  687. }
  688. else
  689. {
  690. my_clone->Draw( m_canvas, DC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
  691. }
  692. SaveCopyInUndoList( my_clone, UR_NEW );
  693. my_clone->ClearFlags();
  694. }
  695. // clone my_clone, now that it has been moved, thus saving new position.
  696. SetRepeatItem( my_clone );
  697. }