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.

525 lines
16 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 <schframe.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_LINE* segment, const 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( !last_lines.empty() )
  68. {
  69. SCH_LINE* last_line = last_lines[last_lines.size() - 1];
  70. bool contiguous = ( last_line->GetEndPoint() == line->GetStartPoint() );
  71. bool backtracks = IsPointOnSegment( last_line->GetStartPoint(),
  72. last_line->GetEndPoint(), line->GetEndPoint() );
  73. bool total_backtrack = ( last_line->GetStartPoint() == line->GetEndPoint() );
  74. if( contiguous && backtracks )
  75. {
  76. if( total_backtrack )
  77. {
  78. delete s_wires.Remove( last_line );
  79. delete s_wires.Remove( line );
  80. last_lines.pop_back();
  81. }
  82. else
  83. {
  84. last_line->SetEndPoint( line->GetEndPoint() );
  85. delete s_wires.Remove( line );
  86. }
  87. }
  88. else
  89. {
  90. last_lines.push_back( line );
  91. }
  92. }
  93. else
  94. {
  95. last_lines.push_back( line );
  96. }
  97. }
  98. }
  99. /**
  100. * Mouse capture callback for drawing line segments.
  101. */
  102. static void DrawSegment( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition,
  103. bool aErase )
  104. {
  105. SCH_LINE* segment;
  106. if( s_wires.GetCount() == 0 )
  107. return;
  108. segment = (SCH_LINE*) s_wires.begin();
  109. EDA_COLOR_T color = GetLayerColor( segment->GetLayer() );
  110. ColorChangeHighlightFlag( &color, !(color & HIGHLIGHT_FLAG) );
  111. if( aErase )
  112. {
  113. while( segment )
  114. {
  115. if( !segment->IsNull() ) // Redraw if segment length != 0
  116. segment->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, color );
  117. segment = segment->Next();
  118. }
  119. }
  120. SCH_EDIT_FRAME* frame = (SCH_EDIT_FRAME*) aPanel->GetParent();
  121. wxPoint endpos = frame->GetCrossHairPosition();
  122. if( frame->GetForceHVLines() ) /* Coerce the line to vertical or horizontal one: */
  123. ComputeBreakPoint( (SCH_LINE*) s_wires.GetLast()->Back(), endpos );
  124. else
  125. ( (SCH_LINE*) s_wires.GetLast() )->SetEndPoint( endpos );
  126. segment = (SCH_LINE*) s_wires.begin();
  127. while( segment )
  128. {
  129. if( !segment->IsNull() ) // Redraw if segment length != 0
  130. segment->Draw( aPanel, aDC, wxPoint( 0, 0 ), g_XorMode, color );
  131. segment = segment->Next();
  132. }
  133. }
  134. void SCH_EDIT_FRAME::BeginSegment( wxDC* DC, int type )
  135. {
  136. SCH_LINE* segment;
  137. SCH_LINE* nextSegment;
  138. wxPoint cursorpos = GetCrossHairPosition();
  139. // We should know if a segment is currently in progress
  140. segment = (SCH_LINE*) GetScreen()->GetCurItem();
  141. if( segment ) // a current item exists, but not necessary a currently edited item
  142. {
  143. if( !segment->GetFlags() || ( segment->Type() != SCH_LINE_T ) )
  144. {
  145. if( segment->GetFlags() )
  146. {
  147. wxLogDebug( wxT( "BeginSegment: item->GetFlags()== %X" ),
  148. segment->GetFlags() );
  149. }
  150. // no wire, bus or graphic line in progress
  151. segment = NULL;
  152. }
  153. }
  154. if( !segment ) // first point : Create the first wire or bus segment
  155. {
  156. switch( type )
  157. {
  158. default:
  159. segment = new SCH_LINE( cursorpos, LAYER_NOTES );
  160. break;
  161. case LAYER_WIRE:
  162. segment = new SCH_LINE( cursorpos, LAYER_WIRE );
  163. /* A junction will be created later, when we'll know the
  164. * segment end position, and if the junction is really needed */
  165. break;
  166. case LAYER_BUS:
  167. segment = new SCH_LINE( cursorpos, LAYER_BUS );
  168. break;
  169. }
  170. segment->SetFlags( IS_NEW );
  171. s_wires.PushBack( segment );
  172. GetScreen()->SetCurItem( segment );
  173. // We need 2 segments to go from a given start pin to an end point when the horizontal
  174. // and vertical lines only switch is on.
  175. if( GetForceHVLines() )
  176. {
  177. nextSegment = new SCH_LINE( *segment );
  178. nextSegment->SetFlags( IS_NEW );
  179. s_wires.PushBack( nextSegment );
  180. GetScreen()->SetCurItem( nextSegment );
  181. }
  182. m_canvas->SetMouseCapture( DrawSegment, AbortCreateNewLine );
  183. SetRepeatItem( NULL );
  184. }
  185. else // A segment is in progress: terminates the current segment and add a new segment.
  186. {
  187. SCH_LINE* prevSegment = segment->Back();
  188. // Be aware prevSegment can be null when the horizontal and vertical lines only switch is off
  189. // when we create the first segment.
  190. if( !GetForceHVLines() )
  191. {
  192. // If only one segment is needed and it has a zero length, do not create a new one.
  193. if( segment->IsNull() )
  194. return;
  195. }
  196. else
  197. {
  198. wxCHECK_RET( prevSegment != NULL, wxT( "Failed to create second line segment." ) );
  199. // If two segments are required and they both have zero length, do not
  200. // create a new one.
  201. if( prevSegment && prevSegment->IsNull() && segment->IsNull() )
  202. return;
  203. }
  204. m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
  205. // Terminate the command if the end point is on a pin, junction, or another wire or bus.
  206. if( GetScreen()->IsTerminalPoint( cursorpos, segment->GetLayer() ) )
  207. {
  208. EndSegment( DC );
  209. return;
  210. }
  211. // Create a new segment, and chain it after the current new segment.
  212. nextSegment = new SCH_LINE( *segment );
  213. nextSegment->SetStartPoint( cursorpos );
  214. s_wires.PushBack( nextSegment );
  215. segment->SetEndPoint( cursorpos );
  216. segment->ClearFlags( IS_NEW );
  217. segment->SetFlags( SELECTED );
  218. nextSegment->SetFlags( IS_NEW );
  219. GetScreen()->SetCurItem( nextSegment );
  220. m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
  221. }
  222. }
  223. void SCH_EDIT_FRAME::EndSegment( wxDC* DC )
  224. {
  225. SCH_SCREEN* screen = GetScreen();
  226. SCH_LINE* segment = (SCH_LINE*) screen->GetCurItem();
  227. if( segment == NULL || segment->Type() != SCH_LINE_T || !segment->IsNew() )
  228. return;
  229. // Delete zero length segments and clear item flags.
  230. SCH_ITEM* item = s_wires.begin();
  231. while( item )
  232. {
  233. item->ClearFlags();
  234. wxCHECK_RET( item->Type() == SCH_LINE_T, wxT( "Unexpected object type in wire list." ) );
  235. segment = (SCH_LINE*) item;
  236. item = item->Next();
  237. if( segment->IsNull() )
  238. delete s_wires.Remove( segment );
  239. }
  240. if( s_wires.GetCount() == 0 )
  241. return;
  242. // Get the last non-null wire (this is the last created segment).
  243. SetRepeatItem( segment = (SCH_LINE*) s_wires.GetLast() );
  244. screen->SetCurItem( NULL );
  245. m_canvas->EndMouseCapture( -1, -1, wxEmptyString, false );
  246. // store the terminal point of this last segment: a junction could be needed
  247. // (the last wire could be merged/deleted/modified, and lost)
  248. wxPoint endpoint = segment->GetEndPoint();
  249. // store the starting point of this first segment: a junction could be needed
  250. SCH_LINE* firstsegment = (SCH_LINE*) s_wires.GetFirst();
  251. wxPoint startPoint = firstsegment->GetStartPoint();
  252. // Save the old wires for the undo command
  253. DLIST< SCH_ITEM > oldWires; // stores here the old wires
  254. GetScreen()->ExtractWires( oldWires, true ); // Save them in oldWires list
  255. // Put the snap shot of the previous wire, buses, and junctions in the undo/redo list.
  256. PICKED_ITEMS_LIST oldItems;
  257. oldItems.m_Status = UR_WIRE_IMAGE;
  258. while( oldWires.GetCount() != 0 )
  259. {
  260. ITEM_PICKER picker = ITEM_PICKER( oldWires.PopFront(), UR_WIRE_IMAGE );
  261. oldItems.PushItem( picker );
  262. }
  263. SaveCopyInUndoList( oldItems, UR_WIRE_IMAGE );
  264. // Remove segments backtracking over others
  265. RemoveBacktracks( s_wires );
  266. // Add the new wires
  267. screen->Append( s_wires );
  268. // Correct and remove segments that need to be merged.
  269. screen->SchematicCleanUp();
  270. // A junction could be needed to connect the end point of the last created segment.
  271. if( screen->IsJunctionNeeded( endpoint ) )
  272. screen->Append( AddJunction( DC, endpoint ) );
  273. // A junction could be needed to connect the start point of the set of new created wires
  274. if( screen->IsJunctionNeeded( startPoint ) )
  275. screen->Append( AddJunction( DC, startPoint ) );
  276. m_canvas->Refresh();
  277. OnModify();
  278. }
  279. /**
  280. * Function ComputeBreakPoint
  281. * computes the middle coordinate for 2 segments from the start point to \a aPosition
  282. * with the segments kept in the horizontal or vertical axis only.
  283. *
  284. * @param aSegment A pointer to a #SCH_LINE object containing the first line break point
  285. * to compute.
  286. * @param aPosition A reference to a wxPoint object containing the coordinates of the
  287. * position used to calculate the line break point.
  288. */
  289. static void ComputeBreakPoint( SCH_LINE* aSegment, const wxPoint& aPosition )
  290. {
  291. wxCHECK_RET( aSegment != NULL, wxT( "Cannot compute break point of NULL line segment." ) );
  292. SCH_LINE* nextSegment = aSegment->Next();
  293. wxPoint midPoint = aPosition;
  294. wxCHECK_RET( nextSegment != NULL,
  295. wxT( "Cannot compute break point of NULL second line segment." ) );
  296. #if 0
  297. if( ABS( midPoint.x - aSegment->GetStartPoint().x ) <
  298. ABS( midPoint.y - aSegment->GetStartPoint().y ) )
  299. midPoint.x = aSegment->GetStartPoint().x;
  300. else
  301. midPoint.y = aSegment->GetStartPoint().y;
  302. #else
  303. int iDx = aSegment->GetEndPoint().x - aSegment->GetStartPoint().x;
  304. int iDy = aSegment->GetEndPoint().y - aSegment->GetStartPoint().y;
  305. if( iDy != 0 ) // keep the first segment orientation (currently horizontal)
  306. {
  307. midPoint.x = aSegment->GetStartPoint().x;
  308. }
  309. else if( iDx != 0 ) // keep the first segment orientation (currently vertical)
  310. {
  311. midPoint.y = aSegment->GetStartPoint().y;
  312. }
  313. else
  314. {
  315. if( std::abs( midPoint.x - aSegment->GetStartPoint().x ) <
  316. std::abs( midPoint.y - aSegment->GetStartPoint().y ) )
  317. midPoint.x = aSegment->GetStartPoint().x;
  318. else
  319. midPoint.y = aSegment->GetStartPoint().y;
  320. }
  321. #endif
  322. aSegment->SetEndPoint( midPoint );
  323. nextSegment->SetStartPoint( midPoint );
  324. nextSegment->SetEndPoint( aPosition );
  325. }
  326. void SCH_EDIT_FRAME::DeleteCurrentSegment( wxDC* DC )
  327. {
  328. SCH_SCREEN* screen = GetScreen();
  329. SetRepeatItem( NULL );
  330. if( ( screen->GetCurItem() == NULL ) || !screen->GetCurItem()->IsNew() )
  331. return;
  332. DrawSegment( m_canvas, DC, wxDefaultPosition, false );
  333. screen->Remove( screen->GetCurItem() );
  334. m_canvas->SetMouseCaptureCallback( NULL );
  335. screen->SetCurItem( NULL );
  336. }
  337. SCH_JUNCTION* SCH_EDIT_FRAME::AddJunction( wxDC* aDC, const wxPoint& aPosition,
  338. bool aPutInUndoList )
  339. {
  340. SCH_JUNCTION* junction = new SCH_JUNCTION( aPosition );
  341. SetRepeatItem( junction );
  342. m_canvas->CrossHairOff( aDC ); // Erase schematic cursor
  343. junction->Draw( m_canvas, aDC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
  344. m_canvas->CrossHairOn( aDC ); // Display schematic cursor
  345. if( aPutInUndoList )
  346. {
  347. GetScreen()->Append( junction );
  348. SaveCopyInUndoList( junction, UR_NEW );
  349. OnModify();
  350. }
  351. return junction;
  352. }
  353. SCH_NO_CONNECT* SCH_EDIT_FRAME::AddNoConnect( wxDC* aDC, const wxPoint& aPosition )
  354. {
  355. SCH_NO_CONNECT* no_connect = new SCH_NO_CONNECT( aPosition );
  356. SetRepeatItem( no_connect );
  357. GetScreen()->Append( no_connect );
  358. GetScreen()->SchematicCleanUp();
  359. OnModify();
  360. m_canvas->Refresh();
  361. SaveCopyInUndoList( no_connect, UR_NEW );
  362. return no_connect;
  363. }
  364. /* Abort function for wire, bus or line creation
  365. */
  366. static void AbortCreateNewLine( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
  367. {
  368. SCH_SCREEN* screen = (SCH_SCREEN*) aPanel->GetScreen();
  369. if( screen->GetCurItem() )
  370. {
  371. s_wires.DeleteAll(); // Free the list, for a future usage
  372. screen->SetCurItem( NULL );
  373. aPanel->Refresh();
  374. }
  375. else
  376. {
  377. SCH_EDIT_FRAME* parent = ( SCH_EDIT_FRAME* ) aPanel->GetParent();
  378. parent->SetRepeatItem( NULL );
  379. }
  380. // Clear flags used in edit functions.
  381. screen->ClearDrawingState();
  382. }
  383. void SCH_EDIT_FRAME::RepeatDrawItem( wxDC* DC )
  384. {
  385. SCH_ITEM* repeater = GetRepeatItem();
  386. if( !repeater )
  387. return;
  388. //D( repeater>Show( 0, std::cout ); )
  389. // clone the repeater, move it, insert into display list, then save a copy
  390. // via SetRepeatItem();
  391. SCH_ITEM* my_clone = (SCH_ITEM*) repeater->Clone();
  392. // If cloning a component then put into 'move' mode.
  393. if( my_clone->Type() == SCH_COMPONENT_T )
  394. {
  395. wxPoint pos = GetCrossHairPosition() -
  396. ( (SCH_COMPONENT*) my_clone )->GetPosition();
  397. my_clone->SetFlags( IS_NEW );
  398. ( (SCH_COMPONENT*) my_clone )->SetTimeStamp( GetNewTimeStamp() );
  399. my_clone->Move( pos );
  400. my_clone->Draw( m_canvas, DC, wxPoint( 0, 0 ), g_XorMode );
  401. PrepareMoveItem( my_clone, DC );
  402. }
  403. else
  404. {
  405. my_clone->Move( GetRepeatStep() );
  406. if( my_clone->CanIncrementLabel() )
  407. ( (SCH_TEXT*) my_clone )->IncrementLabel( GetRepeatDeltaLabel() );
  408. GetScreen()->Append( my_clone );
  409. if( my_clone->IsConnectable() )
  410. {
  411. GetScreen()->TestDanglingEnds();
  412. m_canvas->Refresh();
  413. }
  414. else
  415. {
  416. my_clone->Draw( m_canvas, DC, wxPoint( 0, 0 ), GR_DEFAULT_DRAWMODE );
  417. }
  418. SaveCopyInUndoList( my_clone, UR_NEW );
  419. my_clone->ClearFlags();
  420. }
  421. // clone my_clone, now that it has been moved, thus saving new position.
  422. SetRepeatItem( my_clone );
  423. }