KiCad PCB EDA Suite
sch_move_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-2021 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 <tool/tool_manager.h>
26 #include <tools/ee_grid_helper.h>
29 #include <ee_actions.h>
30 #include <bitmaps.h>
31 #include <eda_item.h>
32 #include <sch_item.h>
33 #include <sch_symbol.h>
34 #include <sch_sheet.h>
35 #include <sch_sheet_pin.h>
36 #include <sch_line.h>
37 #include <sch_edit_frame.h>
38 #include <eeschema_id.h>
39 #include <pgm_base.h>
41 #include "sch_move_tool.h"
42 
43 
44 // For adding to or removing from selections
45 #define QUIET_MODE true
46 
47 
49  EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveMove" ),
50  m_moveInProgress( false ),
51  m_isDrag( false ),
52  m_moveOffset( 0, 0 )
53 {
54 }
55 
56 
58 {
60 
61  auto moveCondition =
62  []( const SELECTION& aSel )
63  {
64  if( aSel.Empty() || SELECTION_CONDITIONS::OnlyType( SCH_MARKER_T )( aSel ) )
65  return false;
66 
68  return false;
69 
70  return true;
71  };
72 
73  // Add move actions to the selection tool menu
74  //
76 
77  selToolMenu.AddItem( EE_ACTIONS::move, moveCondition, 150 );
78  selToolMenu.AddItem( EE_ACTIONS::drag, moveCondition, 150 );
79  selToolMenu.AddItem( EE_ACTIONS::alignToGrid, moveCondition, 150 );
80 
81  return true;
82 }
83 
84 
85 int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
86 {
87  EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
90  bool wasDragging = m_moveInProgress && m_isDrag;
92 
93  m_anchorPos.reset();
94 
95  if( aEvent.IsAction( &EE_ACTIONS::move ) )
96  m_isDrag = false;
97  else if( aEvent.IsAction( &EE_ACTIONS::drag ) )
98  m_isDrag = true;
99  else if( aEvent.IsAction( &EE_ACTIONS::moveActivate ) )
100  m_isDrag = !cfg->m_Input.drag_is_move;
101  else
102  return 0;
103 
104  if( m_moveInProgress )
105  {
106  if( m_isDrag != wasDragging )
107  {
109 
110  if( sel && !sel->IsNew() )
111  {
112  // Reset the selected items so we can start again with the current m_isDrag
113  // state.
117  m_moveInProgress = false;
118  controls->SetAutoPan( false );
119 
120  // And give it a kick so it doesn't have to wait for the first mouse movement
121  // to refresh.
123  }
124  }
125 
126  return 0;
127  }
128 
129  // Be sure that there is at least one item that we can move. If there's no selection try
130  // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
132  bool unselect = selection.IsHover();
133 
134  // Keep an original copy of the starting points for cleanup after the move
135  std::vector<DANGLING_END_ITEM> internalPoints;
136 
137  Activate();
138  // Must be done after Activate() so that it gets set into the correct context
139  controls->ShowCursor( true );
140 
141  std::string tool = aEvent.GetCommandStr().get();
142  m_frame->PushTool( tool );
143 
144  if( selection.Empty() )
145  {
146  // Note that it's important to go through push/pop even when the selection is empty.
147  // This keeps other tools from having to special-case an empty move.
148  m_frame->PopTool( tool );
149  return 0;
150  }
151 
152  bool restore_state = false;
153  bool chain_commands = false;
154  TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
155  VECTOR2I prevPos;
156  int snapLayer = UNDEFINED_LAYER;
157 
158  m_cursor = controls->GetCursorPosition();
159 
160  // Main loop: keep receiving events
161  do
162  {
164  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
165  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
166 
169  || evt->IsAction( &EE_ACTIONS::move )
170  || evt->IsAction( &EE_ACTIONS::drag )
171  || evt->IsMotion()
172  || evt->IsDrag( BUT_LEFT )
173  || evt->IsAction( &ACTIONS::refreshPreview ) )
174  {
175  if( !m_moveInProgress ) // Prepare to start moving/dragging
176  {
177  SCH_ITEM* sch_item = (SCH_ITEM*) selection.Front();
178  bool appendUndo =
179  params ? params->appendToLastUndo : ( sch_item && sch_item->IsNew() );
180  bool placingNewItems =
181  params ? params->placingNewItems : ( sch_item && sch_item->IsNew() );
182 
183 
184  //------------------------------------------------------------------------
185  // Setup a drag or a move
186  //
187  m_dragAdditions.clear();
188  m_specialCaseLabels.clear();
189  internalPoints.clear();
190 
191  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
192  {
193  it->ClearFlags( TEMP_SELECTED );
194 
195  if( !it->IsSelected() )
196  it->ClearFlags( STARTPOINT | ENDPOINT );
197 
198  if( !selection.IsHover() && it->IsSelected() )
199  it->SetFlags( STARTPOINT | ENDPOINT );
200  }
201 
202  if( m_isDrag )
203  {
204  EDA_ITEMS connectedDragItems;
205 
206  // Add connections to the selection for a drag.
207  //
208  for( EDA_ITEM* edaItem : selection )
209  {
210  SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
211  std::vector<wxPoint> connections;
212 
213  if( item->Type() == SCH_LINE_T )
214  static_cast<SCH_LINE*>( item )->GetSelectedPoints( connections );
215  else
216  connections = item->GetConnectionPoints();
217 
218  for( wxPoint point : connections )
219  getConnectedDragItems( item, point, connectedDragItems );
220  }
221 
222  for( EDA_ITEM* item : connectedDragItems )
223  {
224  m_dragAdditions.push_back( item->m_Uuid );
226  }
227  }
228  else
229  {
230  // Mark the edges of the block with dangling flags for a move.
231  for( EDA_ITEM* item : selection )
232  static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
233 
234  for( EDA_ITEM* item : selection )
235  static_cast<SCH_ITEM*>( item )->UpdateDanglingState( internalPoints );
236  }
237  // Generic setup
238  //
239  for( EDA_ITEM* item : selection )
240  {
241  if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
242  {
243  if( snapLayer == LAYER_GRAPHICS )
244  snapLayer = LAYER_ANY;
245  else
246  snapLayer = LAYER_CONNECTABLE;
247  }
248  else
249  {
250  if( snapLayer == LAYER_CONNECTABLE )
251  snapLayer = LAYER_ANY;
252  else
253  snapLayer = LAYER_GRAPHICS;
254  }
255 
256  if( item->IsNew() )
257  {
258  if( item->HasFlag( TEMP_SELECTED ) && m_isDrag )
259  {
260  // Item was added in getConnectedDragItems
261  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
262  appendUndo = true;
263  }
264  else
265  {
266  // Item was added in a previous command (and saved to undo by
267  // that command)
268  }
269  }
270  else if( item->GetParent() && item->GetParent()->IsSelected() )
271  {
272  // Item will be (or has been) saved to undo by parent
273  }
274  else
275  {
276  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::CHANGED, appendUndo );
277  appendUndo = true;
278  }
279 
280  SCH_ITEM* schItem = (SCH_ITEM*) item;
281  schItem->SetStoredPos( schItem->GetPosition() );
282  }
283 
284  // Set up the starting position and move/drag offset
285  //
286  m_cursor = controls->GetCursorPosition();
287 
288  if( evt->IsAction( &EE_ACTIONS::restartMove ) )
289  {
290  wxASSERT_MSG( m_anchorPos, wxT( "Should be already set from previous cmd" ) );
291  }
292  else if( placingNewItems )
293  {
294  m_anchorPos = selection.GetReferencePoint();
295  }
296 
297  if( m_anchorPos )
298  {
299  VECTOR2I delta = m_cursor - (*m_anchorPos);
300  bool isPasted = false;
301 
302  // Drag items to the current cursor position
303  for( EDA_ITEM* item : selection )
304  {
305  // Don't double move pins, fields, etc.
306  if( item->GetParent() && item->GetParent()->IsSelected() )
307  continue;
308 
309  moveItem( item, delta );
310  updateItem( item, false );
311 
312  isPasted |= ( item->GetFlags() & IS_PASTED ) != 0;
313  item->ClearFlags( IS_PASTED );
314  }
315 
316  // The first time pasted items are moved we need to store
317  // the position of the cursor so that rotate while moving
318  // works as expected (instead of around the original anchor
319  // point
320  if( isPasted )
321  selection.SetReferencePoint( m_cursor );
322 
324  }
325  // For some items, moving the cursor to anchor is not good (for instance large
326  // hierarchical sheets or symbols can have the anchor outside the view)
327  else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
328  {
331  }
332  else
333  {
334  if( m_frame->GetMoveWarpsCursor() )
335  {
336  // User wants to warp the mouse
337  m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
338  selection.SetReferencePoint( m_cursor );
339  }
340  else
341  {
342  // User does not want to warp the mouse
344  }
345  }
346 
347  controls->SetCursorPosition( m_cursor, false );
349 
350  prevPos = m_cursor;
351  controls->SetAutoPan( true );
352  m_moveInProgress = true;
353  }
354 
355  //------------------------------------------------------------------------
356  // Follow the mouse
357  //
358  m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
359  snapLayer, selection );
360 
361  VECTOR2I delta( m_cursor - prevPos );
363 
364  m_moveOffset += delta;
365  prevPos = m_cursor;
366 
367  for( EDA_ITEM* item : selection )
368  {
369  // Don't double move pins, fields, etc.
370  if( item->GetParent() && item->GetParent()->IsSelected() )
371  continue;
372 
373  moveItem( item, delta );
374  updateItem( item, false );
375  }
376 
377  if( selection.HasReferencePoint() )
378  selection.SetReferencePoint( selection.GetReferencePoint() + delta );
379 
381  }
382  //------------------------------------------------------------------------
383  // Handle cancel
384  //
385  else if( evt->IsCancelInteractive() || evt->IsActivate() )
386  {
387  if( evt->IsCancelInteractive() )
388  m_frame->GetInfoBar()->Dismiss();
389 
390  if( m_moveInProgress )
391  {
392  if( evt->IsActivate() )
393  {
394  // Allowing other tools to activate during a move runs the risk of race
395  // conditions in which we try to spool up both event loops at once.
396 
397  if( m_isDrag )
398  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drag." ) );
399  else
400  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel move." ) );
401 
402  evt->SetPassEvent( false );
403  continue;
404  }
405 
406  evt->SetPassEvent( false );
407  restore_state = true;
408  }
409 
410  break;
411  }
412  //------------------------------------------------------------------------
413  // Handle TOOL_ACTION special cases
414  //
415  else if( evt->Action() == TA_UNDO_REDO_PRE )
416  {
417  unselect = true;
418  break;
419  }
420  else if( evt->IsAction( &ACTIONS::doDelete ) )
421  {
422  evt->SetPassEvent();
423  // Exit on a delete; there will no longer be anything to drag.
424  break;
425  }
426  else if( evt->IsAction( &ACTIONS::duplicate ) )
427  {
428  if( selection.Front()->IsNew() )
429  {
430  // This doesn't really make sense; we'll just end up dragging a stack of
431  // objects so we ignore the duplicate and just carry on.
432  continue;
433  }
434 
435  // Move original back and exit. The duplicate will run in its own loop.
436  restore_state = true;
437  unselect = false;
438  chain_commands = true;
439  break;
440  }
441  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
442  {
443  if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
445  {
446  SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
447  int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
448 
449  if( symbol )
450  {
451  m_frame->SelectUnit( symbol, unit );
453  }
454  }
455  }
456  else if( evt->IsAction( &EE_ACTIONS::highlightNet )
458  {
459  // These don't make any sense during a move. Eat them.
460  }
461  //------------------------------------------------------------------------
462  // Handle context menu
463  //
464  else if( evt->IsClick( BUT_RIGHT ) )
465  {
467  }
468  //------------------------------------------------------------------------
469  // Handle drop
470  //
471  else if( evt->IsMouseUp( BUT_LEFT )
472  || evt->IsClick( BUT_LEFT )
473  || evt->IsDblClick( BUT_LEFT ) )
474  {
475  break; // Finish
476  }
477  else
478  {
479  evt->SetPassEvent();
480  }
481 
482  controls->SetAutoPan( m_moveInProgress );
483 
484  } while( ( evt = Wait() ) ); //Should be assignment not equality test
485 
486  controls->ForceCursorPosition( false );
487  controls->ShowCursor( false );
488  controls->SetAutoPan( false );
489 
490  if( !chain_commands )
491  m_moveOffset = { 0, 0 };
492 
493  m_anchorPos.reset();
494 
495  for( EDA_ITEM* item : selection )
496  item->ClearEditFlags();
497 
498  if( restore_state )
499  {
502  }
503  else
504  {
505  // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
506  for( EDA_ITEM* item : selection )
507  updateItem( item, true );
508 
509  EE_SELECTION selectionCopy( selection );
511 
512  // If we move items away from a junction, we _may_ want to add a junction there
513  // to denote the state.
514  for( const DANGLING_END_ITEM& it : internalPoints )
515  {
516  if( m_frame->GetScreen()->IsExplicitJunctionNeeded( it.GetPosition()) )
517  m_frame->AddJunction( m_frame->GetScreen(), it.GetPosition(), true, false );
518  }
519 
520  m_toolMgr->RunAction( EE_ACTIONS::trimOverlappingWires, true, &selectionCopy );
521  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selectionCopy );
522 
525 
526  m_frame->OnModify();
527  }
528 
529  if( unselect )
531  else
532  m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
533 
534  m_dragAdditions.clear();
535  m_moveInProgress = false;
536  m_frame->PopTool( tool );
537  return 0;
538 }
539 
540 
541 void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const wxPoint& aPoint,
542  EDA_ITEMS& aList )
543 {
544  EE_RTREE& items = m_frame->GetScreen()->Items();
545  EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
546  bool ptHasUnselectedJunction = false;
547 
548  for( SCH_ITEM* item : itemsOverlapping )
549  {
550  if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
551  {
552  ptHasUnselectedJunction = true;
553  break;
554  }
555  }
556 
557  for( SCH_ITEM* test : itemsOverlapping )
558  {
559  if( test == aOriginalItem || test->IsSelected() || !test->CanConnect( aOriginalItem ) )
560  continue;
561 
562  KICAD_T testType = test->Type();
563 
564  switch( testType )
565  {
566  case SCH_LINE_T:
567  {
568  // Select the connected end of wires/bus connections that don't have an unselected
569  // junction isolating them from the drag
570  if( ptHasUnselectedJunction )
571  break;
572 
573  SCH_LINE* line = static_cast<SCH_LINE*>( test );
574 
575  if( line->GetStartPoint() == aPoint )
576  {
577  if( !line->HasFlag(TEMP_SELECTED ) )
578  aList.push_back( line );
579 
581  }
582  else if( line->GetEndPoint() == aPoint )
583  {
584  if( !line->HasFlag(TEMP_SELECTED ) )
585  aList.push_back( line );
586 
587  line->SetFlags(ENDPOINT | TEMP_SELECTED );
588  }
589  else
590  {
591  break;
592  }
593 
594  // Since only one end is going to move, the movement vector of any labels attached
595  // to it is scaled by the proportion of the line length the label is from the moving
596  // end.
597  for( SCH_ITEM* item : itemsOverlapping )
598  {
599  if( item->Type() == SCH_LABEL_T )
600  {
601  SCH_TEXT* label = static_cast<SCH_TEXT*>( item );
602 
603  if( label->IsSelected() )
604  continue; // These will be moved on their own because they're selected
605 
606  if( label->HasFlag( TEMP_SELECTED ) )
607  continue;
608 
609  if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) )
610  {
611  label->SetFlags( TEMP_SELECTED );
612  aList.push_back( label );
613 
615  info.attachedLine = line;
616  info.originalLabelPos = label->GetPosition();
617  m_specialCaseLabels[label] = info;
618  }
619  }
620  }
621 
622  break;
623  }
624 
625  case SCH_SHEET_T:
626  case SCH_SYMBOL_T:
627  case SCH_JUNCTION_T:
628  if( test->IsConnected( aPoint ) )
629  {
630  // Add a new wire between the symbol or junction and the selected item so
631  // the selected item can be dragged.
632  SCH_LINE* newWire = nullptr;
633 
634  if( test->GetLayer() == LAYER_BUS_JUNCTION ||
635  aOriginalItem->GetLayer() == LAYER_BUS )
636  {
637  newWire = new SCH_LINE( aPoint, LAYER_BUS );
638  }
639  else
640  newWire = new SCH_LINE( aPoint, LAYER_WIRE );
641 
642  newWire->SetFlags( IS_NEW );
643  m_frame->AddToScreen( newWire, m_frame->GetScreen() );
644 
645  newWire->SetFlags( TEMP_SELECTED | STARTPOINT );
646  aList.push_back( newWire );
647  }
648  break;
649 
650  case SCH_NO_CONNECT_T:
651  // Select no-connects that are connected to items being moved.
652  if( !test->HasFlag( TEMP_SELECTED ) && test->IsConnected( aPoint ) )
653  {
654  aList.push_back( test );
655  test->SetFlags( TEMP_SELECTED );
656  }
657 
658  break;
659 
660  case SCH_LABEL_T:
661  case SCH_GLOBAL_LABEL_T:
662  case SCH_HIER_LABEL_T:
663  // Performance optimization:
664  if( test->HasFlag( TEMP_SELECTED ) )
665  break;
666 
667  // Select labels that are connected to a wire (or bus) being moved.
668  if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
669  {
670  SCH_TEXT* label = static_cast<SCH_TEXT*>( test );
671  SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
672  bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
673 
674  if( line->HitTest( label->GetTextPos(), 1 ) )
675  {
676  label->SetFlags( TEMP_SELECTED );
677  aList.push_back( label );
678 
679  if( oneEndFixed )
680  {
682  info.attachedLine = line;
683  info.originalLabelPos = label->GetPosition();
684  m_specialCaseLabels[ label ] = info;
685  }
686  }
687  }
688 
689  break;
690 
692  case SCH_BUS_BUS_ENTRY_T:
693  // Performance optimization:
694  if( test->HasFlag( TEMP_SELECTED ) )
695  break;
696 
697  // Select bus entries that are connected to a bus being moved.
698  if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
699  {
700  SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
701  bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
702 
703  if( oneEndFixed )
704  {
705  // This is only going to end in tears, so don't go there
706  continue;
707  }
708 
709  for( wxPoint& point : test->GetConnectionPoints() )
710  {
711  if( line->HitTest( point, 1 ) )
712  {
713  test->SetFlags( TEMP_SELECTED );
714  aList.push_back( test );
715 
716  // A bus entry needs its wire & label as well
717  std::vector<wxPoint> ends = test->GetConnectionPoints();
718  wxPoint otherEnd;
719 
720  if( ends[0] == point )
721  otherEnd = ends[1];
722  else
723  otherEnd = ends[0];
724 
725  getConnectedDragItems( test, otherEnd, aList );
726 
727  // No need to test the other end of the bus entry
728  break;
729  }
730  }
731  }
732 
733  break;
734 
735  default:
736  break;
737  }
738  }
739 }
740 
741 
742 void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
743 {
744  switch( aItem->Type() )
745  {
746  case SCH_LINE_T:
747  {
748  SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
749 
750  if( aItem->HasFlag( STARTPOINT ) )
751  line->MoveStart( (wxPoint) aDelta );
752 
753  if( aItem->HasFlag( ENDPOINT ) )
754  line->MoveEnd( (wxPoint) aDelta );
755 
756  break;
757  }
758 
759  case SCH_PIN_T:
760  case SCH_FIELD_T:
761  {
762  SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
763  wxPoint delta( aDelta );
764 
765  if( parent && parent->Type() == SCH_SYMBOL_T )
766  {
767  SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem->GetParent();
768  TRANSFORM transform = symbol->GetTransform().InverseTransform();
769 
770  delta = transform.TransformCoordinate( delta );
771  }
772 
773  static_cast<SCH_ITEM*>( aItem )->Move( delta );
774 
775  // If we're moving a field with respect to its parent then it's no longer auto-placed
776  if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
777  parent->ClearFieldsAutoplaced();
778 
779  break;
780  }
781 
782  case SCH_SHEET_PIN_T:
783  {
784  SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
785 
786  pin->SetStoredPos( pin->GetStoredPos() + (wxPoint) aDelta );
787  pin->ConstrainOnEdge( pin->GetStoredPos() );
788  break;
789  }
790 
791  case SCH_LABEL_T:
792  {
793  SCH_TEXT* label = static_cast<SCH_TEXT*>( aItem );
794 
795  if( m_specialCaseLabels.count( label ) )
796  {
798  SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
799  label->SetPosition( (wxPoint) currentLine.NearestPoint( info.originalLabelPos ) );
800  }
801  else
802  {
803  label->Move( (wxPoint) aDelta );
804  }
805 
806  break;
807  }
808 
809  default:
810  static_cast<SCH_ITEM*>( aItem )->Move( (wxPoint) aDelta );
811  break;
812  }
813 
814  getView()->Hide( aItem, false );
815  aItem->SetFlags( IS_MOVING );
816 }
817 
818 
820 {
823  bool append_undo = false;
824 
825  auto doMoveItem =
826  [&]( EDA_ITEM* item, const VECTOR2I& delta )
827  {
828  saveCopyInUndoList( item, UNDO_REDO::CHANGED, append_undo );
829  append_undo = true;
830 
831  moveItem( item, delta );
832  item->ClearFlags( IS_MOVING );
833  updateItem( item, true );
834  };
835 
836  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
837  {
838  if( !it->IsSelected() )
839  it->ClearFlags( STARTPOINT | ENDPOINT );
840 
841  if( !selection.IsHover() && it->IsSelected() )
842  it->SetFlags( STARTPOINT | ENDPOINT );
843 
844  it->SetStoredPos( it->GetPosition() );
845 
846  if( it->Type() == SCH_SHEET_T )
847  {
848  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
849  pin->SetStoredPos( pin->GetPosition() );
850  }
851  }
852 
853  for( EDA_ITEM* item : selection )
854  {
855  if( item->Type() == SCH_LINE_T )
856  {
857  SCH_LINE* line = static_cast<SCH_LINE*>( item );
858  std::vector<int> flags{ STARTPOINT, ENDPOINT };
859  std::vector<wxPoint> pts{ line->GetStartPoint(), line->GetEndPoint() };
860 
861  for( int ii = 0; ii < 2; ++ii )
862  {
863  EDA_ITEMS drag_items{ item };
864  line->ClearFlags();
865  line->SetFlags( SELECTED );
866  line->SetFlags( flags[ii] );
867  getConnectedDragItems( line, pts[ii], drag_items );
868  std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
869 
870  VECTOR2I gridpt = grid.AlignGrid( pts[ii] ) - pts[ii];
871 
872  if( gridpt != VECTOR2I( 0, 0 ) )
873  {
874  for( EDA_ITEM* dragItem : unique_items )
875  {
876  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
877  continue;
878 
879  doMoveItem( dragItem, gridpt );
880  }
881  }
882  }
883  }
884  else if( item->Type() == SCH_FIELD_T )
885  {
886  VECTOR2I gridpt = grid.AlignGrid( item->GetPosition() ) - item->GetPosition();
887 
888  if( gridpt != VECTOR2I( 0, 0 ) )
889  doMoveItem( item, gridpt );
890  }
891  else if( item->Type() == SCH_SHEET_T )
892  {
893  SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
894  VECTOR2I topRight = sheet->GetPosition();
895  VECTOR2I bottomRight = topRight + sheet->GetSize();
896  VECTOR2I tl_gridpt = grid.AlignGrid( topRight ) - topRight;
897  VECTOR2I br_gridpt = grid.AlignGrid( bottomRight ) - bottomRight;
898 
899  if( tl_gridpt != VECTOR2I( 0, 0 ) || br_gridpt != VECTOR2I( 0, 0 ) )
900  {
901  doMoveItem( sheet, tl_gridpt );
902 
903  VECTOR2I newSize = (VECTOR2I) sheet->GetSize() - tl_gridpt + br_gridpt;
904  sheet->SetSize( wxSize( newSize.x, newSize.y ) );
905  updateItem( sheet, true );
906 
907  for( SCH_SHEET_PIN* pin : sheet->GetPins() )
908  {
909  VECTOR2I gridpt;
910 
911  if( pin->GetSide() == SHEET_SIDE::TOP || pin->GetSide() == SHEET_SIDE::LEFT )
912  gridpt = tl_gridpt;
913  else
914  gridpt = br_gridpt;
915 
916  if( gridpt != VECTOR2I( 0, 0 ) )
917  {
918  EDA_ITEMS drag_items;
919  getConnectedDragItems( pin, pin->GetConnectionPoints()[0], drag_items );
920 
921  doMoveItem( pin, gridpt );
922 
923  for( EDA_ITEM* dragItem : drag_items )
924  {
925  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
926  continue;
927 
928  doMoveItem( dragItem, gridpt );
929  }
930  }
931  }
932  }
933  }
934  else
935  {
936  std::vector<wxPoint> connections;
937  EDA_ITEMS drag_items{ item };
938  connections = static_cast<SCH_ITEM*>( item )->GetConnectionPoints();
939 
940  for( const wxPoint& point : connections )
941  getConnectedDragItems( static_cast<SCH_ITEM*>( item ), point, drag_items );
942 
943  std::map<VECTOR2I, int> shifts;
944  VECTOR2I most_common( 0, 0 );
945  int max_count = 0;
946 
947  for( const wxPoint& conn : connections )
948  {
949  VECTOR2I gridpt = grid.AlignGrid( conn ) - conn;
950 
951  shifts[gridpt]++;
952 
953  if( shifts[gridpt] > max_count )
954  {
955  most_common = gridpt;
956  max_count = shifts[most_common];
957  }
958  }
959 
960  if( most_common != VECTOR2I( 0, 0 ) )
961  {
962  for( EDA_ITEM* dragItem : drag_items )
963  {
964  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
965  continue;
966 
967  doMoveItem( dragItem, most_common );
968  }
969  }
970  }
971  }
972 
975  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selection );
976 
979 
980  m_frame->OnModify();
981  return 0;
982 }
983 
984 
986 {
988  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
989  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
991 }
static TOOL_ACTION moveActivate
Definition: ee_actions.h:113
void Hide(VIEW_ITEM *aItem, bool aHide=true)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1537
OPT< int > GetCommandId() const
Definition: tool_event.h:455
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
bool IsHover() const
Definition: selection.h:74
#define STARTPOINT
When a line is selected, these flags indicate which.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
void SetPassEvent(bool aPass=true)
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition: tool_event.h:236
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
virtual std::vector< wxPoint > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition: sch_item.h:364
void RecalculateConnections(SCH_CLEANUP_FLAGS aCleanupFlags)
Generate the connection data for the entire schematic hierarchy.
wxPoint GetStartPoint() const
Definition: sch_line.h:90
bool IsSelected() const
Definition: eda_item.h:122
#define IS_NEW
New item, just created.
static TOOL_ACTION doDelete
Definition: actions.h:72
int Main(const TOOL_EVENT &aEvent)
Run an interactive move of the selected items, or the item under the cursor.
VECTOR2I m_cursor
Definition: sch_move_tool.h:96
TOOL_ACTIONS Action() const
These give a tool a method of informing the TOOL_MANAGER that a particular event should be passed on ...
Definition: tool_event.h:230
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:199
VECTOR2I m_moveOffset
Last cursor position (needed for getModificationPoint() to avoid changes of edit reference point).
Definition: sch_move_tool.h:92
static TOOL_ACTION restartMove
Definition: ee_actions.h:199
void MoveStart(const wxPoint &aMoveVector)
Definition: sch_line.cpp:144
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
std::vector< KIID > m_dragAdditions
Used for chaining commands.
Definition: sch_move_tool.h:89
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:155
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
TRANSFORM InverseTransform() const
Calculate the Inverse mirror/rotation transform.
Definition: transform.cpp:57
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:152
TOOL_MENU & GetToolMenu()
bool IsMotion() const
Definition: tool_event.h:300
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
Schematic editor (Eeschema) main window.
Implements an R-tree for fast spatial and type indexing of schematic items.
Definition: sch_rtree.h:40
#define ENDPOINT
ends. (Used to support dragging.)
void SelectUnit(SCH_SYMBOL *aSymbol, int aUnit)
Definition: picksymbol.cpp:211
bool m_moveInProgress
< Flag determining if anything is being dragged right now
Definition: sch_move_tool.h:85
virtual wxPoint GetPosition() const
Definition: eda_item.h:251
static const KICAD_T MovableItems[]
Definition: ee_collectors.h:44
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
static TOOL_ACTION alignToGrid
Definition: ee_actions.h:111
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
TRANSFORM & GetTransform()
Definition: sch_symbol.h:232
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:205
bool HitTest(const wxPoint &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: sch_line.cpp:833
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:211
wxPoint TransformCoordinate(const wxPoint &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition: transform.cpp:42
bool IsNew() const
Definition: eda_item.h:118
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:88
SCH_JUNCTION * AddJunction(SCH_SCREEN *aScreen, const wxPoint &aPos, bool aAppendToUndo, bool aFinal=true)
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:66
EE_SELECTION & GetSelection()
Return the set of currently selected items.
EE_SELECTION & RequestSelection(const KICAD_T *aFilterList=EE_COLLECTOR::AllItems)
Return either an existing selection (filtered), or the selection at the current cursor if the existin...
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
static TOOL_ACTION trimOverlappingWires
Definition: ee_actions.h:76
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: infobar.cpp:175
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:285
virtual bool CanConnect(const SCH_ITEM *aItem) const
Definition: sch_item.h:344
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:45
void Move(const wxPoint &aMoveVector) override
Move the item by aMoveVector to a new position.
Definition: sch_text.h:201
#define IS_PASTED
Modifier on IS_NEW which indicates it came from clipboard.
#define IS_MOVING
Item being moved.
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
virtual bool IsMovableFromAnchorPoint() const
Definition: sch_item.h:220
std::map< SCH_TEXT *, SPECIAL_CASE_LABEL_INFO > m_specialCaseLabels
void saveCopyInUndoList(EDA_ITEM *aItem, UNDO_REDO aType, bool aAppend=false)
Definition: ee_tool_base.h:134
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:205
virtual void PopTool(const std::string &actionName)
boost::optional< VECTOR2I > m_anchorPos
Definition: sch_move_tool.h:98
static TOOL_ACTION explicitCrossProbe
Definition: ee_actions.h:200
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:432
void getConnectedDragItems(SCH_ITEM *aOriginalItem, const wxPoint &aPoint, EDA_ITEMS &aList)
Set up handlers for various events.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
Generic, UI-independent tool event.
Definition: tool_event.h:152
int AlignElements(const TOOL_EVENT &aEvent)
Align selected elements to the grid.
void ClearFieldsAutoplaced()
Definition: sch_item.h:427
EDA_ITEM * GetParent() const
Definition: eda_item.h:114
An interface for classes handling user events controlling the view behavior such as zooming,...
#define _(s)
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:153
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:179
#define SELECTED
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:208
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:65
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:506
bool DisableGridSnapping() const
Definition: tool_event.h:341
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:56
bool Init() override
Init() is called once upon a registration of the tool.
static TOOL_ACTION addNeededJunctions
Definition: ee_actions.h:75
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:295
wxPoint GetPosition() const override
Definition: sch_text.h:241
wxPoint GetPosition() const override
Definition: sch_sheet.h:379
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:98
EE_TYPE Overlapping(const EDA_RECT &aRect) const
Definition: sch_rtree.h:235
void updateItem(EDA_ITEM *aItem, bool aUpdateRTree) const
Similar to getView()->Update(), but handles items that are redrawn by their parents and updating the ...
Definition: ee_tool_base.h:103
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
void SetSize(const wxSize &aSize)
Definition: sch_sheet.h:105
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:259
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:183
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:336
Definition: seg.h:40
bool IsExplicitJunctionNeeded(const wxPoint &aPosition) const
Indicates that a junction dot is necessary at the given location, and does not yet exist.
Definition: sch_screen.cpp:423
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:460
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
bool IsActivate() const
Definition: tool_event.h:315
see class PGM_BASE
Schematic symbol object.
Definition: sch_symbol.h:78
int AddItemToSel(const TOOL_EVENT &aEvent)
#define TEMP_SELECTED
flag indicating that the structure has already selected
void AddToScreen(EDA_ITEM *aItem, SCH_SCREEN *aScreen)
Add an item to the screen (and view) aScreen is the screen the item is located on,...
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
void MoveEnd(const wxPoint &aMoveVector)
Definition: sch_line.cpp:154
wxSize GetSize() const
Definition: sch_sheet.h:104
void moveItem(EDA_ITEM *aItem, const VECTOR2I &aDelta)
Find additional items for a drag operation.
static TOOL_ACTION drag
Definition: ee_actions.h:115
void RollbackSchematicFromUndo()
Perform an undo of the last edit WITHOUT logging a corresponding redo.
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:110
static SELECTION_CONDITION OnlyType(KICAD_T aType)
Create a functor that tests if the selected items are only of given type.
void RebuildSelection()
Rebuild the selection from the EDA_ITEMs' selection flags.
The EE_TYPE struct provides a type-specific auto-range iterator to the RTree.
Definition: sch_rtree.h:192
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:99
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Definition: tools_holder.h:141
void Activate()
Run the tool.
const wxPoint & GetTextPos() const
Definition: eda_text.h:268
WX_INFOBAR * GetInfoBar()
constexpr int delta
static TOOL_ACTION move
Definition: ee_actions.h:114
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:49
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current screen and u...
Helper class used to store the state of schematic items that can be connected to other schematic item...
Definition: sch_item.h:79
static TOOL_ACTION highlightNet
Definition: ee_actions.h:220
virtual const EDA_RECT GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:75
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
#define QUIET_MODE
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
void SetPosition(const wxPoint &aPosition) override
Definition: sch_text.h:242
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void SetStoredPos(const wxPoint &aPos)
Definition: sch_item.h:223
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:59
static TOOL_ACTION duplicate
Definition: actions.h:71
static TOOL_ACTION refreshPreview
Definition: actions.h:106
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:145
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:112
bool m_isDrag
Items (such as wires) which were added to the selection for a drag.
Definition: sch_move_tool.h:86
wxPoint GetEndPoint() const
Definition: sch_line.h:93