KiCad PCB EDA Suite
sch_line_wire_bus_tool.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2019 CERN
5  * Copyright (C) 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 #include <connection_graph.h>
26 #include <sch_line_wire_bus_tool.h>
27 #include <ee_selection_tool.h>
28 #include <ee_actions.h>
29 #include <sch_edit_frame.h>
30 #include <sch_view.h>
31 #include <class_draw_panel_gal.h>
32 #include <id.h>
33 #include <eeschema_id.h>
34 #include <confirm.h>
35 #include <view/view_group.h>
36 #include <view/view_controls.h>
37 #include <view/view.h>
38 #include <tool/tool_manager.h>
39 #include <sch_junction.h>
40 #include <sch_line.h>
41 #include <sch_bus_entry.h>
42 #include <sch_text.h>
43 #include <sch_sheet.h>
44 #include <advanced_config.h>
45 #include "ee_point_editor.h"
46 
48 {
49 public:
51  ACTION_MENU( true ),
52  m_showTitle( false )
53  {
54  SetIcon( add_line2bus_xpm );
55  SetTitle( _( "Unfold from Bus" ) );
56  }
57 
58  void SetShowTitle()
59  {
60  m_showTitle = true;
61  }
62 
63 
64 protected:
65  ACTION_MENU* create() const override
66  {
67  return new BUS_UNFOLD_MENU();
68  }
69 
70 private:
71  void update() override
72  {
75  KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT };
76  EE_SELECTION& selection = selTool->RequestSelection( busType );
77  SCH_LINE* bus = (SCH_LINE*) selection.Front();
78 
79  Clear();
80 
81  // TODO(JE) remove once real-time is enabled
82  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
83  {
85 
86  // Pick up the pointer again because it may have been changed by SchematicCleanUp
87  selection = selTool->RequestSelection( busType );
88  bus = (SCH_LINE*) selection.Front();
89  }
90  if( !bus )
91  {
92  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "no bus selected" ), wxEmptyString );
93  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
94  return;
95  }
96 
97  SCH_CONNECTION* connection = bus->Connection( *g_CurrentSheet );
98 
99  if( !connection || !connection->IsBus() || connection->Members().empty() )
100  {
101  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "bus has no connections" ), wxEmptyString );
102  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
103  return;
104  }
105 
106  int idx = 0;
107 
108  if( m_showTitle )
109  {
110  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "Unfold from Bus" ), wxEmptyString );
111  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
112  }
113 
114  for( const auto& member : connection->Members() )
115  {
116  int id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
117  wxString name = member->Name( true );
118 
119  if( member->Type() == CONNECTION_BUS )
120  {
121  ACTION_MENU* submenu = new ACTION_MENU( true );
122  AppendSubMenu( submenu, name );
123 
124  for( const auto& sub_member : member->Members() )
125  {
126  id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
127  submenu->Append( id, sub_member->Name( true ), wxEmptyString );
128  }
129  }
130  else
131  {
132  Append( id, name, wxEmptyString );
133  }
134  }
135  }
136 
138 };
139 
140 
142  EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveDrawingLineWireBus" )
143 {
144  m_busUnfold = {};
145 }
146 
147 
149 {
150 }
151 
152 
154 {
156 
157  auto wireOrBusTool = [ this ] ( const SELECTION& aSel ) {
160  };
161 
162  auto lineTool = [ this ] ( const SELECTION& aSel ) {
164  };
165 
166  auto belowRootSheetCondition = [] ( const SELECTION& aSel ) {
167  return g_CurrentSheet->Last() != g_RootSheet;
168  };
169 
170  auto busSelection = EE_CONDITIONS::MoreThan( 0 )
172 
173  auto& ctxMenu = m_menu.GetMenu();
174 
175  // Build the tool menu
176  //
177  ctxMenu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 2 );
178 
179  ctxMenu.AddSeparator( 10 );
180  ctxMenu.AddItem( EE_ACTIONS::drawWire, wireOrBusTool && EE_CONDITIONS::Idle, 10 );
181  ctxMenu.AddItem( EE_ACTIONS::drawBus, wireOrBusTool && EE_CONDITIONS::Idle, 10 );
182  ctxMenu.AddItem( EE_ACTIONS::drawLines, lineTool && EE_CONDITIONS::Idle, 10 );
183  ctxMenu.AddItem( EE_ACTIONS::finishWire, IsDrawingWire, 10 );
184  ctxMenu.AddItem( EE_ACTIONS::finishBus, IsDrawingBus, 10 );
185  ctxMenu.AddItem( EE_ACTIONS::finishLine, IsDrawingLine, 10 );
186 
187  std::shared_ptr<BUS_UNFOLD_MENU> busUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>();
188  busUnfoldMenu->SetTool( this );
189  m_menu.AddSubMenu( busUnfoldMenu );
190  ctxMenu.AddMenu( busUnfoldMenu.get(), EE_CONDITIONS::Idle, 10 );
191 
192  ctxMenu.AddSeparator( 100 );
193  ctxMenu.AddItem( EE_ACTIONS::placeJunction, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
194  ctxMenu.AddItem( EE_ACTIONS::placeLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
195  ctxMenu.AddItem( EE_ACTIONS::placeGlobalLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
196  ctxMenu.AddItem( EE_ACTIONS::placeHierLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
197  ctxMenu.AddItem( EE_ACTIONS::breakWire, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
198  ctxMenu.AddItem( EE_ACTIONS::breakBus, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
199 
200  ctxMenu.AddSeparator( 200 );
201  ctxMenu.AddItem( EE_ACTIONS::selectNode, wireOrBusTool && EE_CONDITIONS::Idle, 200 );
202  ctxMenu.AddItem( EE_ACTIONS::selectConnection, wireOrBusTool && EE_CONDITIONS::Idle, 200 );
203 
204  // Add bus unfolding to the selection tool
205  //
207 
208  std::shared_ptr<BUS_UNFOLD_MENU> selBusUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>();
209  selBusUnfoldMenu->SetTool( m_selectionTool );
210  m_selectionTool->GetToolMenu().AddSubMenu( selBusUnfoldMenu );
211  selToolMenu.AddMenu( selBusUnfoldMenu.get(), busSelection && EE_CONDITIONS::Idle, 100 );
212 
213  return true;
214 }
215 
216 
217 static bool isNewSegment( SCH_ITEM* aItem )
218 {
219  return aItem && aItem->IsNew() && aItem->Type() == SCH_LINE_T;
220 }
221 
222 
224 {
225  static KICAD_T graphicLineType[] = { SCH_LINE_LOCATE_GRAPHIC_LINE_T, EOT };
226  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( graphicLineType );
227 }
228 
229 
231 {
232  static KICAD_T wireType[] = { SCH_LINE_LOCATE_WIRE_T, EOT };
233  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( wireType );
234 }
235 
236 
238 {
239  static KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT };
240  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( busType );
241 }
242 
243 
245 {
246  // NOTE: for immediate hotkeys, it is NOT required that the line, wire or bus tool
247  // be selected
248  SCH_ITEM* item = (SCH_ITEM*) aSelection.Front();
249  return isNewSegment( item );
250 }
251 
252 
254 {
255  SCH_LAYER_ID layer = aEvent.Parameter<SCH_LAYER_ID>();
256  SCH_LINE* segment = nullptr;
257 
258  if( aEvent.HasPosition() )
259  getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
260 
261  std::string tool = aEvent.GetCommandStr().get();
262  m_frame->PushTool( tool );
263 
264  if( aEvent.HasPosition() )
265  {
266  VECTOR2D cursorPos = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
267  segment = startSegments( layer, cursorPos );
268  }
269 
270  return doDrawSegments( tool, layer, segment );
271 }
272 
273 
275 {
276  wxString* netPtr = aEvent.Parameter<wxString*>();
277  wxString net;
278  SCH_LINE* segment = nullptr;
279 
280  std::string tool = aEvent.GetCommandStr().get();
281  m_frame->PushTool( tool );
282  Activate();
283 
284  if( netPtr )
285  {
286  net = *netPtr;
287  delete netPtr;
288  }
289  else
290  {
291  BUS_UNFOLD_MENU unfoldMenu;
292  unfoldMenu.SetTool( this );
293  unfoldMenu.SetShowTitle();
294 
295  SetContextMenu( &unfoldMenu, CMENU_NOW );
296 
297  while( TOOL_EVENT* evt = Wait() )
298  {
299  if( evt->Action() == TA_CHOICE_MENU_CHOICE )
300  {
301  OPT<int> id = evt->GetCommandId();
302 
303  if( id && ( *id > 0 ) )
304  net = *evt->Parameter<wxString*>();
305 
306  break;
307  }
308  }
309  }
310 
311  // Break a wire for the given net out of the bus
312  if( !net.IsEmpty() )
313  segment = doUnfoldBus( net );
314 
315  // If we have an unfolded wire to draw, then draw it
316  if( segment )
317  return doDrawSegments( tool, LAYER_WIRE, segment );
318  else
319  {
320  m_frame->PopTool( tool );
321  return 0;
322  }
323 }
324 
325 
327 {
329 
330  wxPoint pos = (wxPoint) getViewControls()->GetCursorPosition();
331 
332  m_busUnfold.entry = new SCH_BUS_WIRE_ENTRY( pos, '\\' );
335 
336  m_busUnfold.label = new SCH_LABEL( m_busUnfold.entry->m_End(), aNet );
340 
341  m_busUnfold.in_progress = true;
342  m_busUnfold.origin = pos;
343  m_busUnfold.net_name = aNet;
344 
346 
348 }
349 
350 
351 // Storage for the line segments while drawing
353 
354 
358 static const SCH_SHEET_PIN* getSheetPin( SCH_SCREEN* aScreen, const wxPoint& aPosition )
359 {
360  for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
361  {
362  if( item->Type() == SCH_SHEET_T )
363  {
364  SCH_SHEET* sheet = (SCH_SHEET*) item;
365 
366  for( const SCH_SHEET_PIN& pin : sheet->GetPins() )
367  {
368  if( pin.GetPosition() == aPosition )
369  return &pin;
370  }
371  }
372  }
373 
374  return nullptr;
375 }
376 
377 
388 static void computeBreakPoint( SCH_SCREEN* aScreen, SCH_LINE* aSegment, wxPoint& aPosition )
389 {
390  wxCHECK_RET( aSegment != nullptr, wxT( "Cannot compute break point of NULL line segment." ) );
391 
392  SCH_LINE* nextSegment = aSegment->Next();
393 
394  wxPoint midPoint;
395  int iDx = aSegment->GetEndPoint().x - aSegment->GetStartPoint().x;
396  int iDy = aSegment->GetEndPoint().y - aSegment->GetStartPoint().y;
397 
398  const SCH_SHEET_PIN* connectedPin = getSheetPin( aScreen, aSegment->GetStartPoint() );
399  auto force = connectedPin ? connectedPin->GetEdge() : SHEET_UNDEFINED_SIDE;
400 
401  if( force == SHEET_LEFT_SIDE || force == SHEET_RIGHT_SIDE )
402  {
403  if( aPosition.x == connectedPin->GetPosition().x ) // push outside sheet boundary
404  {
405  int direction = ( force == SHEET_LEFT_SIDE ) ? -1 : 1;
406  aPosition.x += int( aScreen->GetGridSize().x * direction );
407  }
408 
409  midPoint.x = aPosition.x;
410  midPoint.y = aSegment->GetStartPoint().y; // force horizontal
411  }
412  else if( iDy != 0 ) // keep the first segment orientation (vertical)
413  {
414  midPoint.x = aSegment->GetStartPoint().x;
415  midPoint.y = aPosition.y;
416  }
417  else if( iDx != 0 ) // keep the first segment orientation (horizontal)
418  {
419  midPoint.x = aPosition.x;
420  midPoint.y = aSegment->GetStartPoint().y;
421  }
422  else
423  {
424  if( std::abs( aPosition.x - aSegment->GetStartPoint().x ) <
425  std::abs( aPosition.y - aSegment->GetStartPoint().y ) )
426  {
427  midPoint.x = aSegment->GetStartPoint().x;
428  midPoint.y = aPosition.y;
429  }
430  else
431  {
432  midPoint.x = aPosition.x;
433  midPoint.y = aSegment->GetStartPoint().y;
434  }
435  }
436 
437  aSegment->SetEndPoint( midPoint );
438  nextSegment->SetStartPoint( midPoint );
439  nextSegment->SetEndPoint( aPosition );
440 }
441 
442 
443 int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const std::string& aTool, int aType, SCH_LINE* aSegment )
444 {
445  SCH_SCREEN* screen = m_frame->GetScreen();
447 
449  getViewControls()->ShowCursor( true );
450 
451  Activate();
452 
453  // Add the new label to the selection so the rotate command operates on it
454  if( m_busUnfold.label )
456 
457  // Main loop: keep receiving events
458  while( TOOL_EVENT* evt = Wait() )
459  {
460  if( !pointEditor->HasPoint() ) // Set wxCursor shape when starting the tool
461  m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
462 
463  wxPoint cursorPos = (wxPoint) getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
464  bool forceHV = m_frame->GetForceHVLines();
465 
466  //------------------------------------------------------------------------
467  // Handle cancel:
468  //
469  auto cleanup = [&] () {
471 
472  aSegment = nullptr;
473  s_wires.DeleteAll();
474 
475  if( m_busUnfold.entry )
477 
480 
483 
484  delete m_busUnfold.entry;
485  delete m_busUnfold.label;
486  m_busUnfold = {};
487 
488  m_view->ClearPreview();
489  m_view->ShowPreview( false );
490  };
491 
492  if( evt->IsCancelInteractive() )
493  {
494  if( aSegment || m_busUnfold.in_progress )
495  cleanup();
496  else
497  {
498  m_frame->PopTool( aTool );
499  break;
500  }
501  }
502  else if( evt->IsActivate() )
503  {
504  if( aSegment || m_busUnfold.in_progress )
505  cleanup();
506 
507  if( evt->IsMoveTool() )
508  {
509  // leave ourselves on the stack so we come back after the move
510  break;
511  }
512  else
513  {
514  m_frame->PopTool( aTool );
515  break;
516  }
517  }
518  //------------------------------------------------------------------------
519  // Handle finish:
520  //
521  else if( evt->IsAction( &EE_ACTIONS::finishLineWireOrBus )
522  || evt->IsAction( &EE_ACTIONS::finishWire )
523  || evt->IsAction( &EE_ACTIONS::finishBus )
524  || evt->IsAction( &EE_ACTIONS::finishLine ) )
525  {
526  if( aSegment || m_busUnfold.in_progress )
527  {
528  finishSegments();
529  aSegment = nullptr;
530  }
531  }
532  //------------------------------------------------------------------------
533  // Handle click:
534  //
535  else if( evt->IsClick( BUT_LEFT ) || ( aSegment && evt->IsDblClick( BUT_LEFT ) ) )
536  {
537  // First click when unfolding places the label and wire-to-bus entry
539  {
540  wxASSERT( aType == LAYER_WIRE );
541 
544  m_busUnfold.label_placed = true;
545  }
546 
547  if( !aSegment )
548  {
549  aSegment = startSegments( aType, cursorPos );
550  }
551  // Create a new segment if we're out of previously-created ones
552  else if( !aSegment->IsNull() || ( forceHV && !aSegment->Back()->IsNull() ) )
553  {
554  // Terminate the command if the end point is on a pin, junction, or another
555  // wire or bus.
557  && screen->IsTerminalPoint( cursorPos, aSegment->GetLayer() ) )
558  {
559  finishSegments();
560  aSegment = nullptr;
561  }
562  else
563  {
564  aSegment->SetEndPoint( cursorPos );
565 
566  // Create a new segment, and chain it after the current segment.
567  aSegment = new SCH_LINE( *aSegment );
568  aSegment->SetFlags( IS_NEW | IS_MOVED );
569  aSegment->SetStartPoint( cursorPos );
570  s_wires.PushBack( aSegment );
571 
572  m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ );
573  }
574  }
575 
576  if( evt->IsDblClick( BUT_LEFT ) && aSegment )
577  {
578  if( forceHV )
579  computeBreakPoint( screen, aSegment->Back(), cursorPos );
580 
581  finishSegments();
582  aSegment = nullptr;
583  }
584  }
585  //------------------------------------------------------------------------
586  // Handle motion:
587  //
588  else if( evt->IsMotion() )
589  {
590  m_view->ClearPreview();
591 
592  // Update the bus unfold posture based on the mouse movement
594  {
595  wxPoint cursor_delta = cursorPos - m_busUnfold.origin;
597 
598  bool offset = ( cursor_delta.x < 0 );
599  char shape = ( offset ? ( ( cursor_delta.y >= 0 ) ? '/' : '\\' )
600  : ( ( cursor_delta.y >= 0 ) ? '\\' : '/' ) );
601 
602  // Erase and redraw if necessary
603  if( shape != entry->GetBusEntryShape() || offset != m_busUnfold.offset )
604  {
605  entry->SetBusEntryShape( shape );
606  wxPoint entry_pos = m_busUnfold.origin;
607 
608  if( offset )
609  entry_pos -= entry->GetSize();
610 
611  entry->SetPosition( entry_pos );
612  m_busUnfold.offset = offset;
613 
614  m_frame->RefreshItem( entry );
615 
616  wxPoint wire_start = offset ? entry->GetPosition() : entry->m_End();
617  s_wires.begin()->SetStartPoint( wire_start );
618  }
619 
620  // Update the label "ghost" position
621  m_busUnfold.label->SetPosition( cursorPos );
623  }
624 
625  if( aSegment )
626  {
627  // Coerce the line to vertical or horizontal if necessary
628  if( forceHV )
629  computeBreakPoint( screen, aSegment->Back(), cursorPos );
630  else
631  aSegment->SetEndPoint( cursorPos );
632  }
633 
634  for( auto seg = s_wires.begin(); seg; seg = seg->Next() )
635  {
636  if( !seg->IsNull() ) // Add to preview if segment length != 0
637  m_view->AddToPreview( seg->Clone() );
638  }
639  }
640  //------------------------------------------------------------------------
641  // Handle context menu:
642  //
643  else if( evt->IsClick( BUT_RIGHT ) )
644  {
645  // Warp after context menu only if dragging...
646  if( !aSegment )
648 
650  }
651  else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
652  {
653  if( evt->GetCommandId().get() >= ID_POPUP_SCH_UNFOLD_BUS
654  && evt->GetCommandId().get() <= ID_POPUP_SCH_UNFOLD_BUS_END )
655  {
656  wxASSERT_MSG( !aSegment, "Bus unfold event received when already drawing!" );
657 
658  aType = LAYER_WIRE;
659  wxString net = *evt->Parameter<wxString*>();
660  aSegment = doUnfoldBus( net );
661  }
662  }
663  else
664  evt->SetPassEvent();
665 
666  // Enable autopanning and cursor capture only when there is a segment to be placed
667  getViewControls()->SetAutoPan( aSegment != nullptr );
668  getViewControls()->CaptureCursor( aSegment != nullptr );
669  }
670 
671  return 0;
672 }
673 
674 
676 {
677  SCH_LINE* segment = nullptr;
678  bool forceHV = m_frame->GetForceHVLines();
679 
680  switch( aType )
681  {
682  default: segment = new SCH_LINE( (wxPoint) aPos, LAYER_NOTES ); break;
683  case LAYER_WIRE: segment = new SCH_LINE( (wxPoint) aPos, LAYER_WIRE ); break;
684  case LAYER_BUS: segment = new SCH_LINE( (wxPoint) aPos, LAYER_BUS ); break;
685  }
686 
687  segment->SetFlags( IS_NEW | IS_MOVED );
688  s_wires.PushBack( segment );
689 
690  m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
691 
692  // We need 2 segments to go from a given start pin to an end point when the
693  // horizontal and vertical lines only switch is on.
694  if( forceHV )
695  {
696  segment = new SCH_LINE( *segment );
697  segment->SetFlags( IS_NEW | IS_MOVED );
698  s_wires.PushBack( segment );
699 
700  m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
701  }
702 
703  return segment;
704 }
705 
706 
720 static void removeBacktracks( DLIST<SCH_LINE>& aWires )
721 {
722  SCH_LINE* next = nullptr;
723  std::vector<SCH_LINE*> last_lines;
724 
725  for( SCH_LINE* line = aWires.GetFirst(); line; line = next )
726  {
727  next = line->Next();
728 
729  if( line->IsNull() )
730  {
731  delete s_wires.Remove( line );
732  continue;
733  }
734 
735  if( !last_lines.empty() )
736  {
737  SCH_LINE* last_line = last_lines[last_lines.size() - 1];
738  bool contiguous = ( last_line->GetEndPoint() == line->GetStartPoint() );
739  bool backtracks = IsPointOnSegment( last_line->GetStartPoint(),
740  last_line->GetEndPoint(), line->GetEndPoint() );
741  bool total_backtrack = ( last_line->GetStartPoint() == line->GetEndPoint() );
742 
743  if( contiguous && backtracks )
744  {
745  if( total_backtrack )
746  {
747  delete s_wires.Remove( last_line );
748  delete s_wires.Remove( line );
749  last_lines.pop_back();
750  }
751  else
752  {
753  last_line->SetEndPoint( line->GetEndPoint() );
754  delete s_wires.Remove( line );
755  }
756  }
757  else
758  {
759  last_lines.push_back( line );
760  }
761  }
762  else
763  {
764  last_lines.push_back( line );
765  }
766  }
767 }
768 
769 
771 {
772  // Clear selection when done so that a new wire can be started.
773  // NOTE: this must be done before RemoveBacktracks is called or we might end up with
774  // freed selected items.
776 
777  PICKED_ITEMS_LIST itemList;
778 
779  // Remove segments backtracking over others
781 
782  // Collect the possible connection points for the new lines
783  std::vector< wxPoint > connections;
784  std::vector< wxPoint > new_ends;
785  m_frame->GetSchematicConnections( connections );
786 
787  // Check each new segment for possible junctions and add/split if needed
788  for( SCH_LINE* wire = s_wires.GetFirst(); wire; wire = wire->Next() )
789  {
790  if( wire->HasFlag( SKIP_STRUCT ) )
791  continue;
792 
793  wire->GetConnectionPoints( new_ends );
794 
795  for( auto i : connections )
796  {
797  if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), i ) )
798  new_ends.push_back( i );
799  }
800  itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) );
801  }
802 
804  {
805  wxASSERT( m_busUnfold.entry && m_busUnfold.label );
806 
807  itemList.PushItem( ITEM_PICKER( m_busUnfold.entry, UR_NEW ) );
808  itemList.PushItem( ITEM_PICKER( m_busUnfold.label, UR_NEW ) );
809  }
810 
811  // Get the last non-null wire (this is the last created segment).
812  m_frame->SaveCopyForRepeatItem( s_wires.GetLast() );
813 
814  // Add the new wires
815  while( s_wires.GetFirst() )
816  {
817  s_wires.GetFirst()->ClearFlags( IS_NEW | IS_MOVED );
818  m_frame->AddToScreen( s_wires.PopFront() );
819  }
820 
821  m_view->ClearPreview();
822  m_view->ShowPreview( false );
823 
824  getViewControls()->CaptureCursor( false );
825  getViewControls()->SetAutoPan( false );
826 
827  m_frame->SaveCopyInUndoList( itemList, UR_NEW );
828 
829  // Correct and remove segments that need to be merged.
831 
832  for( auto item = m_frame->GetScreen()->GetDrawItems(); item; item = item->Next() )
833  {
834  if( item->Type() != SCH_COMPONENT_T )
835  continue;
836 
837  std::vector< wxPoint > pts;
838  item->GetConnectionPoints( pts );
839 
840  if( pts.size() > 2 )
841  continue;
842 
843  for( auto i = pts.begin(); i != pts.end(); i++ )
844  {
845  for( auto j = i + 1; j != pts.end(); j++ )
846  m_frame->TrimWire( *i, *j );
847  }
848  }
849 
850  for( auto i : new_ends )
851  {
852  if( m_frame->GetScreen()->IsJunctionNeeded( i, true ) )
853  m_frame->AddJunction( i, true, false );
854  }
855 
857  m_busUnfold = {};
858 
861 
862  m_frame->OnModify();
863 }
864 
865 
867 {
868  EE_SELECTION* aSelection = aEvent.Parameter<EE_SELECTION*>();
869 
870  std::vector<wxPoint> pts;
871  std::vector<wxPoint> connections;
872 
873  m_frame->GetSchematicConnections( connections );
874 
875  for( unsigned ii = 0; ii < aSelection->GetSize(); ii++ )
876  {
877  SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aSelection->GetItem( ii ) );
878  std::vector<wxPoint> new_pts;
879 
880  if( !item || !item->IsConnectable() )
881  continue;
882 
883  item->GetConnectionPoints( new_pts );
884  pts.insert( pts.end(), new_pts.begin(), new_pts.end() );
885 
886  // If the item is a line, we also add any connection points from the rest of the schematic
887  // that terminate on the line after it is moved.
888  if( item->Type() == SCH_LINE_T )
889  {
890  SCH_LINE* line = (SCH_LINE*) item;
891  for( auto i : connections )
892  {
893  if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), i ) )
894  pts.push_back( i );
895  }
896  }
897  else
898  {
899  // Clean up any wires that short non-wire connections in the list
900  for( auto point = new_pts.begin(); point != new_pts.end(); point++ )
901  {
902  for( auto second_point = point + 1; second_point != new_pts.end(); second_point++ )
903  m_frame->TrimWire( *point, *second_point );
904  }
905  }
906  }
907 
908  // We always have some overlapping connection points. Drop duplicates here
909  std::sort( pts.begin(), pts.end(), []( const wxPoint& a, const wxPoint& b ) -> bool {
910  return a.x < b.x || ( a.x == b.x && a.y < b.y );
911  } );
912 
913  pts.erase( unique( pts.begin(), pts.end() ), pts.end() );
914 
915  for( auto point : pts )
916  {
917  if( m_frame->GetScreen()->IsJunctionNeeded( point, true ) )
918  m_frame->AddJunction( point, true, false );
919  }
920 
921  return 0;
922 }
923 
924 
926 {
931 
933 }
virtual void ShowCursor(bool aEnabled)
Function ShowCursor() Enables or disables display of cursor.
CITER next(CITER it)
Definition: ptree.cpp:130
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Function AddMenu()
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
const wxRealPoint & GetGridSize() const
Return the grid size of the currently selected grid.
Definition: base_screen.h:279
bool IsPointOnSegment(const wxPoint &aSegStart, const wxPoint &aSegEnd, const wxPoint &aTestPoint)
Function IsPointOnSegment.
Definition: trigo.cpp:40
bool SchematicCleanUp(SCH_SCREEN *aScreen=nullptr)
Performs routine schematic cleaning including breaking wire and buses and deleting identical objects ...
TOOL_MENU m_menu
functions below are not yet implemented - their interface may change
virtual bool IsConnectable() const
Function IsConnectable returns true if the schematic item can connect to another schematic item.
Definition: sch_item.h:298
void AddToPreview(EDA_ITEM *aItem, bool aTakeOwnership=true)
Definition: sch_view.cpp:155
static bool IsDrawingWire(const SELECTION &aSelection)
SCH_SHEET_PINS & GetPins()
Definition: sch_sheet.h:335
EDA_BASE_FRAME * GetEditFrame() const
Definition: tool_manager.h:268
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Function Wait()
static TOOL_ACTION breakBus
Definition: ee_actions.h:138
void GetSchematicConnections(std::vector< wxPoint > &aConnections)
Collects a unique list of all possible connection points in the schematic.
void RecalculateConnections(SCH_CLEANUP_FLAGS aCleanupFlags)
Generates the connection data for the entire schematic hierarchy.
wxPoint GetStartPoint() const
Definition: sch_line.h:95
void AddToScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
SCH_SHEET * Last() const
Function Last returns a pointer to the last sheet of the list One can see the others sheet as the "pa...
Class ACTION_MENU.
Definition: action_menu.h:43
This file is part of the common library.
int doDrawSegments(const std::string &aTool, int aType, SCH_LINE *aSegment)
bool IsJunctionNeeded(const wxPoint &aPosition, bool aNew=false)
Test if a junction is required for the items at aPosition on the screen.
Definition: sch_screen.cpp:347
SCH_ITEM * Next() const
Definition: sch_item.h:153
static SELECTION_CONDITION MoreThan(int aNumber)
Function MoreThan Creates a functor that tests if the number of selected items is greater than the va...
void SetCurrentCursor(wxStockCursor aStockCursorID)
Function SetCurrentCursor Set the current cursor shape for this panel.
VIEW_CONTROLS class definition.
SCH_LINE * doUnfoldBus(const wxString &aNet)
bool HasPoint()
Indicates the cursor is over an edit point.
#define SKIP_STRUCT
flag indicating that the structure should be ignored
Definition: base_struct.h:131
virtual void SetLabelSpinStyle(int aSpinStyle)
Set a spin or rotation angle, along with specific horizontal and vertical justification styles with e...
Definition: sch_text.cpp:234
int AddJunctionsIfNeeded(const TOOL_EVENT &aEvent)
Handle the addition of junctions to a selection of objects.
CONDITIONAL_MENU & GetMenu()
Function GetMenu.
Definition: tool_menu.cpp:46
static TOOL_ACTION placeHierLabel
Definition: ee_actions.h:90
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:219
static TOOL_ACTION placeJunction
Definition: ee_actions.h:85
static void removeBacktracks(DLIST< SCH_LINE > &aWires)
In a contiguous list of wires, remove wires that backtrack over the previous wire.
static TOOL_ACTION selectConnection
If current selection is a wire or bus, expand to entire connection.
Definition: ee_actions.h:55
void SetPosition(const wxPoint &aPosition) override
Function SetPosition set the schematic item position to aPosition.
SCH_LINE * startSegments(int aType, const VECTOR2D &aPos)
static TOOL_ACTION unfoldBus
Definition: ee_actions.h:83
static TOOL_ACTION finishLineWireOrBus
Definition: ee_actions.h:97
virtual void GetConnectionPoints(std::vector< wxPoint > &aPoints) const
Function GetConnectionPoints add all the connection points for this item to aPoints.
Definition: sch_item.h:308
TOOL_MENU & GetToolMenu()
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:109
static bool IsDrawingLine(const SELECTION &aSelection)
virtual bool IsType(const KICAD_T aScanTypes[])
Function IsType Checks whether the item is one of the listed types.
Definition: base_struct.h:294
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
SHEET_SIDE GetEdge() const
Schematic editor (Eeschema) main window.
bool TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
void RemoveFromScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen=nullptr)
Remove an item from the screen (and view) aScreen is the screen the item is located on,...
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:222
Class DLIST is the head of a doubly linked list.
Definition: dlist.h:142
static SELECTION_CONDITION Idle
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
Function SetContextMenu()
void update() override
Update menu state stub.
void setTransitions() override
Sets up handlers for various events.
int UnfoldBus(const TOOL_EVENT &aEvent)
#define abs(a)
Definition: auxiliary.h:84
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Function Go()
search types array terminator (End Of Types)
Definition: typeinfo.h:82
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
static const TOOL_EVENT SelectedItemsModified
Definition: actions.h:201
static TOOL_ACTION breakWire
Definition: ee_actions.h:137
static TOOL_ACTION finishLine
Definition: ee_actions.h:100
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:99
void SetTool(TOOL_INTERACTIVE *aTool)
Function SetTool() Sets a tool that is the creator of the menu.
bool IsNew() const
Definition: base_struct.h:228
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
static TOOL_ACTION leaveSheet
Definition: ee_actions.h:177
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:69
EE_SELECTION & GetSelection()
Function GetSelection()
#define IS_NEW
New item, just created.
Definition: base_struct.h:120
EE_SELECTION & RequestSelection(const KICAD_T *aFilterList=EE_COLLECTOR::AllItems)
Function RequestSelection()
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
virtual void WarpCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
Function WarpCursor() If enabled (.
SCH_LINE * Next() const
Definition: sch_line.h:57
EDA_ITEM * Clone() const override
Function Clone creates a duplicate of this item with linked list members set to NULL.
Definition: sch_text.cpp:656
static const SCH_SHEET_PIN * getSheetPin(SCH_SCREEN *aScreen, const wxPoint &aPosition)
A helper function to find any sheet pins at the specified position.
wxPoint origin
Origin (on the bus) of the unfold.
void SetFlags(STATUS_FLAGS aMask)
Definition: base_struct.h:265
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:47
SCH_SHEET_PATH * g_CurrentSheet
With the new connectivity algorithm, many more places than before want to know what the current sheet...
static bool isNewSegment(SCH_ITEM *aItem)
static TOOL_ACTION drawWire
Definition: ee_actions.h:81
static void computeBreakPoint(SCH_SCREEN *aScreen, SCH_LINE *aSegment, wxPoint &aPosition)
Function ComputeBreakPoint computes the middle coordinate for 2 segments from the start point to aPos...
void SetParent(EDA_ITEM *aParent)
Definition: base_struct.h:225
bool GetForceHVLines() const
virtual void CaptureCursor(bool aEnabled)
Function CaptureCursor() Forces the cursor to stay within the drawing panel area.
bool IsNull() const
Definition: sch_line.h:93
void ShowPreview(bool aShow=true)
Definition: sch_view.cpp:169
T Parameter() const
Function Parameter() Returns a non-standard parameter assigned to the event.
Definition: tool_event.h:435
void SetStartPoint(const wxPoint &aPosition)
Definition: sch_line.h:96
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void SetIcon(const BITMAP_OPAQUE *aIcon)
Function SetIcon() Assigns an icon for the entry.
Definition: action_menu.cpp:68
Class TOOL_EVENT.
Definition: tool_event.h:171
void SaveCopyInUndoList(SCH_ITEM *aItemToCopy, UNDO_REDO_T aTypeCommand, bool aAppend=false, const wxPoint &aTransformPoint=wxPoint(0, 0))
Create a copy of the current schematic item, and put it in the undo list.
void ClearPreview()
Definition: sch_view.cpp:143
void SaveCopyForRepeatItem(SCH_ITEM *aItem)
Clone aItem and owns that clone in this container.
SCH_JUNCTION * AddJunction(const wxPoint &aPos, bool aAppendToUndo=false, bool aFinal=true)
VIEW_GROUP extends VIEW_ITEM by possibility of grouping items into a single object.
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:140
char GetBusEntryShape() const
function GetBusEntryShape
SCH_LAYER_ID
Eeschema drawing layers.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:75
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:58
void SetBusEntryShape(char aShape)
function SetBusEntryShape
virtual void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true)=0
Moves the graphic crosshair cursor to the requested position expressed in world coordinates.
wxPoint m_End() const
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
static TOOL_ACTION addNeededJunctions
Definition: ee_actions.h:77
wxString net_name
Net label for the unfolding operation.
virtual KIGFX::VIEW_ITEM * GetItem(unsigned int aIdx) const override
Definition: selection.h:103
T * GetFirst() const
Function GetFirst returns the first T* in the list without removing it, or NULL if the list is empty.
Definition: dlist.h:163
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_text.h:198
#define _(s)
virtual void SetAutoPan(bool aEnabled)
Function SetAutoPan Turns on/off auto panning (this feature is used when there is a tool active (eg.
SCH_BUS_WIRE_ENTRY * entry
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieves the connection associated with this object in the given sheet.
Definition: sch_item.cpp:128
static bool IsDrawingBus(const SELECTION &aSelection)
static TOOL_ACTION drawBus
Definition: ee_actions.h:82
bool offset
True if the bus entry should be offset from origin.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:209
virtual unsigned int GetSize() const override
Function GetSize() Returns the number of stored items.
Definition: selection.h:98
SCH_LAYER_ID GetLayer() const
Function GetLayer returns the layer this item is on.
Definition: sch_item.h:193
int Modifier(int aMask=MD_MODIFIER_MASK) const
Returns information about key modifiers state (Ctrl, Alt, etc.)
Definition: tool_event.h:342
static bool m_allowRealTime
static TOOL_ACTION placeLabel
Definition: ee_actions.h:88
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:463
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
wxSize GetSize() const
Definition: sch_bus_entry.h:80
bool IsTerminalPoint(const wxPoint &aPosition, int aLayer)
Test if aPosition is a connection point on aLayer.
Definition: sch_screen.cpp:425
void SetTitle(const wxString &aTitle) override
Function SetTitle() Sets title for the menu.
Definition: action_menu.cpp:86
SCH_LINE * Back() const
Definition: sch_line.h:58
int AddItemToSel(const TOOL_EVENT &aEvent)
const char * name
Definition: DXF_plotter.cpp:61
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
void RefreshItem(EDA_ITEM *aItem, bool isAddOrDelete=false)
Mark an item for refresh.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
ACTION_MENU * create() const override
Returns an instance of this class. It has to be overridden in inheriting classes.
Class EE_POINT_EDITOR.
TOOL_MANAGER * getToolManager() const
Returns an instance of TOOL_MANAGER class.
void AddSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Function CreateSubMenu.
Definition: tool_menu.cpp:52
size_t i
Definition: json11.cpp:649
static SELECTION_CONDITION OnlyType(KICAD_T aType)
Function OnlyType Creates a functor that tests if the selected items are only of given type.
void VetoContextMenuMouseWarp()
Disables mouse warping after the current context menu is closed.
Definition: tool_manager.h:384
void Clear()
Function Clear() Removes all the entries from the menu (as well as its title).
static TOOL_ACTION selectNode
Select the junction, wire or bus segment under the cursor.
Definition: ee_actions.h:51
boost::optional< T > OPT
Definition: optional.h:7
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
static DLIST< SCH_LINE > s_wires
virtual void PopTool(const std::string &actionName)
Class for a wire to bus entry.
void Activate()
Function Activate() Runs the tool.
bool TrimWire(const wxPoint &aStart, const wxPoint &aEnd)
If any single wire passes through both points, remove the portion between the two points,...
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.
Class EE_TOOL_BASE.
Definition: ee_tool_base.h:50
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition: tool_event.h:260
static TOOL_ACTION drawLines
Definition: ee_actions.h:95
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
bool label_placed
True if user has placed the net label.
ACTION_MENU(bool isContextMenu)
Default constructor
Definition: action_menu.cpp:43
KIGFX::VIEW_CONTROLS * getViewControls() const
Function getViewControls()
Definition: tool_base.cpp:42
bool IsBus() const
static TOOL_ACTION placeGlobalLabel
Definition: ee_actions.h:89
static TOOL_ACTION finishBus
Definition: ee_actions.h:99
void SetPosition(const wxPoint &aPosition) override
Function SetPosition set the schematic item position to aPosition.
Definition: sch_text.h:199
void PostEvent(const TOOL_EVENT &aEvent)
Puts an event to the event queue to be processed at the end of event processing cycle.
Definition: tool_manager.h:237
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Function AddItem()
bool IsCurrentTool(const TOOL_ACTION &aAction) const
Class SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container clas...
Definition: sch_item.h:114
Definitions of the SCH_TEXT class and derivatives for Eeschema.
void ShowContextMenu(SELECTION &aSelection)
Function ShowContextMenu.
Definition: tool_menu.cpp:59
int DrawSegments(const TOOL_EVENT &aEvent)
static TOOL_ACTION finishWire
Definition: ee_actions.h:98
BUS_UNFOLDING_T m_busUnfold
Data related to bus unfolding tool.
VECTOR2D GetCursorPosition() const
Returns the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:182
int GetDefaultTextSize()
Default size for text in general.
bool in_progress
True if bus unfold operation is running.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:210
This item represents a bus vector.
wxPoint GetPosition() const override
Function GetPosition.
#define IS_MOVED
Item being moved.
Definition: base_struct.h:119
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:152
wxPoint GetEndPoint() const
Definition: sch_line.h:98