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.

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