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 <sch_line_wire_bus_tool.h>
26 
27 #include <boost/optional/optional.hpp>
28 #include <wx/debug.h>
29 #include <wx/gdicmn.h>
30 #include <wx/menu.h>
31 #include <wx/string.h>
32 #include <wx/stringimpl.h>
33 #include <wx/translation.h>
34 #include <algorithm>
35 #include <cstdlib>
36 #include <deque>
37 #include <iterator>
38 #include <memory>
39 #include <string>
40 #include <utility>
41 #include <vector>
42 
43 #include <base_screen.h>
44 #include <base_struct.h>
45 #include <bitmaps.h>
46 #include <core/typeinfo.h>
47 #include <eda_text.h>
49 #include <math/vector2d.h>
50 #include <advanced_config.h>
51 #include <tool/actions.h>
52 #include <tool/conditional_menu.h>
53 #include <tool/selection.h>
55 #include <tool/tool_action.h>
56 #include <tool/tool_event.h>
57 #include <tool/tool_interactive.h>
58 #include <tool/tool_manager.h>
59 #include <trigo.h>
60 #include <undo_redo_container.h>
61 #include <view/view_controls.h>
62 
63 #include <connection_graph.h>
64 #include <eeschema_id.h>
65 #include <general.h>
66 #include <sch_bus_entry.h>
67 #include <sch_connection.h>
68 #include <sch_draw_panel.h>
69 #include <sch_edit_frame.h>
70 #include <sch_item.h>
71 #include <sch_line.h>
72 #include <sch_screen.h>
73 #include <sch_sheet.h>
74 #include <sch_sheet_path.h>
75 #include <sch_text.h>
76 #include <sch_view.h>
77 
78 #include <ee_actions.h>
79 #include <ee_point_editor.h>
80 #include <ee_selection.h>
81 #include <ee_selection_tool.h>
82 
84 {
85 public:
87  ACTION_MENU( true ),
88  m_showTitle( false )
89  {
91  SetTitle( _( "Unfold from Bus" ) );
92  }
93 
94  void SetShowTitle()
95  {
96  m_showTitle = true;
97  }
98 
99 
100 protected:
101  ACTION_MENU* create() const override
102  {
103  return new BUS_UNFOLD_MENU();
104  }
105 
106 private:
107  void update() override
108  {
111  KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT };
112  EE_SELECTION& selection = selTool->RequestSelection( busType );
113  SCH_LINE* bus = (SCH_LINE*) selection.Front();
114 
115  Clear();
116 
117  // TODO(JE) remove once real-time is enabled
118  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
119  {
121 
122  // Pick up the pointer again because it may have been changed by SchematicCleanUp
123  selection = selTool->RequestSelection( busType );
124  bus = (SCH_LINE*) selection.Front();
125  }
126 
127  if( !bus )
128  {
129  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "No bus selected" ), wxEmptyString );
130  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
131  return;
132  }
133 
134  SCH_CONNECTION* connection = bus->Connection( *g_CurrentSheet );
135 
136  if( !connection || !connection->IsBus() || connection->Members().empty() )
137  {
138  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "Bus has no connections" ), wxEmptyString );
139  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
140  return;
141  }
142 
143  int idx = 0;
144 
145  if( m_showTitle )
146  {
147  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "Unfold from Bus" ), wxEmptyString );
148  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
149  }
150 
151  for( const auto& member : connection->Members() )
152  {
153  int id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
154  wxString name = UnescapeString( member->LocalName() );
155 
156  if( member->Type() == CONNECTION_TYPE::BUS )
157  {
158  ACTION_MENU* submenu = new ACTION_MENU( true );
159  submenu->SetTool( m_tool );
160  AppendSubMenu( submenu, name );
161 
162  for( const auto& sub_member : member->Members() )
163  {
164  id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
165  submenu->Append( id, UnescapeString( sub_member->LocalName() ), wxEmptyString );
166  }
167  }
168  else
169  {
170  Append( id, name, wxEmptyString );
171  }
172  }
173  }
174 
176 };
177 
178 
180  EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveDrawingLineWireBus" )
181 {
182  m_busUnfold = {};
183  m_wires.reserve( 16 );
184 }
185 
186 
188 {
189 }
190 
191 
193 {
195 
196  auto wireOrBusTool = [ this ] ( const SELECTION& aSel ) {
199  };
200 
201  auto lineTool = [ this ] ( const SELECTION& aSel ) {
203  };
204 
205  auto belowRootSheetCondition = [] ( const SELECTION& aSel ) {
206  return g_CurrentSheet->Last() != g_RootSheet;
207  };
208 
209  auto busSelection = EE_CONDITIONS::MoreThan( 0 )
211 
212  auto& ctxMenu = m_menu.GetMenu();
213 
214  // Build the tool menu
215  //
216  ctxMenu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 2 );
217 
218  ctxMenu.AddSeparator( 10 );
219  ctxMenu.AddItem( EE_ACTIONS::drawWire, wireOrBusTool && EE_CONDITIONS::Idle, 10 );
220  ctxMenu.AddItem( EE_ACTIONS::drawBus, wireOrBusTool && EE_CONDITIONS::Idle, 10 );
221  ctxMenu.AddItem( EE_ACTIONS::drawLines, lineTool && EE_CONDITIONS::Idle, 10 );
222  ctxMenu.AddItem( EE_ACTIONS::finishWire, IsDrawingWire, 10 );
223  ctxMenu.AddItem( EE_ACTIONS::finishBus, IsDrawingBus, 10 );
224  ctxMenu.AddItem( EE_ACTIONS::finishLine, IsDrawingLine, 10 );
225 
226  std::shared_ptr<BUS_UNFOLD_MENU> busUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>();
227  busUnfoldMenu->SetTool( this );
228  m_menu.AddSubMenu( busUnfoldMenu );
229  ctxMenu.AddMenu( busUnfoldMenu.get(), EE_CONDITIONS::Idle, 10 );
230 
231  ctxMenu.AddSeparator( 100 );
232  ctxMenu.AddItem( EE_ACTIONS::placeJunction, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
233  ctxMenu.AddItem( EE_ACTIONS::placeLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
234  ctxMenu.AddItem( EE_ACTIONS::placeGlobalLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
235  ctxMenu.AddItem( EE_ACTIONS::placeHierLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
236  ctxMenu.AddItem( EE_ACTIONS::breakWire, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
237  ctxMenu.AddItem( EE_ACTIONS::breakBus, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
238 
239  ctxMenu.AddSeparator( 200 );
240  ctxMenu.AddItem( EE_ACTIONS::selectNode, wireOrBusTool && EE_CONDITIONS::Idle, 200 );
241  ctxMenu.AddItem( EE_ACTIONS::selectConnection, wireOrBusTool && EE_CONDITIONS::Idle, 200 );
242 
243  // Add bus unfolding to the selection tool
244  //
246 
247  std::shared_ptr<BUS_UNFOLD_MENU> selBusUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>();
248  selBusUnfoldMenu->SetTool( m_selectionTool );
249  m_selectionTool->GetToolMenu().AddSubMenu( selBusUnfoldMenu );
250  selToolMenu.AddMenu( selBusUnfoldMenu.get(), busSelection && EE_CONDITIONS::Idle, 100 );
251 
252  return true;
253 }
254 
255 
257 {
258  static KICAD_T graphicLineType[] = { SCH_LINE_LOCATE_GRAPHIC_LINE_T, EOT };
259  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( graphicLineType );
260 }
261 
262 
264 {
265  static KICAD_T wireType[] = { SCH_LINE_LOCATE_WIRE_T, EOT };
266  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( wireType );
267 }
268 
269 
271 {
272  static KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT };
273  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( busType );
274 }
275 
276 
278 {
279  // NOTE: for immediate hotkeys, it is NOT required that the line, wire or bus tool
280  // be selected
281  SCH_ITEM* item = (SCH_ITEM*) aSelection.Front();
282  return item && item->IsNew() && item->Type() == SCH_LINE_T;
283 }
284 
285 
287 {
288  SCH_LAYER_ID layer = aEvent.Parameter<SCH_LAYER_ID>();
289 
290  if( aEvent.HasPosition() )
291  getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true );
292 
293  std::string tool = aEvent.GetCommandStr().get();
294  m_frame->PushTool( tool );
295 
296  if( aEvent.HasPosition() )
297  {
298  VECTOR2D cursorPos = getViewControls()->GetCursorPosition( !aEvent.Modifier( MD_ALT ) );
299  startSegments( layer, cursorPos );
300  }
301 
302  return doDrawSegments( tool, layer );
303 }
304 
305 
307 {
308  wxString* netPtr = aEvent.Parameter<wxString*>();
309  wxString net;
310  SCH_LINE* segment = nullptr;
311 
312  std::string tool = aEvent.GetCommandStr().get();
313  m_frame->PushTool( tool );
314  Activate();
315 
316  if( netPtr )
317  {
318  net = *netPtr;
319  delete netPtr;
320  }
321  else
322  {
323  BUS_UNFOLD_MENU unfoldMenu;
324  unfoldMenu.SetTool( this );
325  unfoldMenu.SetShowTitle();
326 
327  SetContextMenu( &unfoldMenu, CMENU_NOW );
328 
329  while( TOOL_EVENT* evt = Wait() )
330  {
331  if( evt->Action() == TA_CHOICE_MENU_CHOICE )
332  {
333  OPT<int> id = evt->GetCommandId();
334 
335  if( id && ( *id > 0 ) )
336  net = *evt->Parameter<wxString*>();
337 
338  break;
339  }
340  else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
341  {
342  break;
343  }
344  else
345  {
346  evt->SetPassEvent();
347  }
348  }
349  }
350 
351  // Break a wire for the given net out of the bus
352  if( !net.IsEmpty() )
353  segment = doUnfoldBus( net );
354 
355  // If we have an unfolded wire to draw, then draw it
356  if( segment )
357  return doDrawSegments( tool, LAYER_WIRE );
358  else
359  {
360  m_frame->PopTool( tool );
361  return 0;
362  }
363 }
364 
365 
367 {
369 
371 
372  m_busUnfold.entry = new SCH_BUS_WIRE_ENTRY( pos, '\\' );
375 
376  m_busUnfold.label = new SCH_LABEL( m_busUnfold.entry->m_End(), aNet );
381 
382  m_busUnfold.in_progress = true;
383  m_busUnfold.origin = pos;
384  m_busUnfold.net_name = aNet;
385 
387 
389 }
390 
391 
393 {
394  SCH_SCREEN* screen = m_frame->GetScreen();
395 
396  for( auto item : screen->Items().Overlapping( SCH_SHEET_T, aPosition ) )
397  {
398  auto sheet = static_cast<SCH_SHEET*>( item );
399 
400  for( SCH_SHEET_PIN* pin : sheet->GetPins() )
401  {
402  if( pin->GetPosition() == aPosition )
403  return pin;
404  }
405  }
406 
407  return nullptr;
408 }
409 
410 
411 void SCH_LINE_WIRE_BUS_TOOL::computeBreakPoint( const std::pair<SCH_LINE*, SCH_LINE*>& aSegments,
412  wxPoint& aPosition )
413 {
414  wxCHECK_RET( aSegments.first && aSegments.second,
415  wxT( "Cannot compute break point of NULL line segment." ) );
416 
417  SCH_LINE* segment = aSegments.first;
418  SCH_LINE* next_segment = aSegments.second;
419 
420  wxPoint midPoint;
421  int iDx = segment->GetEndPoint().x - segment->GetStartPoint().x;
422  int iDy = segment->GetEndPoint().y - segment->GetStartPoint().y;
423 
424  const SCH_SHEET_PIN* connectedPin = getSheetPin( segment->GetStartPoint() );
425  auto force = connectedPin ? connectedPin->GetEdge() : SHEET_UNDEFINED_SIDE;
426 
427  if( force == SHEET_LEFT_SIDE || force == SHEET_RIGHT_SIDE )
428  {
429  if( aPosition.x == connectedPin->GetPosition().x ) // push outside sheet boundary
430  {
431  int direction = ( force == SHEET_LEFT_SIDE ) ? -1 : 1;
432  aPosition.x += int( m_frame->GetScreen()->GetGridSize().x * direction );
433  }
434 
435  midPoint.x = aPosition.x;
436  midPoint.y = segment->GetStartPoint().y; // force horizontal
437  }
438  else if( iDy != 0 ) // keep the first segment orientation (vertical)
439  {
440  midPoint.x = segment->GetStartPoint().x;
441  midPoint.y = aPosition.y;
442  }
443  else if( iDx != 0 ) // keep the first segment orientation (horizontal)
444  {
445  midPoint.x = aPosition.x;
446  midPoint.y = segment->GetStartPoint().y;
447  }
448  else
449  {
450  if( std::abs( aPosition.x - segment->GetStartPoint().x ) <
451  std::abs( aPosition.y - segment->GetStartPoint().y ) )
452  {
453  midPoint.x = segment->GetStartPoint().x;
454  midPoint.y = aPosition.y;
455  }
456  else
457  {
458  midPoint.x = aPosition.x;
459  midPoint.y = segment->GetStartPoint().y;
460  }
461  }
462 
463  segment->SetEndPoint( midPoint );
464  next_segment->SetStartPoint( midPoint );
465  next_segment->SetEndPoint( aPosition );
466 }
467 
468 
469 int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const std::string& aTool, int aType )
470 {
471  SCH_SCREEN* screen = m_frame->GetScreen();
473  SCH_LINE* segment = nullptr;
474 
476  getViewControls()->ShowCursor( true );
477 
478  Activate();
479 
480  // Add the new label to the selection so the rotate command operates on it
481  if( m_busUnfold.label )
483 
484  // Continue the existing wires if we've started (usually by immediate action preference)
485  if( !m_wires.empty() )
486  segment = m_wires.back();
487 
488  // Main loop: keep receiving events
489  while( TOOL_EVENT* evt = Wait() )
490  {
491  if( !pointEditor->HasPoint() ) // Set wxCursor shape when starting the tool
492  m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
493 
494  wxPoint cursorPos = (wxPoint) getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
495  bool forceHV = m_frame->GetForceHVLines();
496 
497  //------------------------------------------------------------------------
498  // Handle cancel:
499  //
500  auto cleanup = [&] () {
502 
503  for( auto wire : m_wires )
504  delete wire;
505 
506  m_wires.clear();
507  segment = nullptr;
508 
509  if( m_busUnfold.entry )
511 
514 
517 
518  delete m_busUnfold.entry;
519  delete m_busUnfold.label;
520  m_busUnfold = {};
521 
522  m_view->ClearPreview();
523  m_view->ShowPreview( false );
524  };
525 
526  if( evt->IsCancelInteractive() )
527  {
528  if( segment || m_busUnfold.in_progress )
529  cleanup();
530  else
531  {
532  m_frame->PopTool( aTool );
533  break;
534  }
535  }
536  else if( evt->IsActivate() )
537  {
538  if( segment || m_busUnfold.in_progress )
539  cleanup();
540 
541  if( evt->IsMoveTool() )
542  {
543  // leave ourselves on the stack so we come back after the move
544  break;
545  }
546  else
547  {
548  m_frame->PopTool( aTool );
549  break;
550  }
551  }
552  //------------------------------------------------------------------------
553  // Handle finish:
554  //
555  else if( evt->IsAction( &EE_ACTIONS::finishLineWireOrBus )
556  || evt->IsAction( &EE_ACTIONS::finishWire )
557  || evt->IsAction( &EE_ACTIONS::finishBus )
558  || evt->IsAction( &EE_ACTIONS::finishLine ) )
559  {
560  if( segment || m_busUnfold.in_progress )
561  {
562  finishSegments();
563  segment = nullptr;
564  }
565  }
566  //------------------------------------------------------------------------
567  // Handle click:
568  //
569  else if( evt->IsClick( BUT_LEFT ) || ( segment && evt->IsDblClick( BUT_LEFT ) ) )
570  {
571  // First click when unfolding places the label and wire-to-bus entry
573  {
574  wxASSERT( aType == LAYER_WIRE );
575 
578  m_busUnfold.label_placed = true;
579  }
580 
581  if( !segment )
582  {
583  segment = startSegments( aType, VECTOR2D( cursorPos ) );
584  }
585  // Create a new segment if we're out of previously-created ones
586  else if( !segment->IsNull() || ( forceHV && !m_wires.end()[-2]->IsNull() ) )
587  {
588  // Terminate the command if the end point is on a pin, junction, or another
589  // wire or bus.
591  && screen->IsTerminalPoint( cursorPos, segment->GetLayer() ) )
592  {
593  finishSegments();
594  segment = nullptr;
595  }
596  else
597  {
598  segment->SetEndPoint( cursorPos );
599 
600  // Create a new segment, and chain it after the current segment.
601  segment = new SCH_LINE( *segment );
602  segment->SetFlags( IS_NEW | IS_MOVED );
603  segment->SetStartPoint( cursorPos );
604  m_wires.push_back( segment );
605 
606  m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
607  }
608  }
609 
610  if( evt->IsDblClick( BUT_LEFT ) && segment )
611  {
612  if( forceHV && m_wires.size() >= 2 )
613  computeBreakPoint( { m_wires.end()[-2], segment }, cursorPos );
614 
615  finishSegments();
616  segment = nullptr;
617  }
618  }
619  //------------------------------------------------------------------------
620  // Handle motion:
621  //
622  else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
623  {
624  m_view->ClearPreview();
625 
626  // Update the bus unfold posture based on the mouse movement
628  {
629  wxPoint cursor_delta = cursorPos - m_busUnfold.origin;
631 
632  bool offset = ( cursor_delta.x < 0 );
633  char shape = ( offset ? ( ( cursor_delta.y >= 0 ) ? '/' : '\\' )
634  : ( ( cursor_delta.y >= 0 ) ? '\\' : '/' ) );
635 
636  // Erase and redraw if necessary
637  if( shape != entry->GetBusEntryShape() || offset != m_busUnfold.offset )
638  {
639  entry->SetBusEntryShape( shape );
640  wxPoint entry_pos = m_busUnfold.origin;
641 
642  if( offset )
643  entry_pos -= entry->GetSize();
644 
645  entry->SetPosition( entry_pos );
646  m_busUnfold.offset = offset;
647 
648  m_frame->RefreshItem( entry );
649 
650  wxPoint wire_start = offset ? entry->GetPosition() : entry->m_End();
651  m_wires.front()->SetStartPoint( wire_start );
652  }
653 
654  // Update the label "ghost" position
655  m_busUnfold.label->SetPosition( cursorPos );
657 
658  // Ensure segment is non-null at the start of bus unfold
659  if( !segment )
660  segment = m_wires.back();
661  }
662 
663  if( segment )
664  {
665  // Coerce the line to vertical or horizontal if necessary
666  if( forceHV && m_wires.size() >= 2 )
667  computeBreakPoint( { m_wires.end()[-2], segment }, cursorPos );
668  else
669  segment->SetEndPoint( cursorPos );
670  }
671 
672  for( auto wire : m_wires )
673  {
674  if( !wire->IsNull() )
675  m_view->AddToPreview( wire->Clone() );
676  }
677  }
678  //------------------------------------------------------------------------
679  // Handle context menu:
680  //
681  else if( evt->IsClick( BUT_RIGHT ) )
682  {
683  // Warp after context menu only if dragging...
684  if( !segment )
686 
688  }
689  else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
690  {
691  if( evt->GetCommandId().get() >= ID_POPUP_SCH_UNFOLD_BUS
692  && evt->GetCommandId().get() <= ID_POPUP_SCH_UNFOLD_BUS_END )
693  {
694  wxASSERT_MSG( !segment, "Bus unfold event received when already drawing!" );
695 
696  aType = LAYER_WIRE;
697  wxString net = *evt->Parameter<wxString*>();
698  segment = doUnfoldBus( net );
699  }
700  }
701  else
702  evt->SetPassEvent();
703 
704  // Enable autopanning and cursor capture only when there is a segment to be placed
705  getViewControls()->SetAutoPan( segment != nullptr );
706  getViewControls()->CaptureCursor( segment != nullptr );
707  }
708 
709  return 0;
710 }
711 
712 
714 {
715  SCH_LINE* segment = nullptr;
716 
717  switch ( aType )
718  {
719  default:
720  segment = new SCH_LINE( aPos, LAYER_NOTES );
721  break;
722  case LAYER_WIRE:
723  segment = new SCH_LINE( aPos, LAYER_WIRE );
724  break;
725  case LAYER_BUS:
726  segment = new SCH_LINE( aPos, LAYER_BUS );
727  break;
728  }
729 
730  segment->SetFlags( IS_NEW | IS_MOVED );
731  m_wires.push_back( segment );
732 
733  m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
734 
735  // We need 2 segments to go from a given start pin to an end point when the
736  // horizontal and vertical lines only switch is on.
737  if( m_frame->GetForceHVLines() )
738  {
739  segment = new SCH_LINE( *segment );
740  segment->SetFlags( IS_NEW | IS_MOVED );
741  m_wires.push_back( segment );
742 
743  m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
744  }
745 
746  return segment;
747 }
748 
749 
764 {
765  for( auto it = m_wires.begin(); it != m_wires.end(); )
766  {
767  SCH_LINE* line = *it;
768 
769  if( line->IsNull() )
770  {
771  delete line;
772  it = m_wires.erase( it );
773  continue;
774  }
775 
776  auto next_it = it;
777  ++next_it;
778 
779  if( next_it == m_wires.end() )
780  break;
781 
782  SCH_LINE* next_line = *next_it;
783 
784  if( line->IsParallel( next_line ) )
785  {
786  if( SCH_LINE* merged = line->MergeOverlap( next_line ) )
787  {
788  delete line;
789  delete next_line;
790  it = m_wires.erase( it );
791  *it = merged;
792  }
793  }
794 
795  ++it;
796  }
797 }
798 
799 
801 {
802  // Clear selection when done so that a new wire can be started.
803  // NOTE: this must be done before simplifyWireList is called or we might end up with
804  // freed selected items.
806 
807  PICKED_ITEMS_LIST itemList;
808 
809  // Remove segments backtracking over others
811 
812  // Collect the possible connection points for the new lines
813  std::vector< wxPoint > connections;
814  std::vector< wxPoint > new_ends;
815  m_frame->GetSchematicConnections( connections );
816 
817  // Check each new segment for possible junctions and add/split if needed
818  for( auto wire : m_wires )
819  {
820  if( wire->HasFlag( SKIP_STRUCT ) )
821  continue;
822 
823  wire->GetConnectionPoints( new_ends );
824 
825  for( auto i : connections )
826  {
827  if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), i ) )
828  new_ends.push_back( i );
829  }
830  itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) );
831  }
832 
834  {
835  wxASSERT( m_busUnfold.entry && m_busUnfold.label );
836 
837  itemList.PushItem( ITEM_PICKER( m_busUnfold.entry, UR_NEW ) );
838  itemList.PushItem( ITEM_PICKER( m_busUnfold.label, UR_NEW ) );
840  }
841 
842  // Get the last non-null wire (this is the last created segment).
843  if( !m_wires.empty() )
845 
846  // Add the new wires
847  for( auto wire : m_wires )
848  {
849  wire->ClearFlags( IS_NEW | IS_MOVED );
850  m_frame->AddToScreen( wire );
851  }
852 
853  m_wires.clear();
854  m_view->ClearPreview();
855  m_view->ShowPreview( false );
856 
857  getViewControls()->CaptureCursor( false );
858  getViewControls()->SetAutoPan( false );
859 
860  m_frame->SaveCopyInUndoList( itemList, UR_NEW );
861 
862  // Correct and remove segments that need to be merged.
864 
865  for( auto item : m_frame->GetScreen()->Items().OfType( SCH_COMPONENT_T ) )
866  {
867  std::vector< wxPoint > pts;
868  item->GetConnectionPoints( pts );
869 
870  if( pts.size() > 2 )
871  continue;
872 
873  for( auto i = pts.begin(); i != pts.end(); i++ )
874  {
875  for( auto j = i + 1; j != pts.end(); j++ )
876  m_frame->TrimWire( *i, *j );
877  }
878  }
879 
880  for( auto i : new_ends )
881  {
882  if( m_frame->GetScreen()->IsJunctionNeeded( i, true ) )
883  m_frame->AddJunction( i, true, false );
884  }
885 
887  m_busUnfold = {};
888 
891 
892  m_frame->OnModify();
893 }
894 
895 
897 {
898  EE_SELECTION* aSelection = aEvent.Parameter<EE_SELECTION*>();
899 
900  std::vector<wxPoint> pts;
901  std::vector<wxPoint> connections;
902 
903  m_frame->GetSchematicConnections( connections );
904 
905  for( unsigned ii = 0; ii < aSelection->GetSize(); ii++ )
906  {
907  SCH_ITEM* item = dynamic_cast<SCH_ITEM*>( aSelection->GetItem( ii ) );
908  std::vector<wxPoint> new_pts;
909 
910  if( !item || !item->IsConnectable() )
911  continue;
912 
913  item->GetConnectionPoints( new_pts );
914  pts.insert( pts.end(), new_pts.begin(), new_pts.end() );
915 
916  // If the item is a line, we also add any connection points from the rest of the schematic
917  // that terminate on the line after it is moved.
918  if( item->Type() == SCH_LINE_T )
919  {
920  SCH_LINE* line = (SCH_LINE*) item;
921  for( auto i : connections )
922  {
923  if( IsPointOnSegment( line->GetStartPoint(), line->GetEndPoint(), i ) )
924  pts.push_back( i );
925  }
926  }
927  else
928  {
929  // Clean up any wires that short non-wire connections in the list
930  for( auto point = new_pts.begin(); point != new_pts.end(); point++ )
931  {
932  for( auto second_point = point + 1; second_point != new_pts.end(); second_point++ )
933  m_frame->TrimWire( *point, *second_point );
934  }
935  }
936  }
937 
938  // We always have some overlapping connection points. Drop duplicates here
939  std::sort( pts.begin(), pts.end(), []( const wxPoint& a, const wxPoint& b ) -> bool {
940  return a.x < b.x || ( a.x == b.x && a.y < b.y );
941  } );
942 
943  pts.erase( unique( pts.begin(), pts.end() ), pts.end() );
944 
945  for( auto point : pts )
946  {
947  if( m_frame->GetScreen()->IsJunctionNeeded( point, true ) )
948  m_frame->AddJunction( point, true, false );
949  }
950 
951  return 0;
952 }
953 
954 
956 {
961 
963 }
virtual void ShowCursor(bool aEnabled)
Function ShowCursor() Enables or disables display of cursor.
SCH_LINE * MergeOverlap(SCH_LINE *aLine)
Check line against aLine to see if it overlaps and merge if it does.
Definition: sch_line.cpp:396
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 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:337
void AddToPreview(EDA_ITEM *aItem, bool aTakeOwnership=true)
Definition: sch_view.cpp:193
static bool IsDrawingWire(const SELECTION &aSelection)
void simplifyWireList()
Iterates over the wire list and removes the null segments and overlapping segments to create a simpli...
EDA_BASE_FRAME * GetEditFrame() const
Definition: tool_manager.h:298
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:97
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...
ACTION_MENU.
Definition: action_menu.h:43
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:335
const BITMAP_OPAQUE add_line2bus_xpm[1]
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)
std::vector< SCH_LINE * > m_wires
Storage for the line segments while drawing.
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
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
bool IsParallel(SCH_LINE *aLine)
Definition: sch_line.cpp:383
static TOOL_ACTION placeHierLabel
Definition: ee_actions.h:90
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
static TOOL_ACTION placeJunction
Definition: ee_actions.h:85
static TOOL_ACTION selectConnection
If current selection is a wire or bus, expand to entire connection.
Definition: ee_actions.h:55
EE_TYPE OfType(KICAD_T aType)
Definition: sch_rtree.h:219
EE_TYPE Overlapping(const EDA_RECT &aRect)
Definition: sch_rtree.h:224
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:347
TOOL_MENU & GetToolMenu()
bool IsPointOnSegment(const wxPoint &aSegStart, const wxPoint &aSegEnd, const wxPoint &aTestPoint)
Test if aTestPoint is on line defined by aSegStart and aSegEnd.
Definition: trigo.cpp:42
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:139
static bool IsDrawingLine(const SELECTION &aSelection)
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
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)
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:205
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:101
void SetTool(TOOL_INTERACTIVE *aTool)
Function SetTool() Sets a tool that is the creator of the menu.
bool IsNew() const
Definition: base_struct.h:220
const SCH_SHEET_PIN * getSheetPin(const wxPoint &aPosition)
Searches for a sheet pin at a location.
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 (.
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
wxPoint origin
Origin (on the bus) of the unfold.
void SetFlags(STATUS_FLAGS aMask)
Definition: base_struct.h:257
SCH_SHEET_PATH * g_CurrentSheet
With the new connectivity algorithm, many more places than before want to know what the current sheet...
VECTOR2< double > VECTOR2D
Definition: vector2d.h:593
int doDrawSegments(const std::string &aTool, int aType)
static TOOL_ACTION drawWire
Definition: ee_actions.h:81
void SetParent(EDA_ITEM *aParent)
Definition: base_struct.h:218
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:95
void ShowPreview(bool aShow=true)
Definition: sch_view.cpp:207
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:98
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
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:181
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)
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:153
virtual bool IsType(const KICAD_T aScanTypes[]) const
Function IsType Checks whether the item is one of the listed types.
Definition: base_struct.h:286
char GetBusEntryShape() const
SCH_LAYER_ID
Eeschema drawing layers.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:58
void SetBusEntryShape(char aShape)
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
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:104
wxPoint GetPosition() const override
Function GetPosition.
Definition: sch_text.h:317
void computeBreakPoint(const std::pair< SCH_LINE *, SCH_LINE * > &aSegments, wxPoint &aPosition)
Function ComputeBreakPoint computes the middle coordinate for 2 segments from the start point to aPos...
TOOL_INTERACTIVE * m_tool
Creator of the menu
Definition: action_menu.h:227
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 Note: the returned value can ...
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.
virtual unsigned int GetSize() const override
Function GetSize() Returns the number of stored items.
Definition: selection.h:99
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:77
bool IsTerminalPoint(const wxPoint &aPosition, int aLayer)
Test if aPosition is a connection point on aLayer.
Definition: sch_screen.cpp:419
void SetTitle(const wxString &aTitle) override
Function SetTitle() Sets title for the menu.
Definition: action_menu.cpp:86
Board layer functions and definitions.
int AddItemToSel(const TOOL_EVENT &aEvent)
const char * name
Definition: DXF_plotter.cpp:60
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:38
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).
#define _(s)
Definition: 3d_actions.cpp:33
ACTION_MENU * create() const override
Returns an instance of this class. It has to be overridden in inheriting classes.
EE_POINT_EDITOR.
TOOL_MANAGER * getToolManager() const
Returns an instance of TOOL_MANAGER class.
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:131
void AddSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Function CreateSubMenu.
Definition: tool_menu.cpp:52
EE_RTREE & Items()
Definition: sch_screen.h:127
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:414
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()
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.
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...
SCH_SHEET * g_RootSheet
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
virtual void SetLabelSpinStyle(LABEL_SPIN_STYLE aSpinStyle)
Set a spin or rotation angle, along with specific horizontal and vertical justification styles with e...
Definition: sch_text.cpp:206
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
bool IsBus() const
static TOOL_ACTION placeGlobalLabel
Definition: ee_actions.h:89
BASE_SCREEN class implementation.
void ClearEditFlags()
Definition: base_struct.h:275
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:318
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:267
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Function AddItem()
bool IsCurrentTool(const TOOL_ACTION &aAction) const
SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
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.
static TOOL_ACTION refreshPreview
Definition: actions.h:101
This item represents a bus vector.
VECTOR2D GetCursorPosition() const
Returns the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:183
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:212
wxPoint GetPosition() const override
Function GetPosition.
#define IS_MOVED
Item being moved.
Definition: base_struct.h:119
wxPoint GetEndPoint() const
Definition: sch_line.h:100