KiCad PCB EDA Suite
sch_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 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include <connection_graph.h>
25 #include <sch_wire_bus_tool.h>
26 #include <ee_selection_tool.h>
27 #include <ee_actions.h>
28 #include <sch_edit_frame.h>
29 #include <sch_view.h>
30 #include <class_draw_panel_gal.h>
31 #include <id.h>
32 #include <eeschema_id.h>
33 #include <confirm.h>
34 #include <view/view_group.h>
35 #include <view/view_controls.h>
36 #include <view/view.h>
37 #include <tool/tool_manager.h>
38 #include <ee_hotkeys.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 
46 TOOL_ACTION EE_ACTIONS::startWire( "eeschema.WireBusDrawing.startWire",
47  AS_GLOBAL, 0,
48  _( "Start Wire" ), _( "Start drawing a wire" ),
49  add_line_xpm, AF_ACTIVATE );
50 
51 TOOL_ACTION EE_ACTIONS::drawWire( "eeschema.WireBusDrawing.drawWires",
53  _( "Add Wire" ), _( "Add a wire" ),
54  add_line_xpm, AF_ACTIVATE );
55 
56 TOOL_ACTION EE_ACTIONS::startBus( "eeschema.WireBusDrawing.startBus",
57  AS_GLOBAL, 0,
58  _( "Start Bus" ), _( "Start drawing a bus" ),
59  add_bus_xpm, AF_ACTIVATE );
60 
61 TOOL_ACTION EE_ACTIONS::drawBus( "eeschema.WireBusDrawing.drawBusses",
63  _( "Add Bus" ), _( "Add a bus" ),
64  add_bus_xpm, AF_ACTIVATE );
65 
66 TOOL_ACTION EE_ACTIONS::unfoldBus( "eeschema.WireBusDrawing.unfoldBus",
68  _( "Unfold from Bus" ), _( "Break a wire out of a bus" ),
69  nullptr, AF_ACTIVATE );
70 
71 TOOL_ACTION EE_ACTIONS::startLines( "eeschema.WireBusDrawing.startLines",
72  AS_GLOBAL, 0, _( "Begin Lines" ), _( "Start drawing connected graphic lines" ),
73  add_line_xpm, AF_ACTIVATE );
74 
75 TOOL_ACTION EE_ACTIONS::drawLines( "eeschema.WireBusDrawing.drawLines",
77  _( "Add Lines" ), _( "Add connected graphic lines" ),
78  add_graphical_segments_xpm, AF_ACTIVATE );
79 
80 TOOL_ACTION EE_ACTIONS::finishLineWireOrBus( "eeschema.WireBusDrawing.finishLineWireOrBus",
82  _( "Finish Wire or Bus" ), _( "Complete drawing at current segment" ),
83  checked_ok_xpm, AF_NONE );
84 
85 TOOL_ACTION EE_ACTIONS::finishWire( "eeschema.WireBusDrawing.finishWire",
86  AS_GLOBAL, 0, _( "Finish Wire" ), _( "Complete wire with current segment" ),
87  checked_ok_xpm, AF_NONE );
88 
89 TOOL_ACTION EE_ACTIONS::finishBus( "eeschema.WireBusDrawing.finishBus",
90  AS_GLOBAL, 0, _( "Finish Bus" ), _( "Complete bus with current segment" ),
91  checked_ok_xpm, AF_NONE );
92 
93 TOOL_ACTION EE_ACTIONS::finishLine( "eeschema.WireBusDrawing.finishLine",
94  AS_GLOBAL, 0, _( "Finish Lines" ), _( "Complete connected lines with current segment" ),
95  checked_ok_xpm, AF_NONE );
96 
97 
99 {
100 public:
102  m_showTitle( false )
103  {
104  SetIcon( add_line2bus_xpm );
105  SetTitle( _( "Unfold from Bus" ) );
106  }
107 
109  {
110  m_showTitle = true;
111  }
112 
113 
114 protected:
115  ACTION_MENU* create() const override
116  {
117  return new BUS_UNFOLD_MENU();
118  }
119 
120 private:
121  void update() override
122  {
125  KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT };
126  SELECTION& selection = selTool->RequestSelection( busType );
127  SCH_LINE* bus = (SCH_LINE*) selection.Front();
128 
129  // TODO(JE) remove once real-time is enabled
130  if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity || !CONNECTION_GRAPH::m_allowRealTime )
131  {
132  frame->RecalculateConnections();
133 
134  // Have to pick up the pointer again because it may have been changed by SchematicCleanUp
135  selection = selTool->RequestSelection( busType );
136  bus = (SCH_LINE*) selection.Front();
137  }
138  if( !bus )
139  {
140  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "no bus selected" ), wxEmptyString );
141  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
142  return;
143  }
144 
145  SCH_CONNECTION* connection = bus->Connection( *g_CurrentSheet );
146 
147  if( !connection || !connection->IsBus() || connection->Members().empty() )
148  {
149  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "bus has no connections" ), wxEmptyString );
150  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
151  return;
152  }
153 
154  int idx = 0;
155 
156  if( m_showTitle )
157  {
158  Append( ID_POPUP_SCH_UNFOLD_BUS, _( "Unfold from Bus" ), wxEmptyString );
159  Enable( ID_POPUP_SCH_UNFOLD_BUS, false );
160  }
161 
162  for( const auto& member : connection->Members() )
163  {
164  int id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
165  wxString name = member->Name( true );
166 
167  if( member->Type() == CONNECTION_BUS )
168  {
169  wxMenu* submenu = new ACTION_MENU;
170  AppendSubMenu( submenu, name );
171 
172  for( const auto& sub_member : member->Members() )
173  {
174  id = ID_POPUP_SCH_UNFOLD_BUS + ( idx++ );
175  submenu->Append( id, sub_member->Name( true ), wxEmptyString );
176  }
177  }
178  else
179  {
180  Append( id, name, wxEmptyString );
181  }
182  }
183  }
184 
186 };
187 
188 
190  EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.WireBusDrawing" )
191 {
192  m_busUnfold = {};
193 }
194 
195 
197 {
198 }
199 
200 
202 {
204 
205  auto wireOrBusTool = [ this ] ( const SELECTION& aSel ) {
206  return ( m_frame->GetToolId() == ID_WIRE_TOOL || m_frame->GetToolId() == ID_BUS_TOOL );
207  };
208 
209  auto lineTool = [ this ] ( const SELECTION& aSel ) {
210  return ( m_frame->GetToolId() == ID_SCHEMATIC_LINE_TOOL );
211  };
212 
213  auto belowRootSheetCondition = [] ( const SELECTION& aSel ) {
214  return g_CurrentSheet->Last() != g_RootSheet;
215  };
216 
217  auto busSelection = EE_CONDITIONS::MoreThan( 0 )
219 
220  auto& ctxMenu = m_menu.GetMenu();
221 
222  //
223  // Build the tool menu
224  //
225  ctxMenu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 2 );
226 
227  ctxMenu.AddSeparator( EE_CONDITIONS::ShowAlways, 10 );
228  ctxMenu.AddItem( EE_ACTIONS::startWire, wireOrBusTool && EE_CONDITIONS::Idle, 10 );
229  ctxMenu.AddItem( EE_ACTIONS::startBus, wireOrBusTool && EE_CONDITIONS::Idle, 10 );
230  ctxMenu.AddItem( EE_ACTIONS::startLines, lineTool && EE_CONDITIONS::Idle, 10 );
231  ctxMenu.AddItem( EE_ACTIONS::finishWire, IsDrawingWire, 10 );
232  ctxMenu.AddItem( EE_ACTIONS::finishBus, IsDrawingBus, 10 );
233  ctxMenu.AddItem( EE_ACTIONS::finishLine, IsDrawingLine, 10 );
234 
235  std::shared_ptr<BUS_UNFOLD_MENU> busUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>();
236  busUnfoldMenu->SetTool( this );
237  m_menu.AddSubMenu( busUnfoldMenu );
238  ctxMenu.AddMenu( busUnfoldMenu.get(), EE_CONDITIONS::Idle, 10 );
239 
240  ctxMenu.AddSeparator( wireOrBusTool && EE_CONDITIONS::Idle, 100 );
241  ctxMenu.AddItem( EE_ACTIONS::addJunction, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
242  ctxMenu.AddItem( EE_ACTIONS::addLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
243  ctxMenu.AddItem( EE_ACTIONS::addGlobalLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
244  ctxMenu.AddItem( EE_ACTIONS::addHierLabel, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
245  ctxMenu.AddItem( EE_ACTIONS::breakWire, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
246  ctxMenu.AddItem( EE_ACTIONS::breakBus, wireOrBusTool && EE_CONDITIONS::Idle, 100 );
247 
248  ctxMenu.AddSeparator( wireOrBusTool && EE_CONDITIONS::Idle, 200 );
249  ctxMenu.AddItem( EE_ACTIONS::selectNode, wireOrBusTool && EE_CONDITIONS::Idle, 200 );
250  ctxMenu.AddItem( EE_ACTIONS::selectConnection, wireOrBusTool && EE_CONDITIONS::Idle, 200 );
251 
252  //
253  // Add bus unfolding to the selection tool
254  //
256 
257  std::shared_ptr<BUS_UNFOLD_MENU> selBusUnfoldMenu = std::make_shared<BUS_UNFOLD_MENU>();
258  selBusUnfoldMenu->SetTool( m_selectionTool );
259  m_selectionTool->GetToolMenu().AddSubMenu( selBusUnfoldMenu );
260  selToolMenu.AddMenu( selBusUnfoldMenu.get(), busSelection && EE_CONDITIONS::Idle, 100 );
261 
262  return true;
263 }
264 
265 
266 static bool isNewSegment( SCH_ITEM* aItem )
267 {
268  return aItem && aItem->IsNew() && aItem->Type() == SCH_LINE_T;
269 }
270 
271 
273 {
274  static KICAD_T graphicLineType[] = { SCH_LINE_LOCATE_GRAPHIC_LINE_T, EOT };
275  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( graphicLineType );
276 }
277 
278 
280 {
281  static KICAD_T wireType[] = { SCH_LINE_LOCATE_WIRE_T, EOT };
282  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( wireType );
283 }
284 
285 
287 {
288  static KICAD_T busType[] = { SCH_LINE_LOCATE_BUS_T, EOT };
289  return IsDrawingLineWireOrBus( aSelection ) && aSelection.Front()->IsType( busType );
290 }
291 
292 
294 {
295  // NOTE: for immediate hotkeys, it is NOT required that the line, wire or bus tool
296  // be selected
297  SCH_ITEM* item = (SCH_ITEM*) aSelection.Front();
298  return isNewSegment( item );
299 }
300 
301 
302 /*
303  * Immediate action: start drawing a wire. Does not select the wire tool.
304  */
306 {
308 
309  Activate();
310 
313  return doDrawSegments( LAYER_WIRE, segment );
314 }
315 
316 
317 /*
318  * Tool action: first call selects the tool; subsequent calls start wires.
319  */
321 {
322  if( m_frame->GetToolId() == ID_WIRE_TOOL )
323  return StartWire( aEvent );
324  else
325  {
326  m_frame->SetToolID( ID_WIRE_TOOL, wxCURSOR_PENCIL, _( "Add wire" ) );
328 
329  return doDrawSegments( LAYER_WIRE, nullptr );
330  }
331 }
332 
333 
334 /*
335  * Immediate action: start drawing a bus. Does not select the bus tool.
336  */
338 {
340 
341  Activate();
342 
345  return doDrawSegments( LAYER_BUS, segment );
346 }
347 
348 
349 /*
350  * Tool action: first call selects the tool; subsequent calls start busses.
351  */
353 {
354  if( m_frame->GetToolId() == ID_BUS_TOOL )
355  return StartBus( aEvent );
356  else
357  {
358  m_frame->SetToolID( ID_BUS_TOOL, wxCURSOR_PENCIL, _( "Add bus" ) );
360 
361  return doDrawSegments( LAYER_BUS, nullptr );
362  }
363 }
364 
365 
367 {
368  wxString* netPtr = aEvent.Parameter<wxString*>();
369  wxString net;
370  SCH_LINE* segment = nullptr;
371 
373 
374  Activate();
375 
376  if( netPtr )
377  {
378  net = *netPtr;
379  delete netPtr;
380  }
381  else
382  {
383  BUS_UNFOLD_MENU unfoldMenu;
384  unfoldMenu.SetTool( this );
385  unfoldMenu.SetShowTitle();
386 
387  SetContextMenu( &unfoldMenu, CMENU_NOW );
388 
389  while( OPT_TOOL_EVENT evt = Wait() )
390  {
391  if( evt->Action() == TA_CONTEXT_MENU_CHOICE )
392  {
393  OPT<int> id = evt->GetCommandId();
394 
395  if( id && ( *id > 0 ) )
396  net = *evt->Parameter<wxString*>();
397 
398  break;
399  }
400  }
401  }
402 
403  if( !net.IsEmpty() )
404  {
405  // Break a wire for the given net out of the bus
406  segment = doUnfoldBus( net );
407  }
408 
409  // If we have an unfolded wire to draw, then draw it
410  if( segment )
411  return doDrawSegments( LAYER_WIRE, segment );
412 
413  // If we came from one of our tools then re-enter it in the idle state
414  if( m_frame->GetToolId() == ID_WIRE_TOOL )
415  return doDrawSegments( LAYER_WIRE, nullptr );
416  else if( m_frame->GetToolId() == ID_BUS_TOOL )
417  return doDrawSegments( LAYER_BUS, nullptr );
418 
420  return 0;
421 }
422 
423 
424 SCH_LINE* SCH_WIRE_BUS_TOOL::doUnfoldBus( const wxString& aNet )
425 {
426  wxPoint pos = m_frame->GetCrossHairPosition();
427 
428  m_busUnfold.entry = new SCH_BUS_WIRE_ENTRY( pos, '\\' );
431 
432  m_busUnfold.label = new SCH_LABEL( m_busUnfold.entry->m_End(), aNet );
436 
437  m_busUnfold.in_progress = true;
438  m_busUnfold.origin = pos;
439  m_busUnfold.net_name = aNet;
440 
442 
444 }
445 
446 
447 /*
448  * Immediate action: start drawing a line. Does not select the line tool.
449  */
451 {
453 
456  return doDrawSegments( LAYER_BUS, segment );
457 }
458 
459 
460 /*
461  * Tool action: first call selects the tool; subsequent calls start lines.
462  */
464 {
466  return StartLine( aEvent );
467  else
468  {
469  m_frame->SetToolID( ID_SCHEMATIC_LINE_TOOL, wxCURSOR_PENCIL, _( "Add lines" ) );
471 
472  return doDrawSegments( LAYER_NOTES, nullptr );
473  }
474 }
475 
476 
477 // Storage for the line segments while drawing
479 
480 
484 static const SCH_SHEET_PIN* getSheetPin( SCH_SCREEN* aScreen, const wxPoint& aPosition )
485 {
486  for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
487  {
488  if( item->Type() == SCH_SHEET_T )
489  {
490  SCH_SHEET* sheet = (SCH_SHEET*) item;
491 
492  for( const SCH_SHEET_PIN& pin : sheet->GetPins() )
493  {
494  if( pin.GetPosition() == aPosition )
495  return &pin;
496  }
497  }
498  }
499 
500  return nullptr;
501 }
502 
503 
514 static void computeBreakPoint( SCH_SCREEN* aScreen, SCH_LINE* aSegment, wxPoint& aPosition )
515 {
516  wxCHECK_RET( aSegment != nullptr, wxT( "Cannot compute break point of NULL line segment." ) );
517 
518  SCH_LINE* nextSegment = aSegment->Next();
519 
520  wxPoint midPoint;
521  int iDx = aSegment->GetEndPoint().x - aSegment->GetStartPoint().x;
522  int iDy = aSegment->GetEndPoint().y - aSegment->GetStartPoint().y;
523 
524  const SCH_SHEET_PIN* connectedPin = getSheetPin( aScreen, aSegment->GetStartPoint() );
525  auto force = connectedPin ? connectedPin->GetEdge() : SCH_SHEET_PIN::SHEET_UNDEFINED_SIDE;
526 
528  {
529  if( aPosition.x == connectedPin->GetPosition().x ) // push outside sheet boundary
530  {
531  int direction = ( force == SCH_SHEET_PIN::SHEET_LEFT_SIDE ) ? -1 : 1;
532  aPosition.x += int( aScreen->GetGridSize().x * direction );
533  }
534 
535  midPoint.x = aPosition.x;
536  midPoint.y = aSegment->GetStartPoint().y; // force horizontal
537  }
538  else if( iDy != 0 ) // keep the first segment orientation (vertical)
539  {
540  midPoint.x = aSegment->GetStartPoint().x;
541  midPoint.y = aPosition.y;
542  }
543  else if( iDx != 0 ) // keep the first segment orientation (horizontal)
544  {
545  midPoint.x = aPosition.x;
546  midPoint.y = aSegment->GetStartPoint().y;
547  }
548  else
549  {
550  if( std::abs( aPosition.x - aSegment->GetStartPoint().x ) <
551  std::abs( aPosition.y - aSegment->GetStartPoint().y ) )
552  {
553  midPoint.x = aSegment->GetStartPoint().x;
554  midPoint.y = aPosition.y;
555  }
556  else
557  {
558  midPoint.x = aPosition.x;
559  midPoint.y = aSegment->GetStartPoint().y;
560  }
561  }
562 
563  aSegment->SetEndPoint( midPoint );
564  nextSegment->SetStartPoint( midPoint );
565  nextSegment->SetEndPoint( aPosition );
566 }
567 
568 
569 int SCH_WIRE_BUS_TOOL::doDrawSegments( int aType, SCH_LINE* aSegment )
570 {
571  bool forceHV = m_frame->GetForceHVLines();
572  SCH_SCREEN* screen = m_frame->GetScreen();
573  wxPoint cursorPos;
574 
575  getViewControls()->ShowCursor( true );
576 
577  if( aSegment == nullptr )
578  Activate();
579 
580  // Main loop: keep receiving events
581  while( OPT_TOOL_EVENT evt = Wait() )
582  {
583  cursorPos = (wxPoint)getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) );
584 
585  //------------------------------------------------------------------------
586  // Handle cancel:
587  //
588  if( TOOL_EVT_UTILS::IsCancelInteractive( evt.get() ) )
589  {
590  if( aSegment || m_busUnfold.in_progress )
591  {
593 
594  aSegment = nullptr;
595  s_wires.DeleteAll();
596 
597  if( m_busUnfold.entry )
599 
602 
603  delete m_busUnfold.entry;
604  delete m_busUnfold.label;
605  m_busUnfold = {};
606 
607  m_view->ClearPreview();
608  m_view->ShowPreview( false );
609 
610  if( !evt->IsActivate() )
611  continue;
612  }
613 
614  if( ( evt->IsAction( &EE_ACTIONS::drawWire ) && aType == LAYER_WIRE )
615  || ( evt->IsAction( &EE_ACTIONS::drawBus ) && aType == LAYER_BUS )
616  || ( evt->IsAction( &EE_ACTIONS::unfoldBus ) && aType == LAYER_WIRE ) )
617  {
618  // Don't reset tool. If we do the next command will think it needs to
619  // re-select the tool rather than start a wire or bus.
620  }
621  else
622  {
624  }
625 
626  break;
627  }
628  //------------------------------------------------------------------------
629  // Handle finish:
630  //
631  else if( evt->IsAction( &EE_ACTIONS::finishLineWireOrBus )
632  || evt->IsAction( &EE_ACTIONS::finishWire )
633  || evt->IsAction( &EE_ACTIONS::finishBus )
634  || evt->IsAction( &EE_ACTIONS::finishLine ) )
635  {
636  if( aSegment || m_busUnfold.in_progress )
637  {
638  finishSegments();
639  aSegment = nullptr;
640  }
641 
643  break;
644  }
645  //------------------------------------------------------------------------
646  // Handle click:
647  //
648  else if( evt->IsClick( BUT_LEFT ) || ( aSegment && evt->IsDblClick( BUT_LEFT ) ) )
649  {
650  // First click when unfolding places the label and wire-to-bus entry
652  {
653  wxASSERT( aType == LAYER_WIRE );
654 
656  m_busUnfold.label_placed = true;
657  }
658 
659  if( !aSegment )
660  {
661  aSegment = startSegments( aType, cursorPos );
662  }
663  // Create a new segment if we're out of previously-created ones
664  else if( !aSegment->IsNull() || ( forceHV && !aSegment->Back()->IsNull() ) )
665  {
666  // Terminate the command if the end point is on a pin, junction, or another
667  // wire or bus.
669  && screen->IsTerminalPoint( cursorPos, aSegment->GetLayer() ) )
670  {
671  finishSegments();
672  aSegment = nullptr;
673 
675  break;
676  }
677  else
678  {
679  aSegment->SetEndPoint( cursorPos );
680 
681  // Create a new segment, and chain it after the current segment.
682  aSegment = new SCH_LINE( *aSegment );
683  aSegment->SetFlags( IS_NEW | IS_MOVED );
684  aSegment->SetStartPoint( cursorPos );
685  s_wires.PushBack( aSegment );
686 
687  m_selectionTool->AddItemToSel( aSegment, true /*quiet mode*/ );
688  }
689  }
690 
691  if( evt->IsDblClick( BUT_LEFT ) && aSegment )
692  {
693  if( forceHV )
694  computeBreakPoint( screen, aSegment->Back(), cursorPos );
695 
696  finishSegments();
697  aSegment = nullptr;
698 
700  break;
701  }
702  }
703  //------------------------------------------------------------------------
704  // Handle motion:
705  //
706  else if( evt->IsMotion() )
707  {
708  m_view->ClearPreview();
709 
710  // Update the bus unfold posture based on the mouse movement
712  {
713  wxPoint cursor_delta = cursorPos - m_busUnfold.origin;
715 
716  bool offset = ( cursor_delta.x < 0 );
717  char shape = ( offset ? ( ( cursor_delta.y >= 0 ) ? '/' : '\\' )
718  : ( ( cursor_delta.y >= 0 ) ? '\\' : '/' ) );
719 
720  // Erase and redraw if necessary
721  if( shape != entry->GetBusEntryShape() || offset != m_busUnfold.offset )
722  {
723  entry->SetBusEntryShape( shape );
724  wxPoint entry_pos = m_busUnfold.origin;
725 
726  if( offset )
727  entry_pos -= entry->GetSize();
728 
729  entry->SetPosition( entry_pos );
730  m_busUnfold.offset = offset;
731 
732  m_frame->RefreshItem( entry );
733 
734  wxPoint wire_start = offset ? entry->GetPosition() : entry->m_End();
735  s_wires.begin()->SetStartPoint( wire_start );
736  }
737 
738  // Update the label "ghost" position
739  m_busUnfold.label->SetPosition( cursorPos );
741  }
742 
743  if( aSegment )
744  {
745  // Coerce the line to vertical or horizontal if necessary
746  if( forceHV )
747  computeBreakPoint( screen, aSegment->Back(), cursorPos );
748  else
749  aSegment->SetEndPoint( cursorPos );
750  }
751 
752  for( auto seg = s_wires.begin(); seg; seg = seg->Next() )
753  {
754  if( !seg->IsNull() ) // Add to preview if segment length != 0
755  m_view->AddToPreview( seg->Clone() );
756  }
757  }
758  //------------------------------------------------------------------------
759  // Handle context menu:
760  //
761  else if( evt->IsClick( BUT_RIGHT ) )
762  {
763  // Warp after context menu only if dragging...
764  if( !aSegment )
766 
768  }
769  else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CONTEXT_MENU_CHOICE )
770  {
771  if( evt->GetCommandId().get() >= ID_POPUP_SCH_UNFOLD_BUS
772  && evt->GetCommandId().get() <= ID_POPUP_SCH_UNFOLD_BUS_END )
773  {
774  wxASSERT_MSG( !aSegment, "Bus unfold event recieved when already drawing!" );
775 
776  aType = LAYER_WIRE;
777  wxString net = *evt->Parameter<wxString*>();
778  aSegment = doUnfoldBus( net );
779  }
780  }
781 
782  // Enable autopanning and cursor capture only when there is a segment to be placed
783  getViewControls()->SetAutoPan( !!aSegment );
784  getViewControls()->CaptureCursor( !!aSegment );
785  }
786 
787  return 0;
788 }
789 
790 
791 SCH_LINE* SCH_WIRE_BUS_TOOL::startSegments( int aType, const wxPoint& aPos )
792 {
793  SCH_LINE* segment = nullptr;
794  bool forceHV = m_frame->GetForceHVLines();
795 
796  switch( aType )
797  {
798  default: segment = new SCH_LINE( aPos, LAYER_NOTES ); break;
799  case LAYER_WIRE: segment = new SCH_LINE( aPos, LAYER_WIRE ); break;
800  case LAYER_BUS: segment = new SCH_LINE( aPos, LAYER_BUS ); break;
801  }
802 
803  segment->SetFlags( IS_NEW | IS_MOVED );
804  s_wires.PushBack( segment );
805 
806  m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
807 
808  // We need 2 segments to go from a given start pin to an end point when the
809  // horizontal and vertical lines only switch is on.
810  if( forceHV )
811  {
812  segment = new SCH_LINE( *segment );
813  segment->SetFlags( IS_NEW | IS_MOVED );
814  s_wires.PushBack( segment );
815 
816  m_selectionTool->AddItemToSel( segment, true /*quiet mode*/ );
817  }
818 
819  return segment;
820 }
821 
822 
836 static void removeBacktracks( DLIST<SCH_LINE>& aWires )
837 {
838  SCH_LINE* next = nullptr;
839  std::vector<SCH_LINE*> last_lines;
840 
841  for( SCH_LINE* line = aWires.GetFirst(); line; line = next )
842  {
843  next = line->Next();
844 
845  if( line->IsNull() )
846  {
847  delete s_wires.Remove( line );
848  continue;
849  }
850 
851  if( !last_lines.empty() )
852  {
853  SCH_LINE* last_line = last_lines[last_lines.size() - 1];
854  bool contiguous = ( last_line->GetEndPoint() == line->GetStartPoint() );
855  bool backtracks = IsPointOnSegment( last_line->GetStartPoint(),
856  last_line->GetEndPoint(), line->GetEndPoint() );
857  bool total_backtrack = ( last_line->GetStartPoint() == line->GetEndPoint() );
858 
859  if( contiguous && backtracks )
860  {
861  if( total_backtrack )
862  {
863  delete s_wires.Remove( last_line );
864  delete s_wires.Remove( line );
865  last_lines.pop_back();
866  }
867  else
868  {
869  last_line->SetEndPoint( line->GetEndPoint() );
870  delete s_wires.Remove( line );
871  }
872  }
873  else
874  {
875  last_lines.push_back( line );
876  }
877  }
878  else
879  {
880  last_lines.push_back( line );
881  }
882  }
883 }
884 
885 
887 {
888  // Clear selection when done so that a new wire can be started.
889  // NOTE: this must be done before RemoveBacktracks is called or we might end up with
890  // freed selected items.
892 
893  PICKED_ITEMS_LIST itemList;
894 
895  // Remove segments backtracking over others
897 
898  // Collect the possible connection points for the new lines
899  std::vector< wxPoint > connections;
900  std::vector< wxPoint > new_ends;
901  m_frame->GetSchematicConnections( connections );
902 
903  // Check each new segment for possible junctions and add/split if needed
904  for( SCH_LINE* wire = s_wires.GetFirst(); wire; wire = wire->Next() )
905  {
906  if( wire->GetFlags() & SKIP_STRUCT )
907  continue;
908 
909  wire->GetConnectionPoints( new_ends );
910 
911  for( auto i : connections )
912  {
913  if( IsPointOnSegment( wire->GetStartPoint(), wire->GetEndPoint(), i ) )
914  new_ends.push_back( i );
915  }
916  itemList.PushItem( ITEM_PICKER( wire, UR_NEW ) );
917  }
918 
920  {
921  wxASSERT( m_busUnfold.entry && m_busUnfold.label );
922 
923  itemList.PushItem( ITEM_PICKER( m_busUnfold.entry, UR_NEW ) );
924  itemList.PushItem( ITEM_PICKER( m_busUnfold.label, UR_NEW ) );
925  }
926 
927  // Get the last non-null wire (this is the last created segment).
929 
930  // Add the new wires
931  while( s_wires.GetFirst() )
932  {
933  s_wires.GetFirst()->ClearFlags( IS_NEW | IS_MOVED );
934  m_frame->AddToScreen( s_wires.PopFront() );
935  }
936 
937  m_view->ClearPreview();
938  m_view->ShowPreview( false );
939 
940  getViewControls()->CaptureCursor( false );
941  getViewControls()->SetAutoPan( false );
942 
943  m_frame->SaveCopyInUndoList( itemList, UR_NEW );
944 
945  // Correct and remove segments that need to be merged.
947 
948  for( auto item = m_frame->GetScreen()->GetDrawItems(); item; item = item->Next() )
949  {
950  if( item->Type() != SCH_COMPONENT_T )
951  continue;
952 
953  std::vector< wxPoint > pts;
954  item->GetConnectionPoints( pts );
955 
956  if( pts.size() > 2 )
957  continue;
958 
959  for( auto i = pts.begin(); i != pts.end(); i++ )
960  {
961  for( auto j = i + 1; j != pts.end(); j++ )
962  m_frame->TrimWire( *i, *j );
963  }
964  }
965 
966  for( auto i : new_ends )
967  {
968  if( m_frame->GetScreen()->IsJunctionNeeded( i, true ) )
969  m_frame->AddJunction( i, true, false );
970  }
971 
973  m_busUnfold = {};
974 
977 
978  m_frame->OnModify();
979 }
980 
981 
983 {
987 
991 
993 }
virtual void ShowCursor(bool aEnabled)
Function ShowCursor() Enables or disables display of cursor.
CITER next(CITER it)
Definition: ptree.cpp:130
static bool ShowAlways(const SELECTION &aSelection)
Function ShowAlways The default condition function (always returns true).
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Function AddMenu()
const wxRealPoint & GetGridSize() const
Return the grid size of the currently selected grid.
Definition: base_screen.h:410
bool IsPointOnSegment(const wxPoint &aSegStart, const wxPoint &aSegEnd, const wxPoint &aTestPoint)
Function IsPointOnSegment.
Definition: trigo.cpp:39
bool SchematicCleanUp(SCH_SCREEN *aScreen=nullptr)
Performs routine schematic cleaning including breaking wire and buses and deleting identical objects ...
void AddToPreview(EDA_ITEM *aItem, bool aTakeOwnership=true)
Definition: sch_view.cpp:145
SCH_SHEET_PINS & GetPins()
Definition: sch_sheet.h:322
static TOOL_ACTION addHierLabel
Definition: ee_actions.h:135
static TOOL_ACTION breakBus
Definition: ee_actions.h:143
void GetSchematicConnections(std::vector< wxPoint > &aConnections)
Collects a unique list of all possible connection points in the schematic.
wxPoint GetStartPoint() const
Definition: sch_line.h:90
int UnfoldBus(const TOOL_EVENT &aEvent)
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...
void setTransitions() override
Sets up handlers for various events.
TOOL_MENU & GetToolMenu()
Class ACTION_MENU.
Definition: action_menu.h:43
This file is part of the common library.
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:350
SCH_ITEM * Next() const
Definition: sch_item.h:153
static bool isNewSegment(SCH_ITEM *aItem)
static SELECTION_CONDITION MoreThan(int aNumber)
Function MoreThan Creates a functor that tests if the number of selected items is greater than the va...
VIEW_CONTROLS class definition.
virtual void MoveCursorToCrossHair() override
Function MoveCursorToCrossHair warps the cursor to the current cross hair position.
SCH_LINE * startSegments(int aType, const wxPoint &aPos)
#define SKIP_STRUCT
flag indicating that the structure should be ignored
Definition: base_struct.h:125
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:248
CONDITIONAL_MENU & GetMenu()
Function GetMenu.
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
SCH_LINE * doUnfoldBus(const wxString &aNet)
static TOOL_ACTION selectConnection
If current selection is a wire or bus, expand to entire connection.
Definition: ee_actions.h:54
int DrawWires(const TOOL_EVENT &aEvent)
void SetPosition(const wxPoint &aPosition) override
Function SetPosition set the schematic item position to aPosition.
int StartBus(const TOOL_EVENT &aEvent)
EDA_DRAW_FRAME * GetEditFrame() const
Definition: tool_manager.h:267
static TOOL_ACTION unfoldBus
Definition: ee_actions.h:84
static TOOL_ACTION finishLineWireOrBus
Definition: ee_actions.h:99
OPT_TOOL_EVENT Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Function Wait()
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:125
static int LegacyHotKey(int aHotKey)
Creates a hot key code that refers to a legacy hot key setting, instead of a particular key.
Definition: tool_action.h:165
virtual bool IsType(const KICAD_T aScanTypes[])
Function IsType Checks whether the item is one of the listed types.
Definition: base_struct.h:287
void PushItem(const ITEM_PICKER &aItem)
Function PushItem pushes aItem to the top of the list.
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:231
int DrawBusses(const TOOL_EVENT &aEvent)
Class DLIST is the head of a doubly linked list.
Definition: dlist.h:142
static SELECTION_CONDITION Idle
ACTION_MENU()
Default constructor
Definition: action_menu.cpp:40
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
Function SetContextMenu()
static bool IsDrawingLine(const SELECTION &aSelection)
void update() override
Update menu state stub.
#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:136
static TOOL_ACTION breakWire
Definition: ee_actions.h:142
static TOOL_ACTION finishLine
Definition: ee_actions.h:102
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:94
void SetTool(TOOL_INTERACTIVE *aTool)
Function SetTool() Sets a tool that is the creator of the menu.
bool IsNew() const
Definition: base_struct.h:222
static TOOL_ACTION leaveSheet
Definition: ee_actions.h:161
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:69
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...
#define IS_NEW
New item, just created.
Definition: base_struct.h:114
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
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:678
int StartLine(const TOOL_EVENT &aEvent)
wxPoint origin
Origin (on the bus) of the unfold.
void SetFlags(STATUS_FLAGS aMask)
Definition: base_struct.h:259
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:56
SCH_SHEET_PATH * g_CurrentSheet
With the new connectivity algorithm, many more places than before want to know what the current sheet...
static TOOL_ACTION startLines
Definition: ee_actions.h:96
static TOOL_ACTION drawWire
Definition: ee_actions.h:81
void SetParent(EDA_ITEM *aParent)
Definition: base_struct.h:219
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:88
void ShowPreview(bool aShow=true)
Definition: sch_view.cpp:173
T Parameter() const
Function Parameter() Returns a non-standard parameter assigned to the event.
Definition: tool_event.h:378
void SetStartPoint(const wxPoint &aPosition)
Definition: sch_line.h:91
SCH_DRAW_PANEL * GetCanvas() const override
void SetIcon(const BITMAP_OPAQUE *aIcon)
Function SetIcon() Assigns an icon for the entry.
Definition: action_menu.cpp:82
Class TOOL_EVENT.
Definition: tool_event.h:167
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:133
static TOOL_ACTION addJunction
Definition: ee_actions.h:132
void SaveCopyForRepeatItem(SCH_ITEM *aItem)
Clone aItem and owns that clone in this container.
static const SCH_SHEET_PIN * getSheetPin(SCH_SCREEN *aScreen, const wxPoint &aPosition)
A helper function to find any sheet pins at the specified position.
SCH_JUNCTION * AddJunction(const wxPoint &aPos, bool aAppendToUndo=false, bool aFinal=true)
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
VIEW_GROUP extends VIEW_ITEM by possibility of grouping items into a single object.
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:143
char GetBusEntryShape() const
function GetBusEntryShape
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:56
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:57
static bool IsDrawingBus(const SELECTION &aSelection)
void SetBusEntryShape(char aShape)
function SetBusEntryShape
virtual void SetToolID(int aId, int aCursor, const wxString &aToolMsg)
Set the tool command ID to aId and sets the cursor to aCursor.
wxPoint m_End() const
Class PICKED_ITEMS_LIST is a holder to handle information on schematic or board items.
bool IsCancelInteractive(const TOOL_EVENT &aEvt)
Function IsCancelInteractive()
Definition: tool_event.cpp:177
wxString net_name
Net label for the unfolding operation.
All active tools
Definition: tool_event.h:143
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:190
TOOL_MENU m_menu
Menu model displayed by the tool.
Definition: ee_tool_base.h:147
static DLIST< SCH_LINE > s_wires
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:127
static TOOL_ACTION drawBus
Definition: ee_actions.h:83
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:201
SCH_LAYER_ID GetLayer() const
Function GetLayer returns the layer this item is on.
Definition: sch_item.h:193
static bool m_allowRealTime
virtual void SetNoToolSelected()
Select the ID_NO_TOOL_SELECTED id tool (Idle tool)
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:430
void SetTitle(const wxString &aTitle) override
Function SetTitle() Sets title for the menu.
Definition: action_menu.cpp:96
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.
TOOL_MANAGER * getToolManager() const
Returns an instance of TOOL_MANAGER class.
static void removeBacktracks(DLIST< SCH_LINE > &aWires)
In a contiguous list of wires, remove wires that backtrack over the previous wire.
void AddSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Function CreateSubMenu.
Definition: tool_menu.cpp:52
int doDrawSegments(int aType, SCH_LINE *aSegment)
Class TOOL_ACTION.
Definition: tool_action.h:46
size_t i
Definition: json11.cpp:597
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:385
BUS_UNFOLDING_T m_busUnfold
Data related to bus unfolding tool.
static TOOL_ACTION selectNode
Select the junction, wire or bus segment under the cursor.
Definition: ee_actions.h:50
static TOOL_ACTION startWire
Definition: ee_actions.h:80
int DrawLines(const TOOL_EVENT &aEvent)
SHEET_SIDE GetEdge() const
boost::optional< T > OPT
Definition: optional.h:7
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
Class for a wire to bus entry.
void Activate()
Function Activate() Runs the tool.
static TOOL_ACTION startBus
Definition: ee_actions.h:82
bool TrimWire(const wxPoint &aStart, const wxPoint &aEnd)
If any single wire passes through both points, remove the portion between the two points,...
static TOOL_ACTION addLabel
Definition: ee_actions.h:133
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:49
static TOOL_ACTION drawLines
Definition: ee_actions.h:97
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
static TOOL_ACTION addGlobalLabel
Definition: ee_actions.h:134
bool label_placed
True if user has placed the net label.
int StartWire(const TOOL_EVENT &aEvent)
KIGFX::VIEW_CONTROLS * getViewControls() const
Function getViewControls()
Definition: tool_base.cpp:41
void SetCrossHairPosition(const wxPoint &aPosition, bool aSnapToGrid=true)
Set the screen cross hair position to aPosition in logical (drawing) units.
bool IsBus() const
static TOOL_ACTION finishBus
Definition: ee_actions.h:101
bool Init() override
Function Init() Init() is called once upon a registration of the tool.
void SetPosition(const wxPoint &aPosition) override
Function SetPosition set the schematic item position to aPosition.
Definition: sch_text.h:191
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:238
static bool IsDrawingWire(const SELECTION &aSelection)
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Function AddItem()
SELECTION & RequestSelection(const KICAD_T *aFilterList=EE_COLLECTOR::AllItems)
Function RequestSelection()
Class SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container clas...
Definition: sch_item.h:114
Implementation of the label properties dialog.
void ShowContextMenu(SELECTION &aSelection)
Function ShowContextMenu.
Definition: tool_menu.cpp:59
void RecalculateConnections(bool aDoCleanup=true)
Generates the connection data for the entire schematic hierarchy.
static TOOL_ACTION finishWire
Definition: ee_actions.h:100
int GetToolId() const
Definition: draw_frame.h:524
VECTOR2D GetCursorPosition() const
Returns the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:155
int GetDefaultTextSize()
Default size for text in general.
bool in_progress
True if bus unfold operation is running.
wxPoint GetCrossHairPosition(bool aInvertY=false) const
Return the current cross hair position in logical (drawing) coordinates.
OPT< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:486
KICAD_T Type() const
Function Type()
Definition: base_struct.h:204
This item represents a bus vector.
wxPoint GetPosition() const override
Function GetPosition.
#define IS_MOVED
Item being moved.
Definition: base_struct.h:113
SCH_ITEM * GetDrawItems() const
Definition: sch_screen.h:153
wxPoint GetEndPoint() const
Definition: sch_line.h:93