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_line.h>
36 #include <sch_edit_frame.h>
37 #include <eeschema_id.h>
38 #include <pgm_base.h>
40 #include "sch_move_tool.h"
41 
42 
43 // For adding to or removing from selections
44 #define QUIET_MODE true
45 
46 
48  EE_TOOL_BASE<SCH_EDIT_FRAME>( "eeschema.InteractiveMove" ),
49  m_moveInProgress( false ),
50  m_isDragOperation( false ),
51  m_moveOffset( 0, 0 )
52 {
53 }
54 
55 
57 {
59 
60  auto moveCondition =
61  []( const SELECTION& aSel )
62  {
63  if( aSel.Empty() || SELECTION_CONDITIONS::OnlyType( SCH_MARKER_T )( aSel ) )
64  return false;
65 
67  return false;
68 
69  return true;
70  };
71 
72  // Add move actions to the selection tool menu
73  //
75 
76  selToolMenu.AddItem( EE_ACTIONS::move, moveCondition, 150 );
77  selToolMenu.AddItem( EE_ACTIONS::drag, moveCondition, 150 );
78  selToolMenu.AddItem( EE_ACTIONS::alignToGrid, moveCondition, 150 );
79 
80  return true;
81 }
82 
83 
84 /* TODO - Tom/Jeff
85  - add preferences option "Move origin: always cursor / item origin"
86  - add preferences option "Default drag action: drag items / move"
87  - add preferences option "Drag always selects"
88  */
89 
90 
91 int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
92 {
93  EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
95  EE_GRID_HELPER grid( m_toolMgr );
96 
97  m_anchorPos.reset();
98 
99  if( aEvent.IsAction( &EE_ACTIONS::move ) )
100  m_isDragOperation = false;
101  else if( aEvent.IsAction( &EE_ACTIONS::drag ) )
102  m_isDragOperation = true;
103  else if( aEvent.IsAction( &EE_ACTIONS::moveActivate ) )
105  else
106  return 0;
107 
108  if( m_moveInProgress )
109  {
110  auto sel = m_selectionTool->GetSelection().Front();
111 
112  if( sel && !sel->IsNew() )
113  {
114  // User must have switched from move to drag or vice-versa. Reset the selected
115  // items so we can start again with the current m_isDragOperation.
119  m_moveInProgress = false;
120  controls->SetAutoPan( false );
121 
122  // And give it a kick so it doesn't have to wait for the first mouse movement to
123  // refresh.
125  }
126 
127  return 0;
128  }
129 
130  // Be sure that there is at least one item that we can move. If there's no selection try
131  // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
133  bool unselect = selection.IsHover();
134 
135  // Keep an original copy of the starting points for cleanup after the move
136  std::vector<DANGLING_END_ITEM> internalPoints;
137 
138  Activate();
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->Modifier( MD_ALT ) );
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 = sch_item && sch_item->IsNew();
179  bool placingNewItems = sch_item && sch_item->IsNew();
180 
181  //------------------------------------------------------------------------
182  // Setup a drag or a move
183  //
184  m_dragAdditions.clear();
185  m_specialCaseLabels.clear();
186  internalPoints.clear();
187 
188  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
189  {
190  it->ClearFlags( TEMP_SELECTED );
191 
192  if( !it->IsSelected() )
193  it->ClearFlags( STARTPOINT | ENDPOINT );
194 
195  if( !selection.IsHover() && it->IsSelected() )
196  it->SetFlags( STARTPOINT | ENDPOINT );
197  }
198 
199  if( m_isDragOperation )
200  {
201  // Add connections to the selection for a drag.
202  //
203  for( EDA_ITEM* edaItem : selection )
204  {
205  SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
206  std::vector<wxPoint> connections;
207 
208  if( item->Type() == SCH_LINE_T )
209  static_cast<SCH_LINE*>( item )->GetSelectedPoints( connections );
210  else
211  connections = item->GetConnectionPoints();
212 
213  for( wxPoint point : connections )
214  getConnectedDragItems( item, point, m_dragAdditions );
215  }
216 
218  }
219  else
220  {
221  // Mark the edges of the block with dangling flags for a move.
222  for( EDA_ITEM* item : selection )
223  static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
224 
225  for( EDA_ITEM* item : selection )
226  static_cast<SCH_ITEM*>( item )->UpdateDanglingState( internalPoints );
227  }
228  // Generic setup
229  //
230  for( EDA_ITEM* item : selection )
231  {
232  if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
233  {
234  if( snapLayer == LAYER_GRAPHICS )
235  snapLayer = LAYER_ANY;
236  else
237  snapLayer = LAYER_CONNECTABLE;
238  }
239  else
240  {
241  if( snapLayer == LAYER_CONNECTABLE )
242  snapLayer = LAYER_ANY;
243  else
244  snapLayer = LAYER_GRAPHICS;
245  }
246 
247  if( item->IsNew() )
248  {
249  if( item->HasFlag( TEMP_SELECTED ) && m_isDragOperation )
250  {
251  // Item was added in getConnectedDragItems
252  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
253  appendUndo = true;
254  }
255  else
256  {
257  // Item was added in a previous command (and saved to undo by
258  // that command)
259  }
260  }
261  else if( item->GetParent() && item->GetParent()->IsSelected() )
262  {
263  // Item will be (or has been) saved to undo by parent
264  }
265  else
266  {
267  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::CHANGED, appendUndo );
268  appendUndo = true;
269  }
270 
271  SCH_ITEM* schItem = (SCH_ITEM*) item;
272  schItem->SetStoredPos( schItem->GetPosition() );
273  }
274 
275  // Set up the starting position and move/drag offset
276  //
277  m_cursor = controls->GetCursorPosition();
278 
279  if( evt->IsAction( &EE_ACTIONS::restartMove ) )
280  {
281  wxASSERT_MSG( m_anchorPos, "Should be already set from previous cmd" );
282  }
283  else if( placingNewItems )
284  {
285  m_anchorPos = selection.GetReferencePoint();
286  }
287 
288  if( m_anchorPos )
289  {
290  VECTOR2I delta = m_cursor - (*m_anchorPos);
291 
292  // Drag items to the current cursor position
293  for( EDA_ITEM* item : selection )
294  {
295  // Don't double move pins, fields, etc.
296  if( item->GetParent() && item->GetParent()->IsSelected() )
297  continue;
298 
299  moveItem( item, delta );
300  updateItem( item, false );
301  }
302 
304  }
305  // For some items, moving the cursor to anchor is not good (for instance large
306  // hierarchical sheets or components can have the anchor outside the view)
307  else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
308  {
311  }
312  else
313  {
314  if( m_frame->GetMoveWarpsCursor() )
315  {
316  // User wants to warp the mouse
317  m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
318  }
319  else
320  {
321  // User does not want to warp the mouse
323  }
324  }
325 
326  controls->SetCursorPosition( m_cursor, false );
328 
329  prevPos = m_cursor;
330  controls->SetAutoPan( true );
331  m_moveInProgress = true;
332  }
333 
334  //------------------------------------------------------------------------
335  // Follow the mouse
336  //
337  m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
338  snapLayer, selection );
339 
340  VECTOR2I delta( m_cursor - prevPos );
342 
343  m_moveOffset += delta;
344  prevPos = m_cursor;
345 
346  for( EDA_ITEM* item : selection )
347  {
348  // Don't double move pins, fields, etc.
349  if( item->GetParent() && item->GetParent()->IsSelected() )
350  continue;
351 
352  moveItem( item, delta );
353  updateItem( item, false );
354  }
355 
357  }
358  //------------------------------------------------------------------------
359  // Handle cancel
360  //
361  else if( evt->IsCancelInteractive() || evt->IsActivate() )
362  {
363  if( m_moveInProgress && evt->IsCancelInteractive() )
364  evt->SetPassEvent( false );
365 
366  if( m_moveInProgress )
367  restore_state = true;
368 
369  break;
370  }
371  //------------------------------------------------------------------------
372  // Handle TOOL_ACTION special cases
373  //
374  else if( evt->Action() == TA_UNDO_REDO_PRE )
375  {
376  unselect = true;
377  break;
378  }
379  else if( evt->IsAction( &ACTIONS::doDelete ) )
380  {
381  // Exit on a remove operation; there is no further processing for removed items.
382  break;
383  }
384  else if( evt->IsAction( &ACTIONS::duplicate ) )
385  {
386  if( selection.Front()->IsNew() )
387  {
388  // This doesn't really make sense; we'll just end up dragging a stack of
389  // objects so Duplicate() is going to ignore this and we'll just carry on.
390  continue;
391  }
392 
393  // Move original back and exit. The duplicate will run in its own loop.
394  restore_state = true;
395  unselect = false;
396  chain_commands = true;
397  break;
398  }
399  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
400  {
401  if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
403  {
404  SCH_COMPONENT* component = dynamic_cast<SCH_COMPONENT*>( selection.Front() );
405  int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
406 
407  if( component )
408  {
409  m_frame->SelectUnit( component, unit );
411  }
412  }
413  }
414  //------------------------------------------------------------------------
415  // Handle context menu
416  //
417  else if( evt->IsClick( BUT_RIGHT ) )
418  {
420  }
421  //------------------------------------------------------------------------
422  // Handle drop
423  //
424  else if( evt->IsMouseUp( BUT_LEFT )
425  || evt->IsClick( BUT_LEFT )
426  || evt->IsDblClick( BUT_LEFT ) )
427  {
428  break; // Finish
429  }
430  else
431  evt->SetPassEvent();
432 
433  controls->SetAutoPan( m_moveInProgress );
434 
435  } while( ( evt = Wait() ) ); //Should be assignment not equality test
436 
437  controls->ForceCursorPosition( false );
438  controls->ShowCursor( false );
439  controls->SetAutoPan( false );
440 
441  if( !chain_commands )
442  m_moveOffset = { 0, 0 };
443 
444  m_anchorPos.reset();
445 
446  for( EDA_ITEM* item : selection )
447  item->ClearEditFlags();
448 
449  if( restore_state )
450  {
453  }
454  else
455  {
456  // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
457  for( EDA_ITEM* item : selection )
458  updateItem( item, true );
459 
460  EE_SELECTION selectionCopy( selection );
462 
463  // If we move items away from a junction, we _may_ want to add a junction there
464  // to denote the state.
465  for( const DANGLING_END_ITEM& it : internalPoints )
466  {
467  if( m_frame->GetScreen()->IsJunctionNeeded( it.GetPosition(), true ) )
468  m_frame->AddJunction( m_frame->GetScreen(), it.GetPosition(), true, false );
469  }
470 
471  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selectionCopy );
472 
475 
476  m_frame->OnModify();
477  }
478 
479  if( unselect )
481  else
482  m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
483 
484  m_dragAdditions.clear();
485  m_moveInProgress = false;
486  m_frame->PopTool( tool );
487  return 0;
488 }
489 
490 
491 void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, wxPoint aPoint,
492  EDA_ITEMS& aList )
493 {
494  EE_RTREE& items = m_frame->GetScreen()->Items();
495  EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
496  bool ptHasUnselectedJunction = false;
497 
498  for( SCH_ITEM* item : itemsOverlapping )
499  {
500  if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
501  {
502  ptHasUnselectedJunction = true;
503  break;
504  }
505  }
506 
507  for( SCH_ITEM *test : itemsOverlapping )
508  {
509  if( test == aOriginalItem || test->IsSelected() || !test->CanConnect( aOriginalItem ) )
510  continue;
511 
512  KICAD_T testType = test->Type();
513 
514  switch( testType )
515  {
516  case SCH_LINE_T:
517  {
518  // Select the connected end of wires/bus connections that don't have an unselected
519  // junction isolating them from the drag
520  if( ptHasUnselectedJunction )
521  break;
522 
523  SCH_LINE* testLine = static_cast<SCH_LINE*>( test );
524 
525  if( testLine->GetStartPoint() == aPoint )
526  {
527  if( !testLine->HasFlag( TEMP_SELECTED ) )
528  aList.push_back( testLine );
529 
530  testLine->SetFlags( STARTPOINT | TEMP_SELECTED );
531  }
532  else if( testLine->GetEndPoint() == aPoint )
533  {
534  if( !testLine->HasFlag( TEMP_SELECTED ) )
535  aList.push_back( testLine );
536 
537  testLine->SetFlags( ENDPOINT | TEMP_SELECTED );
538  }
539  else
540  {
541  break;
542  }
543 
544  // Since only one end is going to move, the movement vector of any labels attached
545  // to it is scaled by the proportion of the line length the label is from the moving
546  // end.
547  for( SCH_ITEM* item : itemsOverlapping )
548  {
549  if( item->Type() == SCH_LABEL_T )
550  {
551  SCH_TEXT* label = static_cast<SCH_TEXT*>( item );
552 
553  if( label->IsSelected() )
554  continue; // These will be moved on their own because they're selected
555 
556  if( label->CanConnect( testLine )
557  && testLine->HitTest( label->GetPosition(), 1 ) )
558  {
559  if( !label->HasFlag( TEMP_SELECTED ) )
560  aList.push_back( label );
561 
563  info.attachedLine = testLine;
564  info.originalLabelPos = label->GetPosition();
565  m_specialCaseLabels[label] = info;
566  }
567  }
568  }
569 
570  break;
571  }
572 
573  case SCH_SHEET_T:
574  // Dragging a sheet just because it's connected to something else feels a bit like
575  // the tail wagging the dog, but this could be moved down to the next case.
576  break;
577 
578  case SCH_COMPONENT_T:
579  case SCH_JUNCTION_T:
580  if( test->IsConnected( aPoint ) )
581  {
582  // Add a new wire between the component or junction and the selected item so
583  // the selected item can be dragged.
584  SCH_LINE* newWire = nullptr;
585 
586  if( test->GetLayer() == LAYER_BUS_JUNCTION )
587  newWire = new SCH_LINE( aPoint, LAYER_BUS );
588  else
589  newWire = new SCH_LINE( aPoint, LAYER_WIRE );
590 
591  newWire->SetFlags( IS_NEW );
592  m_frame->AddToScreen( newWire, m_frame->GetScreen() );
593 
594  newWire->SetFlags( TEMP_SELECTED | STARTPOINT );
595  aList.push_back( newWire );
596  }
597  break;
598 
599  case SCH_NO_CONNECT_T:
600  // Select no-connects that are connected to items being moved.
601  if( !test->HasFlag( TEMP_SELECTED ) && test->IsConnected( aPoint ) )
602  {
603  aList.push_back( test );
604  test->SetFlags( TEMP_SELECTED );
605  }
606 
607  break;
608 
609  case SCH_LABEL_T:
610  case SCH_GLOBAL_LABEL_T:
611  case SCH_HIER_LABEL_T:
613  case SCH_BUS_BUS_ENTRY_T:
614  // Performance optimization:
615  if( test->HasFlag( TEMP_SELECTED ) )
616  break;
617 
618  // Select labels and bus entries that are connected to a wire being moved.
619  if( aOriginalItem->Type() == SCH_LINE_T )
620  {
621  std::vector<wxPoint> connections = test->GetConnectionPoints();
622 
623  for( wxPoint& point : connections )
624  {
625  if( aOriginalItem->HitTest( point, 1 ) )
626  {
627  test->SetFlags( TEMP_SELECTED );
628  aList.push_back( test );
629 
630  // A bus entry needs its wire & label as well
631  if( testType == SCH_BUS_WIRE_ENTRY_T || testType == SCH_BUS_BUS_ENTRY_T )
632  {
633  std::vector<wxPoint> ends = test->GetConnectionPoints();
634  wxPoint otherEnd;
635 
636  if( ends[0] == point )
637  otherEnd = ends[1];
638  else
639  otherEnd = ends[0];
640 
641  getConnectedDragItems( test, otherEnd, aList );
642  }
643  break;
644  }
645  }
646  }
647 
648  break;
649 
650  default:
651  break;
652  }
653  }
654 }
655 
656 
657 void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
658 {
659  switch( aItem->Type() )
660  {
661  case SCH_LINE_T:
662  {
663  SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
664 
665  if( aItem->HasFlag( STARTPOINT ) )
666  line->MoveStart( (wxPoint) aDelta );
667 
668  if( aItem->HasFlag( ENDPOINT ) )
669  line->MoveEnd( (wxPoint) aDelta );
670 
671  }
672  break;
673 
674  case SCH_PIN_T:
675  case SCH_FIELD_T:
676  {
677  SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
678  wxPoint delta( aDelta );
679 
680  if( parent && parent->Type() == SCH_COMPONENT_T )
681  {
682  SCH_COMPONENT* component = (SCH_COMPONENT*) aItem->GetParent();
683  TRANSFORM transform = component->GetTransform().InverseTransform();
684 
685  delta = transform.TransformCoordinate( delta );
686  }
687 
688  static_cast<SCH_ITEM*>( aItem )->Move( delta );
689 
690  // If we're moving a field with respect to its parent then it's no longer auto-placed
691  if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
692  parent->ClearFieldsAutoplaced();
693 
694  break;
695  }
696  case SCH_SHEET_PIN_T:
697  {
698  SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
699  pin->SetStoredPos( pin->GetStoredPos() + (wxPoint) aDelta );
700  pin->ConstrainOnEdge( pin->GetStoredPos() );
701  break;
702  }
703  case SCH_LABEL_T:
704  {
705  SCH_TEXT* label = static_cast<SCH_TEXT*>( aItem );
706 
707  if( m_specialCaseLabels.count( label ) )
708  {
710  SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
711  label->SetPosition( (wxPoint) currentLine.NearestPoint( info.originalLabelPos ) );
712  }
713  else
714  {
715  label->Move( (wxPoint) aDelta );
716  }
717 
718  break;
719  }
720  default:
721  static_cast<SCH_ITEM*>( aItem )->Move( (wxPoint) aDelta );
722  break;
723  }
724 
725  getView()->Hide( aItem, false );
726  aItem->SetFlags( IS_MOVED );
727 }
728 
729 
731 {
732  EE_GRID_HELPER grid( m_toolMgr);
734  bool append_undo = false;
735 
736  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
737  {
738  if( !it->IsSelected() )
739  it->ClearFlags( STARTPOINT | ENDPOINT );
740 
741  if( !selection.IsHover() && it->IsSelected() )
742  it->SetFlags( STARTPOINT | ENDPOINT );
743 
744  it->SetStoredPos( it->GetPosition() );
745 
746  if( it->Type() == SCH_SHEET_T )
747  {
748  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
749  pin->SetStoredPos( pin->GetPosition() );
750  }
751  }
752 
753  for( EDA_ITEM* item : selection )
754  {
755  if( item->Type() == SCH_LINE_T )
756  {
757  SCH_LINE* line = static_cast<SCH_LINE*>( item );
758  std::vector<int> flags{ STARTPOINT, ENDPOINT };
759  std::vector<wxPoint> pts{ line->GetStartPoint(), line->GetEndPoint() };
760 
761  for( int ii = 0; ii < 2; ++ii )
762  {
763  EDA_ITEMS drag_items{ item };
764  line->ClearFlags();
765  line->SetFlags( flags[ii] );
766  getConnectedDragItems( line, pts[ii], drag_items );
767  std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
768 
769  VECTOR2I gridpt = grid.AlignGrid( pts[ii] ) - pts[ii];
770 
771  if( gridpt != VECTOR2I( 0, 0 ) )
772  {
773  for( EDA_ITEM* dragItem : unique_items )
774  {
775  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
776  continue;
777 
778  saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
779  append_undo = true;
780 
781  moveItem( dragItem, gridpt );
782  updateItem( dragItem, true );
783  }
784  }
785  }
786  }
787  else if( item->Type() == SCH_FIELD_T )
788  {
789  VECTOR2I gridpt = grid.AlignGrid( item->GetPosition() ) - item->GetPosition();
790 
791  if( gridpt != VECTOR2I( 0, 0 ) )
792  {
793  saveCopyInUndoList( item, UNDO_REDO::CHANGED, append_undo );
794  append_undo = true;
795 
796  moveItem( item, gridpt );
797  updateItem( item, true );
798  }
799  }
800  else
801  {
802  std::vector<wxPoint> connections;
803  EDA_ITEMS drag_items{ item };
804  connections = static_cast<SCH_ITEM*>( item )->GetConnectionPoints();
805 
806  for( const wxPoint& point : connections )
807  getConnectedDragItems( static_cast<SCH_ITEM*>( item ), point, drag_items );
808 
809  std::map<VECTOR2I, int> shifts;
810  VECTOR2I most_common( 0, 0 );
811  int max_count = 0;
812 
813  for( const wxPoint& conn : connections )
814  {
815  VECTOR2I gridpt = grid.AlignGrid( conn ) - conn;
816 
817  shifts[gridpt]++;
818 
819  if( shifts[gridpt] > max_count )
820  {
821  most_common = gridpt;
822  max_count = shifts[most_common];
823  }
824  }
825 
826  if( most_common != VECTOR2I( 0, 0 ) )
827  {
828  for( EDA_ITEM* dragItem : drag_items )
829  {
830  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
831  continue;
832 
833  saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
834  append_undo = true;
835 
836  moveItem( dragItem, most_common );
837  updateItem( dragItem, true );
838  }
839  }
840  }
841  }
842 
844  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selection );
845 
848 
849  m_frame->OnModify();
850  return 0;
851 }
852 
853 
855 {
857  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
858  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
860 }
static TOOL_ACTION moveActivate
Definition: ee_actions.h:110
void Hide(VIEW_ITEM *aItem, bool aHide=true)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1475
OPT< int > GetCommandId() const
Definition: tool_event.h:466
bool SchematicCleanUp(SCH_SCREEN *aScreen=nullptr)
Performs routine schematic cleaning including breaking wire and buses and deleting identical objects ...
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
bool IsHover() const
Definition: selection.h:72
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:257
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:390
void getConnectedDragItems(SCH_ITEM *aOriginalItem, wxPoint aPoint, EDA_ITEMS &aList)
Set up handlers for various events.
wxPoint GetStartPoint() const
Definition: sch_line.h:94
bool IsSelected() const
Definition: eda_item.h:172
static TOOL_ACTION doDelete
Definition: actions.h:75
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:369
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:89
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:251
bool IsClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:181
VECTOR2I m_moveOffset
Last cursor position (needed for getModificationPoint() to avoid changes of edit reference point).
Definition: sch_move_tool.h:85
static TOOL_ACTION restartMove
Definition: ee_actions.h:195
void MoveStart(const wxPoint &aMoveVector)
Definition: sch_line.cpp:120
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:215
EE_TYPE Overlapping(const EDA_RECT &aRect)
Definition: sch_rtree.h:224
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:59
TOOL_MENU & GetToolMenu()
bool IsMotion() const
Definition: tool_event.h:316
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:141
VECTOR2I BestSnapAnchor(const VECTOR2I &aOrigin, int aLayer, SCH_ITEM *aDraggedItem)
int AddItemsToSel(const TOOL_EVENT &aEvent)
Schematic editor (Eeschema) main window.
EE_RTREE - Implements an R-tree for fast spatial and type indexing of schematic items.
Definition: sch_rtree.h:41
bool m_moveInProgress
< Flag determining if anything is being dragged right now
Definition: sch_move_tool.h:78
virtual wxPoint GetPosition() const
Definition: eda_item.h:301
static const KICAD_T MovableItems[]
Definition: ee_collectors.h:47
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
static TOOL_ACTION alignToGrid
Definition: ee_actions.h:108
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
#define TEMP_SELECTED
flag indicating that the structure has already selected
Definition: eda_item.h:114
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).
EDA_ITEMS m_dragAdditions
Used for chaining commands.
Definition: sch_move_tool.h:82
void SetCurrentCursor(KICURSOR cursor)
Set the current cursor shape for this panel.
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:214
bool HitTest(const wxPoint &aPosition, int aAccuracy=0) const override
Test if aPosition is contained within or on the bounding box of an item.
Definition: sch_line.cpp:776
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
Definition: tool_event.cpp:193
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:168
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:70
SCH_JUNCTION * AddJunction(SCH_SCREEN *aScreen, const wxPoint &aPos, bool aAppendToUndo, bool aFinal=true)
VECTOR2I BestDragOrigin(const VECTOR2I &aMousePos, int aLayer, const EE_SELECTION &aItems)
bool Init() override
Init() is called once upon a registration of the tool.
Definition: ee_tool_base.h:65
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...
virtual bool HitTest(const wxPoint &aPosition, int aAccuracy=0) const
Test if aPosition is contained within or on the bounding box of an item.
Definition: eda_item.h:274
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
wxPoint & GetStoredPos()
Definition: sch_item.h:251
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:306
virtual bool CanConnect(const SCH_ITEM *aItem) const
Definition: sch_item.h:371
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:274
void SetFlags(STATUS_FLAGS aMask)
Definition: eda_item.h:202
void TestDanglingEnds()
Test all of the connectable objects in the schematic for unused connection points.
virtual bool IsMovableFromAnchorPoint() const
Definition: sch_item.h:249
std::map< SCH_TEXT *, SPECIAL_CASE_LABEL_INFO > m_specialCaseLabels
Definition: sch_move_tool.h:95
void saveCopyInUndoList(EDA_ITEM *aItem, UNDO_REDO aType, bool aAppend=false)
Definition: ee_tool_base.h:133
bool IsDblClick(int aButtonMask=BUT_ANY) const
Definition: tool_event.cpp:187
virtual void PopTool(const std::string &actionName)
boost::optional< VECTOR2I > m_anchorPos
Definition: sch_move_tool.h:91
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:173
int AlignElements(const TOOL_EVENT &aEvent)
Align selected elements to the grid.
void ClearFieldsAutoplaced()
Set fields automatically placed flag false.
Definition: sch_item.h:451
EDA_ITEM * GetParent() const
Definition: eda_item.h:164
An interface for classes handling user events controlling the view behavior such as zooming,...
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:176
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:217
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:85
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:556
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:54
TRANSFORM & GetTransform()
Definition: sch_symbol.h:273
bool Init() override
Init() is called once upon a registration of the tool.
bool m_isDragOperation
Items (such as wires) which were added to the selection for a drag.
Definition: sch_move_tool.h:79
static TOOL_ACTION addNeededJunctions
Definition: ee_actions.h:73
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:311
void ConstrainOnEdge(wxPoint Pos)
Adjust label position to edge based on proximity to vertical or horizontal edge of the parent sheet.
wxPoint GetPosition() const override
Definition: sch_text.h:312
#define IS_MOVED
Item being moved.
Definition: eda_item.h:105
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
void SelectUnit(SCH_COMPONENT *aComponent, int aUnit)
Definition: getpart.cpp:198
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:122
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:102
void SetSnap(bool aSnap)
Definition: grid_helper.h:64
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:352
Definition: seg.h:41
#define STARTPOINT
When a line is selected, these flags indicate which.
Definition: eda_item.h:111
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:471
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
bool IsActivate() const
Definition: tool_event.h:331
void SetUseGrid(bool aSnapToGrid)
Definition: grid_helper.h:67
see class PGM_BASE
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:130
void moveItem(EDA_ITEM *aItem, const VECTOR2I &aDelta)
Find additional items for a drag operation.
static TOOL_ACTION drag
Definition: ee_actions.h:112
void RollbackSchematicFromUndo()
Performs an undo of the last edit WITHOUT logging a corresponding redo.
EE_RTREE & Items()
Definition: sch_screen.h:162
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:181
Schematic symbol object.
Definition: sch_symbol.h:79
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:149
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:203
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Definition: tools_holder.h:140
void Activate()
Run the tool.
static TOOL_ACTION move
Definition: ee_actions.h:111
#define ENDPOINT
ends. (Used to support dragging.)
Definition: eda_item.h:112
A foundation class for a tool operating on a schematic or symbol.
Definition: ee_tool_base.h:48
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:87
virtual const EDA_RECT GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:89
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:313
void PostEvent(const TOOL_EVENT &aEvent)
Put 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)
Add a menu entry to run a TOOL_ACTION on selected items.
void SetStoredPos(wxPoint aPos)
Definition: sch_item.h:252
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:196
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
#define IS_NEW
New item, just created.
Definition: eda_item.h:106
static TOOL_ACTION duplicate
Definition: actions.h:74
bool HasFlag(STATUS_FLAGS aFlag) const
Definition: eda_item.h:205
static TOOL_ACTION refreshPreview
Definition: actions.h:109
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:203
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
VECTOR2I AlignGrid(const VECTOR2I &aPoint) const
Definition: grid_helper.cpp:83
wxPoint GetEndPoint() const
Definition: sch_line.h:97