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  bool isPasted = false;
297 
298  // Drag items to the current cursor position
299  for( EDA_ITEM* item : selection )
300  {
301  // Don't double move pins, fields, etc.
302  if( item->GetParent() && item->GetParent()->IsSelected() )
303  continue;
304 
305  moveItem( item, delta );
306  updateItem( item, false );
307 
308  isPasted |= ( item->GetFlags() & IS_PASTED ) != 0;
309  item->ClearFlags( IS_PASTED );
310  }
311 
312  // The first time pasted items are moved we need to store
313  // the position of the cursor so that rotate while moving
314  // works as expected (instead of around the original anchor
315  // point
316  if( isPasted )
317  selection.SetReferencePoint( m_cursor );
318 
320  }
321  // For some items, moving the cursor to anchor is not good (for instance large
322  // hierarchical sheets or symbols can have the anchor outside the view)
323  else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
324  {
327  }
328  else
329  {
330  if( m_frame->GetMoveWarpsCursor() )
331  {
332  // User wants to warp the mouse
333  m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
334  selection.SetReferencePoint( m_cursor );
335  }
336  else
337  {
338  // User does not want to warp the mouse
340  }
341  }
342 
343  controls->SetCursorPosition( m_cursor, false );
345 
346  prevPos = m_cursor;
347  controls->SetAutoPan( true );
348  m_moveInProgress = true;
349  }
350 
351  //------------------------------------------------------------------------
352  // Follow the mouse
353  //
354  m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
355  snapLayer, selection );
356 
357  VECTOR2I delta( m_cursor - prevPos );
359 
360  m_moveOffset += delta;
361  prevPos = m_cursor;
362 
363  for( EDA_ITEM* item : selection )
364  {
365  // Don't double move pins, fields, etc.
366  if( item->GetParent() && item->GetParent()->IsSelected() )
367  continue;
368 
369  moveItem( item, delta );
370  updateItem( item, false );
371  }
372 
373  if( selection.HasReferencePoint() )
374  selection.SetReferencePoint( selection.GetReferencePoint() + delta );
375 
377  }
378  //------------------------------------------------------------------------
379  // Handle cancel
380  //
381  else if( evt->IsCancelInteractive() || evt->IsActivate() )
382  {
383  if( m_moveInProgress )
384  {
385  if( evt->IsActivate() )
386  {
387  // Allowing other tools to activate during a move runs the risk of race
388  // conditions in which we try to spool up both event loops at once.
389 
390  if( m_isDrag )
391  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drag." ) );
392  else
393  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel move." ) );
394 
395  evt->SetPassEvent( false );
396  continue;
397  }
398 
399  evt->SetPassEvent( false );
400  restore_state = true;
401  }
402 
403  break;
404  }
405  //------------------------------------------------------------------------
406  // Handle TOOL_ACTION special cases
407  //
408  else if( evt->Action() == TA_UNDO_REDO_PRE )
409  {
410  unselect = true;
411  break;
412  }
413  else if( evt->IsAction( &ACTIONS::doDelete ) )
414  {
415  evt->SetPassEvent();
416  // Exit on a delete; there will no longer be anything to drag.
417  break;
418  }
419  else if( evt->IsAction( &ACTIONS::duplicate ) )
420  {
421  if( selection.Front()->IsNew() )
422  {
423  // This doesn't really make sense; we'll just end up dragging a stack of
424  // objects so we ignore the duplicate and just carry on.
425  continue;
426  }
427 
428  // Move original back and exit. The duplicate will run in its own loop.
429  restore_state = true;
430  unselect = false;
431  chain_commands = true;
432  break;
433  }
434  else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
435  {
436  if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
438  {
439  SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( selection.Front() );
440  int unit = evt->GetCommandId().get() - ID_POPUP_SCH_SELECT_UNIT_CMP;
441 
442  if( symbol )
443  {
444  m_frame->SelectUnit( symbol, unit );
446  }
447  }
448  }
449  //------------------------------------------------------------------------
450  // Handle context menu
451  //
452  else if( evt->IsClick( BUT_RIGHT ) )
453  {
455  }
456  //------------------------------------------------------------------------
457  // Handle drop
458  //
459  else if( evt->IsMouseUp( BUT_LEFT )
460  || evt->IsClick( BUT_LEFT )
461  || evt->IsDblClick( BUT_LEFT ) )
462  {
463  break; // Finish
464  }
465  else
466  {
467  evt->SetPassEvent();
468  }
469 
470  controls->SetAutoPan( m_moveInProgress );
471 
472  } while( ( evt = Wait() ) ); //Should be assignment not equality test
473 
474  controls->ForceCursorPosition( false );
475  controls->ShowCursor( false );
476  controls->SetAutoPan( false );
477 
478  if( !chain_commands )
479  m_moveOffset = { 0, 0 };
480 
481  m_anchorPos.reset();
482 
483  for( EDA_ITEM* item : selection )
484  item->ClearEditFlags();
485 
486  if( restore_state )
487  {
490  }
491  else
492  {
493  // One last update after exiting loop (for slower stuff, such as updating SCREEN's RTree).
494  for( EDA_ITEM* item : selection )
495  updateItem( item, true );
496 
497  EE_SELECTION selectionCopy( selection );
499 
500  // If we move items away from a junction, we _may_ want to add a junction there
501  // to denote the state.
502  for( const DANGLING_END_ITEM& it : internalPoints )
503  {
504  if( m_frame->GetScreen()->IsExplicitJunctionNeeded( it.GetPosition()) )
505  m_frame->AddJunction( m_frame->GetScreen(), it.GetPosition(), true, false );
506  }
507 
508  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selectionCopy );
509 
512 
513  m_frame->OnModify();
514  }
515 
516  if( unselect )
518  else
519  m_selectionTool->RebuildSelection(); // Schematic cleanup might have merged lines, etc.
520 
521  m_dragAdditions.clear();
522  m_moveInProgress = false;
523  m_frame->PopTool( tool );
524  return 0;
525 }
526 
527 
528 void SCH_MOVE_TOOL::getConnectedDragItems( SCH_ITEM* aOriginalItem, const wxPoint& aPoint,
529  EDA_ITEMS& aList )
530 {
531  EE_RTREE& items = m_frame->GetScreen()->Items();
532  EE_RTREE::EE_TYPE itemsOverlapping = items.Overlapping( aOriginalItem->GetBoundingBox() );
533  bool ptHasUnselectedJunction = false;
534 
535  for( SCH_ITEM* item : itemsOverlapping )
536  {
537  if( item->Type() == SCH_JUNCTION_T && item->IsConnected( aPoint ) && !item->IsSelected() )
538  {
539  ptHasUnselectedJunction = true;
540  break;
541  }
542  }
543 
544  for( SCH_ITEM* test : itemsOverlapping )
545  {
546  if( test == aOriginalItem || test->IsSelected() || !test->CanConnect( aOriginalItem ) )
547  continue;
548 
549  KICAD_T testType = test->Type();
550 
551  switch( testType )
552  {
553  case SCH_LINE_T:
554  {
555  // Select the connected end of wires/bus connections that don't have an unselected
556  // junction isolating them from the drag
557  if( ptHasUnselectedJunction )
558  break;
559 
560  SCH_LINE* line = static_cast<SCH_LINE*>( test );
561 
562  if( line->GetStartPoint() == aPoint )
563  {
564  if( !line->HasFlag(TEMP_SELECTED ) )
565  aList.push_back( line );
566 
568  }
569  else if( line->GetEndPoint() == aPoint )
570  {
571  if( !line->HasFlag(TEMP_SELECTED ) )
572  aList.push_back( line );
573 
574  line->SetFlags(ENDPOINT | TEMP_SELECTED );
575  }
576  else
577  {
578  break;
579  }
580 
581  // Since only one end is going to move, the movement vector of any labels attached
582  // to it is scaled by the proportion of the line length the label is from the moving
583  // end.
584  for( SCH_ITEM* item : itemsOverlapping )
585  {
586  if( item->Type() == SCH_LABEL_T )
587  {
588  SCH_TEXT* label = static_cast<SCH_TEXT*>( item );
589 
590  if( label->IsSelected() )
591  continue; // These will be moved on their own because they're selected
592 
593  if( label->HasFlag( TEMP_SELECTED ) )
594  continue;
595 
596  if( label->CanConnect( line ) && line->HitTest( label->GetPosition(), 1 ) )
597  {
598  label->SetFlags( TEMP_SELECTED );
599  aList.push_back( label );
600 
602  info.attachedLine = line;
603  info.originalLabelPos = label->GetPosition();
604  m_specialCaseLabels[label] = info;
605  }
606  }
607  }
608 
609  break;
610  }
611 
612  case SCH_SHEET_T:
613  case SCH_SYMBOL_T:
614  case SCH_JUNCTION_T:
615  if( test->IsConnected( aPoint ) )
616  {
617  // Add a new wire between the symbol or junction and the selected item so
618  // the selected item can be dragged.
619  SCH_LINE* newWire = nullptr;
620 
621  if( test->GetLayer() == LAYER_BUS_JUNCTION ||
622  aOriginalItem->GetLayer() == LAYER_BUS )
623  {
624  newWire = new SCH_LINE( aPoint, LAYER_BUS );
625  }
626  else
627  newWire = new SCH_LINE( aPoint, LAYER_WIRE );
628 
629  newWire->SetFlags( IS_NEW );
630  m_frame->AddToScreen( newWire, m_frame->GetScreen() );
631 
632  newWire->SetFlags( TEMP_SELECTED | STARTPOINT );
633  aList.push_back( newWire );
634  }
635  break;
636 
637  case SCH_NO_CONNECT_T:
638  // Select no-connects that are connected to items being moved.
639  if( !test->HasFlag( TEMP_SELECTED ) && test->IsConnected( aPoint ) )
640  {
641  aList.push_back( test );
642  test->SetFlags( TEMP_SELECTED );
643  }
644 
645  break;
646 
647  case SCH_LABEL_T:
648  case SCH_GLOBAL_LABEL_T:
649  case SCH_HIER_LABEL_T:
650  // Performance optimization:
651  if( test->HasFlag( TEMP_SELECTED ) )
652  break;
653 
654  // Select labels that are connected to a wire (or bus) being moved.
655  if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
656  {
657  SCH_TEXT* label = static_cast<SCH_TEXT*>( test );
658  SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
659  bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
660 
661  if( line->HitTest( label->GetTextPos(), 1 ) )
662  {
663  label->SetFlags( TEMP_SELECTED );
664  aList.push_back( label );
665 
666  if( oneEndFixed )
667  {
669  info.attachedLine = line;
670  info.originalLabelPos = label->GetPosition();
671  m_specialCaseLabels[ label ] = info;
672  }
673  }
674  }
675 
676  break;
677 
679  case SCH_BUS_BUS_ENTRY_T:
680  // Performance optimization:
681  if( test->HasFlag( TEMP_SELECTED ) )
682  break;
683 
684  // Select bus entries that are connected to a bus being moved.
685  if( aOriginalItem->Type() == SCH_LINE_T && test->CanConnect( aOriginalItem ) )
686  {
687  SCH_LINE* line = static_cast<SCH_LINE*>( aOriginalItem );
688  bool oneEndFixed = !line->HasFlag( STARTPOINT ) || !line->HasFlag( ENDPOINT );
689 
690  if( oneEndFixed )
691  {
692  // This is only going to end in tears, so don't go there
693  continue;
694  }
695 
696  for( wxPoint& point : test->GetConnectionPoints() )
697  {
698  if( line->HitTest( point, 1 ) )
699  {
700  test->SetFlags( TEMP_SELECTED );
701  aList.push_back( test );
702 
703  // A bus entry needs its wire & label as well
704  std::vector<wxPoint> ends = test->GetConnectionPoints();
705  wxPoint otherEnd;
706 
707  if( ends[0] == point )
708  otherEnd = ends[1];
709  else
710  otherEnd = ends[0];
711 
712  getConnectedDragItems( test, otherEnd, aList );
713 
714  // No need to test the other end of the bus entry
715  break;
716  }
717  }
718  }
719 
720  break;
721 
722  default:
723  break;
724  }
725  }
726 }
727 
728 
729 void SCH_MOVE_TOOL::moveItem( EDA_ITEM* aItem, const VECTOR2I& aDelta )
730 {
731  switch( aItem->Type() )
732  {
733  case SCH_LINE_T:
734  {
735  SCH_LINE* line = static_cast<SCH_LINE*>( aItem );
736 
737  if( aItem->HasFlag( STARTPOINT ) )
738  line->MoveStart( (wxPoint) aDelta );
739 
740  if( aItem->HasFlag( ENDPOINT ) )
741  line->MoveEnd( (wxPoint) aDelta );
742 
743  }
744  break;
745 
746  case SCH_PIN_T:
747  case SCH_FIELD_T:
748  {
749  SCH_ITEM* parent = (SCH_ITEM*) aItem->GetParent();
750  wxPoint delta( aDelta );
751 
752  if( parent && parent->Type() == SCH_SYMBOL_T )
753  {
754  SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem->GetParent();
755  TRANSFORM transform = symbol->GetTransform().InverseTransform();
756 
757  delta = transform.TransformCoordinate( delta );
758  }
759 
760  static_cast<SCH_ITEM*>( aItem )->Move( delta );
761 
762  // If we're moving a field with respect to its parent then it's no longer auto-placed
763  if( aItem->Type() == SCH_FIELD_T && parent && !parent->IsSelected() )
764  parent->ClearFieldsAutoplaced();
765 
766  break;
767  }
768  case SCH_SHEET_PIN_T:
769  {
770  SCH_SHEET_PIN* pin = (SCH_SHEET_PIN*) aItem;
771  pin->SetStoredPos( pin->GetStoredPos() + (wxPoint) aDelta );
772  pin->ConstrainOnEdge( pin->GetStoredPos() );
773  break;
774  }
775  case SCH_LABEL_T:
776  {
777  SCH_TEXT* label = static_cast<SCH_TEXT*>( aItem );
778 
779  if( m_specialCaseLabels.count( label ) )
780  {
782  SEG currentLine( info.attachedLine->GetStartPoint(), info.attachedLine->GetEndPoint() );
783  label->SetPosition( (wxPoint) currentLine.NearestPoint( info.originalLabelPos ) );
784  }
785  else
786  {
787  label->Move( (wxPoint) aDelta );
788  }
789 
790  break;
791  }
792  default:
793  static_cast<SCH_ITEM*>( aItem )->Move( (wxPoint) aDelta );
794  break;
795  }
796 
797  getView()->Hide( aItem, false );
798  aItem->SetFlags( IS_MOVING );
799 }
800 
801 
803 {
806  bool append_undo = false;
807 
808  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
809  {
810  if( !it->IsSelected() )
811  it->ClearFlags( STARTPOINT | ENDPOINT );
812 
813  if( !selection.IsHover() && it->IsSelected() )
814  it->SetFlags( STARTPOINT | ENDPOINT );
815 
816  it->SetStoredPos( it->GetPosition() );
817 
818  if( it->Type() == SCH_SHEET_T )
819  {
820  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( it )->GetPins() )
821  pin->SetStoredPos( pin->GetPosition() );
822  }
823  }
824 
825  for( EDA_ITEM* item : selection )
826  {
827  if( item->Type() == SCH_LINE_T )
828  {
829  SCH_LINE* line = static_cast<SCH_LINE*>( item );
830  std::vector<int> flags{ STARTPOINT, ENDPOINT };
831  std::vector<wxPoint> pts{ line->GetStartPoint(), line->GetEndPoint() };
832 
833  for( int ii = 0; ii < 2; ++ii )
834  {
835  EDA_ITEMS drag_items{ item };
836  line->ClearFlags();
837  line->SetFlags( SELECTED );
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:112
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: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:198
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:82
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: getpart.cpp:203
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: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:110
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: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:831
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...
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
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: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:177
#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
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
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:259
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
void moveItem(EDA_ITEM *aItem, const VECTOR2I &aDelta)
Find additional items for a drag operation.
static TOOL_ACTION drag
Definition: ee_actions.h:114
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
constexpr int delta
static TOOL_ACTION move
Definition: ee_actions.h:113
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
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:79
wxPoint GetEndPoint() const
Definition: sch_line.h:93