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;
91 
92  m_anchorPos.reset();
93 
94  if( aEvent.IsAction( &EE_ACTIONS::move ) )
95  m_isDrag = false;
96  else if( aEvent.IsAction( &EE_ACTIONS::drag ) )
97  m_isDrag = true;
98  else if( aEvent.IsAction( &EE_ACTIONS::moveActivate ) )
100  else
101  return 0;
102 
103  if( m_moveInProgress )
104  {
105  if( m_isDrag != wasDragging )
106  {
108 
109  if( sel && !sel->IsNew() )
110  {
111  // Reset the selected items so we can start again with the current m_isDrag
112  // state.
116  m_moveInProgress = false;
117  controls->SetAutoPan( false );
118 
119  // And give it a kick so it doesn't have to wait for the first mouse movement
120  // to refresh.
122  }
123  }
124 
125  return 0;
126  }
127 
128  // Be sure that there is at least one item that we can move. If there's no selection try
129  // looking for the stuff under mouse cursor (i.e. Kicad old-style hover selection).
131  bool unselect = selection.IsHover();
132 
133  // Keep an original copy of the starting points for cleanup after the move
134  std::vector<DANGLING_END_ITEM> internalPoints;
135 
136  Activate();
137  // Must be done after Activate() so that it gets set into the correct context
138  controls->ShowCursor( true );
139 
140  std::string tool = aEvent.GetCommandStr().get();
141  m_frame->PushTool( tool );
142 
143  if( selection.Empty() )
144  {
145  // Note that it's important to go through push/pop even when the selection is empty.
146  // This keeps other tools from having to special-case an empty move.
147  m_frame->PopTool( tool );
148  return 0;
149  }
150 
151  bool restore_state = false;
152  bool chain_commands = false;
153  TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
154  VECTOR2I prevPos;
155  int snapLayer = UNDEFINED_LAYER;
156 
157  m_cursor = controls->GetCursorPosition();
158 
159  // Main loop: keep receiving events
160  do
161  {
163  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
164  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
165 
168  || evt->IsAction( &EE_ACTIONS::move )
169  || evt->IsAction( &EE_ACTIONS::drag )
170  || evt->IsMotion()
171  || evt->IsDrag( BUT_LEFT )
172  || evt->IsAction( &ACTIONS::refreshPreview ) )
173  {
174  if( !m_moveInProgress ) // Prepare to start moving/dragging
175  {
176  SCH_ITEM* sch_item = (SCH_ITEM*) selection.Front();
177  bool appendUndo = sch_item && sch_item->IsNew();
178  bool placingNewItems = sch_item && sch_item->IsNew();
179 
180  //------------------------------------------------------------------------
181  // Setup a drag or a move
182  //
183  m_dragAdditions.clear();
184  m_specialCaseLabels.clear();
185  internalPoints.clear();
186 
187  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
188  {
189  it->ClearFlags( TEMP_SELECTED );
190 
191  if( !it->IsSelected() )
192  it->ClearFlags( STARTPOINT | ENDPOINT );
193 
194  if( !selection.IsHover() && it->IsSelected() )
195  it->SetFlags( STARTPOINT | ENDPOINT );
196  }
197 
198  if( m_isDrag )
199  {
200  EDA_ITEMS connectedDragItems;
201 
202  // Add connections to the selection for a drag.
203  //
204  for( EDA_ITEM* edaItem : selection )
205  {
206  SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
207  std::vector<wxPoint> connections;
208 
209  if( item->Type() == SCH_LINE_T )
210  static_cast<SCH_LINE*>( item )->GetSelectedPoints( connections );
211  else
212  connections = item->GetConnectionPoints();
213 
214  for( wxPoint point : connections )
215  getConnectedDragItems( item, point, connectedDragItems );
216  }
217 
218  for( EDA_ITEM* item : connectedDragItems )
219  {
220  m_dragAdditions.push_back( item->m_Uuid );
222  }
223  }
224  else
225  {
226  // Mark the edges of the block with dangling flags for a move.
227  for( EDA_ITEM* item : selection )
228  static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
229 
230  for( EDA_ITEM* item : selection )
231  static_cast<SCH_ITEM*>( item )->UpdateDanglingState( internalPoints );
232  }
233  // Generic setup
234  //
235  for( EDA_ITEM* item : selection )
236  {
237  if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
238  {
239  if( snapLayer == LAYER_GRAPHICS )
240  snapLayer = LAYER_ANY;
241  else
242  snapLayer = LAYER_CONNECTABLE;
243  }
244  else
245  {
246  if( snapLayer == LAYER_CONNECTABLE )
247  snapLayer = LAYER_ANY;
248  else
249  snapLayer = LAYER_GRAPHICS;
250  }
251 
252  if( item->IsNew() )
253  {
254  if( item->HasFlag( TEMP_SELECTED ) && m_isDrag )
255  {
256  // Item was added in getConnectedDragItems
257  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
258  appendUndo = true;
259  }
260  else
261  {
262  // Item was added in a previous command (and saved to undo by
263  // that command)
264  }
265  }
266  else if( item->GetParent() && item->GetParent()->IsSelected() )
267  {
268  // Item will be (or has been) saved to undo by parent
269  }
270  else
271  {
272  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::CHANGED, appendUndo );
273  appendUndo = true;
274  }
275 
276  SCH_ITEM* schItem = (SCH_ITEM*) item;
277  schItem->SetStoredPos( schItem->GetPosition() );
278  }
279 
280  // Set up the starting position and move/drag offset
281  //
282  m_cursor = controls->GetCursorPosition();
283 
284  if( evt->IsAction( &EE_ACTIONS::restartMove ) )
285  {
286  wxASSERT_MSG( m_anchorPos, "Should be already set from previous cmd" );
287  }
288  else if( placingNewItems )
289  {
290  m_anchorPos = selection.GetReferencePoint();
291  }
292 
293  if( m_anchorPos )
294  {
295  VECTOR2I delta = m_cursor - (*m_anchorPos);
296 
297  // Drag items to the current cursor position
298  for( EDA_ITEM* item : selection )
299  {
300  // Don't double move pins, fields, etc.
301  if( item->GetParent() && item->GetParent()->IsSelected() )
302  continue;
303 
304  moveItem( item, delta );
305  updateItem( item, false );
306  }
307 
309  }
310  // For some items, moving the cursor to anchor is not good (for instance large
311  // hierarchical sheets or symbols can have the anchor outside the view)
312  else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
313  {
316  }
317  else
318  {
319  if( m_frame->GetMoveWarpsCursor() )
320  {
321  // User wants to warp the mouse
322  m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
323  }
324  else
325  {
326  // User does not want to warp the mouse
328  }
329  }
330 
331  controls->SetCursorPosition( m_cursor, false );
333 
334  prevPos = m_cursor;
335  controls->SetAutoPan( true );
336  m_moveInProgress = true;
337  }
338 
339  //------------------------------------------------------------------------
340  // Follow the mouse
341  //
342  m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
343  snapLayer, selection );
344 
345  VECTOR2I delta( m_cursor - prevPos );
347 
348  m_moveOffset += delta;
349  prevPos = m_cursor;
350 
351  for( EDA_ITEM* item : selection )
352  {
353  // Don't double move pins, fields, etc.
354  if( item->GetParent() && item->GetParent()->IsSelected() )
355  continue;
356 
357  moveItem( item, delta );
358  updateItem( item, false );
359  }
360 
362  }
363  //------------------------------------------------------------------------
364  // Handle cancel
365  //
366  else if( evt->IsCancelInteractive() || evt->IsActivate() )
367  {
368  if( m_moveInProgress )
369  {
370  if( evt->IsActivate() )
371  {
372  // Allowing other tools to activate during a move runs the risk of race
373  // conditions in which we try to spool up both event loops at once.
374 
375  if( m_isDrag )
376  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drag." ) );
377  else
378  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel move." ) );
379 
380  evt->SetPassEvent( false );
381  continue;
382  }
383 
384  evt->SetPassEvent( false );
385  restore_state = true;
386  }
387 
388  break;
389  }
390  //------------------------------------------------------------------------
391  // Handle TOOL_ACTION special cases
392  //
393  else if( evt->Action() == TA_UNDO_REDO_PRE )
394  {
395  unselect = true;
396  break;
397  }
398  else if( evt->IsAction( &ACTIONS::doDelete ) )
399  {
400  evt->SetPassEvent();
401  // Exit on a delete; there will no longer be anything to drag.
402  break;
403  }
404  else if( evt->IsAction( &ACTIONS::duplicate ) )
405  {
406  if( selection.Front()->IsNew() )
407  {
408  // This doesn't really make sense; we'll just end up dragging a stack of
409  // objects so we ignore the duplicate and just carry on.
410  continue;
411  }
412 
413  // Move original back and exit. The duplicate will run in its own loop.
414  restore_state = true;
415  unselect = false;
416  chain_commands = true;
417  break;
418  }
419  else if( evt->IsAction( &EE_ACTIONS::rotateCW )
420  || evt->IsAction( &EE_ACTIONS::rotateCCW )
421  || evt->IsAction( &EE_ACTIONS::mirrorH )
422  || evt->IsAction( &EE_ACTIONS::mirrorV ) )
423  {
424  if( m_isDrag )
425  {
426  // These are just going to make a mess, so eat them without doing anything.
427  wxBell();
428  }
429  else
430  {
431  // Allow operations while moving
432  evt->SetPassEvent();
433  }
434  }
435  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
436  {
437  if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
439  {
440  SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
441  int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
442 
443  if( symbol )
444  {
445  m_frame->SelectUnit( symbol, unit );
447  }
448  }
449  }
450  //------------------------------------------------------------------------
451  // Handle context menu
452  //
453  else if( evt->IsClick( BUT_RIGHT ) )
454  {
456  }
457  //------------------------------------------------------------------------
458  // Handle drop
459  //
460  else if( evt->IsMouseUp( BUT_LEFT )
461  || evt->IsClick( BUT_LEFT )
462  || evt->IsDblClick( BUT_LEFT ) )
463  {
464  break; // Finish
465  }
466  else
467  {
468  evt->SetPassEvent();
469  }
470 
471  controls->SetAutoPan( m_moveInProgress );
472 
473  } while( ( evt = Wait() ) ); //Should be assignment not equality test
474 
475  controls->ForceCursorPosition( false );
476  controls->ShowCursor( false );
477  controls->SetAutoPan( false );
478 
479  if( !chain_commands )
480  m_moveOffset = { 0, 0 };
481 
482  m_anchorPos.reset();
483 
484  for( EDA_ITEM* item : selection )
485  item->ClearEditFlags();
486 
487  if( restore_state )
488  {
491  }
492  else
493  {
494  // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
495  for( EDA_ITEM* item : selection )
496  updateItem( item, true );
497 
498  EE_SELECTION selectionCopy( selection );
500 
501  // If we move items away from a junction, we _may_ want to add a junction there
502  // to denote the state.
503  for( const DANGLING_END_ITEM& it : internalPoints )
504  {
505  if( m_frame->GetScreen()->IsJunctionNeeded( it.GetPosition(), true ) )
506  m_frame->AddJunction( m_frame->GetScreen(), it.GetPosition(), true, false );
507  }
508 
509  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selectionCopy );
510 
513 
514  m_frame->OnModify();
515  }
516 
517  if( unselect )
519  else
520  m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
521 
522  m_dragAdditions.clear();
523  m_moveInProgress = false;
524  m_frame->PopTool( tool );
525  return 0;
526 }
527 
528 
529 void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const wxPoint& aPoint,
530  EDA_ITEMS& aList )
531 {
532  EE_RTREE& items = m_frame->GetScreen()->Items();
533  EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
534  bool ptHasUnselectedJunction = false;
535 
536  for( SCH_ITEM* item : itemsOverlapping )
537  {
538  if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
539  {
540  ptHasUnselectedJunction = true;
541  break;
542  }
543  }
544 
545  for( SCH_ITEM* test : itemsOverlapping )
546  {
547  if( test == aOriginalItem || test->IsSelected() || !test->CanConnect( aOriginalItem ) )
548  continue;
549 
550  KICAD_T testType = test->Type();
551 
552  switch( testType )
553  {
554  case SCH_LINE_T:
555  {
556  // Select the connected end of wires/bus connections that don't have an unselected
557  // junction isolating them from the drag
558  if( ptHasUnselectedJunction )
559  break;
560 
561  SCH_LINE* line = static_cast<SCH_LINE*>( test );
562 
563  if( line->GetStartPoint() == aPoint )
564  {
565  if( !line->HasFlag(TEMP_SELECTED ) )
566  aList.push_back( line );
567 
569  }
570  else if( line->GetEndPoint() == aPoint )
571  {
572  if( !line->HasFlag(TEMP_SELECTED ) )
573  aList.push_back( line );
574 
575  line->SetFlags(ENDPOINT | TEMP_SELECTED );
576  }
577  else
578  {
579  break;
580  }
581 
582  // Since only one end is going to move, the movement vector of any labels attached
583  // to it is scaled by the proportion of the line length the label is from the moving
584  // end.
585  for( SCH_ITEM* item : itemsOverlapping )
586  {
587  if( item->Type() == SCH_LABEL_T )
588  {
589  SCH_TEXT* label = static_cast<SCH_TEXT*>( item );
590 
591  if( label->IsSelected() )
592  continue; // These will be moved on their own because they're selected
593 
594  if( label->HasFlag( TEMP_SELECTED ) )
595  continue;
596 
597  if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) )
598  {
599  label->SetFlags( TEMP_SELECTED );
600  aList.push_back( label );
601 
603  info.attachedLine = line;
604  info.originalLabelPos = label->GetPosition();
605  m_specialCaseLabels[label] = info;
606  }
607  }
608  }
609 
610  break;
611  }
612 
613  case SCH_SHEET_T:
614  case SCH_SYMBOL_T:
615  case SCH_JUNCTION_T:
616  if( test->IsConnected( aPoint ) )
617  {
618  // Add a new wire between the symbol or junction and the selected item so
619  // the selected item can be dragged.
620  SCH_LINE* newWire = nullptr;
621 
622  if( test->GetLayer() == LAYER_BUS_JUNCTION ||
623  aOriginalItem->GetLayer() == LAYER_BUS )
624  {
625  newWire = new SCH_LINE( aPoint, LAYER_BUS );
626  }
627  else
628  newWire = new SCH_LINE( aPoint, LAYER_WIRE );
629 
630  newWire->SetFlags( IS_NEW );
631  m_frame->AddToScreen( newWire, m_frame->GetScreen() );
632 
633  newWire->SetFlags( TEMP_SELECTED | STARTPOINT );
634  aList.push_back( newWire );
635  }
636  break;
637 
638  case SCH_NO_CONNECT_T:
639  // Select no-connects that are connected to items being moved.
640  if( !test->HasFlag( TEMP_SELECTED ) && test->IsConnected( aPoint ) )
641  {
642  aList.push_back( test );
643  test->SetFlags( TEMP_SELECTED );
644  }
645 
646  break;
647 
648  case SCH_LABEL_T:
649  case SCH_GLOBAL_LABEL_T:
650  case SCH_HIER_LABEL_T:
651  // Performance optimization:
652  if( test->HasFlag( TEMP_SELECTED ) )
653  break;
654 
655  // Select labels that are connected to a wire (or bus) being moved.
656  if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
657  {
658  SCH_TEXT* label = static_cast<SCH_TEXT*>( test );
659  SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
660  bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
661 
662  if( line->HitTest( label->GetTextPos(), 1 ) )
663  {
664  label->SetFlags( TEMP_SELECTED );
665  aList.push_back( label );
666 
667  if( oneEndFixed )
668  {
670  info.attachedLine = line;
671  info.originalLabelPos = label->GetPosition();
672  m_specialCaseLabels[ label ] = info;
673  }
674  }
675  }
676 
677  break;
678 
680  case SCH_BUS_BUS_ENTRY_T:
681  // Performance optimization:
682  if( test->HasFlag( TEMP_SELECTED ) )
683  break;
684 
685  // Select bus entries that are connected to a bus being moved.
686  if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
687  {
688  SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
689  bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
690 
691  if( oneEndFixed )
692  {
693  // This is only going to end in tears, so don't go there
694  continue;
695  }
696 
697  for( wxPoint& point : test->GetConnectionPoints() )
698  {
699  if( line->HitTest( point, 1 ) )
700  {
701  test->SetFlags( TEMP_SELECTED );
702  aList.push_back( test );
703 
704  // A bus entry needs its wire & label as well
705  std::vector<wxPoint> ends = test->GetConnectionPoints();
706  wxPoint otherEnd;
707 
708  if( ends[0] == point )
709  otherEnd = ends[1];
710  else
711  otherEnd = ends[0];
712 
713  getConnectedDragItems( test, otherEnd, aList );
714 
715  // No need to test the other end of the bus entry
716  break;
717  }
718  }
719  }
720 
721  break;
722 
723  default:
724  break;
725  }
726  }
727 }
728 
729 
730 void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
731 {
732  switch( aItem->Type() )
733  {
734  case SCH_LINE_T:
735  {
736  SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
737 
738  if( aItem->HasFlag( STARTPOINT ) )
739  line->MoveStart( (wxPoint) aDelta );
740 
741  if( aItem->HasFlag( ENDPOINT ) )
742  line->MoveEnd( (wxPoint) aDelta );
743 
744  }
745  break;
746 
747  case SCH_PIN_T:
748  case SCH_FIELD_T:
749  {
750  SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
751  wxPoint delta( aDelta );
752 
753  if( parent && parent->Type() == SCH_SYMBOL_T )
754  {
755  SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem->GetParent();
756  TRANSFORM transform = symbol->GetTransform().InverseTransform();
757 
758  delta = transform.TransformCoordinate( delta );
759  }
760 
761  static_cast<SCH_ITEM*>( aItem )->Move( delta );
762 
763  // If we're moving a field with respect to its parent then it's no longer auto-placed
764  if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
765  parent->ClearFieldsAutoplaced();
766 
767  break;
768  }
769  case SCH_SHEET_PIN_T:
770  {
771  SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
772  pin->SetStoredPos( pin->GetStoredPos() + (wxPoint) aDelta );
773  pin->ConstrainOnEdge( pin->GetStoredPos() );
774  break;
775  }
776  case SCH_LABEL_T:
777  {
778  SCH_TEXT* label = static_cast<SCH_TEXT*>( aItem );
779 
780  if( m_specialCaseLabels.count( label ) )
781  {
783  SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
784  label->SetPosition( (wxPoint) currentLine.NearestPoint( info.originalLabelPos ) );
785  }
786  else
787  {
788  label->Move( (wxPoint) aDelta );
789  }
790 
791  break;
792  }
793  default:
794  static_cast<SCH_ITEM*>( aItem )->Move( (wxPoint) aDelta );
795  break;
796  }
797 
798  getView()->Hide( aItem, false );
799  aItem->SetFlags( IS_MOVING );
800 }
801 
802 
804 {
807  bool append_undo = false;
808 
809  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
810  {
811  if( !it->IsSelected() )
812  it->ClearFlags( STARTPOINT | ENDPOINT );
813 
814  if( !selection.IsHover() && it->IsSelected() )
815  it->SetFlags( STARTPOINT | ENDPOINT );
816 
817  it->SetStoredPos( it->GetPosition() );
818 
819  if( it->Type() == SCH_SHEET_T )
820  {
821  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
822  pin->SetStoredPos( pin->GetPosition() );
823  }
824  }
825 
826  for( EDA_ITEM* item : selection )
827  {
828  if( item->Type() == SCH_LINE_T )
829  {
830  SCH_LINE* line = static_cast<SCH_LINE*>( item );
831  std::vector<int> flags{ STARTPOINT, ENDPOINT };
832  std::vector<wxPoint> pts{ line->GetStartPoint(), line->GetEndPoint() };
833 
834  for( int ii = 0; ii < 2; ++ii )
835  {
836  EDA_ITEMS drag_items{ item };
837  line->ClearFlags();
838  line->SetFlags( flags[ii] );
839  getConnectedDragItems( line, pts[ii], drag_items );
840  std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
841 
842  VECTOR2I gridpt = grid.AlignGrid( pts[ii] ) - pts[ii];
843 
844  if( gridpt != VECTOR2I( 0, 0 ) )
845  {
846  for( EDA_ITEM* dragItem : unique_items )
847  {
848  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
849  continue;
850 
851  saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
852  append_undo = true;
853 
854  moveItem( dragItem, gridpt );
855  dragItem->ClearFlags( IS_MOVING );
856  updateItem( dragItem, true );
857  }
858  }
859  }
860  }
861  else if( item->Type() == SCH_FIELD_T )
862  {
863  VECTOR2I gridpt = grid.AlignGrid( item->GetPosition() ) - item->GetPosition();
864 
865  if( gridpt != VECTOR2I( 0, 0 ) )
866  {
867  saveCopyInUndoList( item, UNDO_REDO::CHANGED, append_undo );
868  append_undo = true;
869 
870  moveItem( item, gridpt );
871  updateItem( item, true );
872  item->ClearFlags( IS_MOVING );
873  }
874  }
875  else
876  {
877  std::vector<wxPoint> connections;
878  EDA_ITEMS drag_items{ item };
879  connections = static_cast<SCH_ITEM*>( item )->GetConnectionPoints();
880 
881  for( const wxPoint& point : connections )
882  getConnectedDragItems( static_cast<SCH_ITEM*>( item ), point, drag_items );
883 
884  std::map<VECTOR2I, int> shifts;
885  VECTOR2I most_common( 0, 0 );
886  int max_count = 0;
887 
888  for( const wxPoint& conn : connections )
889  {
890  VECTOR2I gridpt = grid.AlignGrid( conn ) - conn;
891 
892  shifts[gridpt]++;
893 
894  if( shifts[gridpt] > max_count )
895  {
896  most_common = gridpt;
897  max_count = shifts[most_common];
898  }
899  }
900 
901  if( most_common != VECTOR2I( 0, 0 ) )
902  {
903  for( EDA_ITEM* dragItem : drag_items )
904  {
905  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
906  continue;
907 
908  saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
909  append_undo = true;
910 
911  moveItem( dragItem, most_common );
912  dragItem->ClearFlags( IS_MOVING );
913  updateItem( dragItem, true );
914  }
915  }
916  }
917  }
918 
920  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selection );
921 
924 
925  m_frame->OnModify();
926  return 0;
927 }
928 
929 
931 {
933  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
934  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
936 }
static TOOL_ACTION moveActivate
Definition: ee_actions.h:109
void Hide(VIEW_ITEM *aItem, bool aHide=true)
Temporarily hide the item in the view (e.g.
Definition: view.cpp:1485
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:73
#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:373
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:123
#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: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: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:85
static TOOL_ACTION restartMove
Definition: ee_actions.h:194
void MoveStart(const wxPoint &aMoveVector)
Definition: sch_line.cpp:121
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:82
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:156
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:153
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: getpart.cpp:198
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:252
static const KICAD_T MovableItems[]
Definition: ee_collectors.h:44
static TOOL_ACTION mirrorH
Definition: ee_actions.h:116
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
static TOOL_ACTION alignToGrid
Definition: ee_actions.h:107
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
TRANSFORM & GetTransform()
Definition: sch_symbol.h:231
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:204
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:780
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
static TOOL_ACTION mirrorV
Definition: ee_actions.h:115
static TOOL_ACTION rotateCW
Definition: ee_actions.h:113
bool IsNew() const
Definition: eda_item.h:119
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 rotateCCW
Definition: ee_actions.h:114
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:285
virtual bool CanConnect(const SCH_ITEM *aItem) const
Definition: sch_item.h:353
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_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:229
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: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:91
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:432
EDA_ITEM * GetParent() const
Definition: eda_item.h:115
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:154
EE_SELECTION_TOOL * m_selectionTool
Definition: ee_tool_base.h:177
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:207
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:507
bool DisableGridSnapping() const
Definition: tool_event.h:341
static TOOL_ACTION clearSelection
Clears the current selection.
Definition: ee_actions.h:53
bool Init() override
Init() is called once upon a registration of the tool.
static TOOL_ACTION addNeededJunctions
Definition: ee_actions.h:72
bool IsMouseUp(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:295
wxPoint GetPosition() const override
Definition: sch_text.h:239
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:97
EE_TYPE Overlapping(const EDA_RECT &aRect) const
Definition: sch_rtree.h:221
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
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:268
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:336
Definition: seg.h:40
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:131
void moveItem(EDA_ITEM *aItem, const VECTOR2I &aDelta)
Find additional items for a drag operation.
static TOOL_ACTION drag
Definition: ee_actions.h:111
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:178
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
bool IsJunctionNeeded(const wxPoint &aPosition, bool aNew=false) const
Test if a junction is required for the items at aPosition on the screen.
Definition: sch_screen.cpp:401
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:247
constexpr int delta
static TOOL_ACTION move
Definition: ee_actions.h:110
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:83
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:240
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:232
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:193
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:144
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
bool m_isDrag
Items (such as wires) which were added to the selection for a drag.
Definition: sch_move_tool.h:79
wxPoint GetEndPoint() const
Definition: sch_line.h:93