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.

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