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  controls->ShowCursor( true );
138 
139  std::string tool = aEvent.GetCommandStr().get();
140  m_frame->PushTool( tool );
141 
142  if( selection.Empty() )
143  {
144  // Note that it's important to go through push/pop even when the selection is empty.
145  // This keeps other tools from having to special-case an empty move.
146  m_frame->PopTool( tool );
147  return 0;
148  }
149 
150  bool restore_state = false;
151  bool chain_commands = false;
152  TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
153  VECTOR2I prevPos;
154  int snapLayer = UNDEFINED_LAYER;
155 
156  m_cursor = controls->GetCursorPosition();
157 
158  // Main loop: keep receiving events
159  do
160  {
162  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
163  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
164 
167  || evt->IsAction( &EE_ACTIONS::move )
168  || evt->IsAction( &EE_ACTIONS::drag )
169  || evt->IsMotion()
170  || evt->IsDrag( BUT_LEFT )
171  || evt->IsAction( &ACTIONS::refreshPreview ) )
172  {
173  if( !m_moveInProgress ) // Prepare to start moving/dragging
174  {
175  SCH_ITEM* sch_item = (SCH_ITEM*) selection.Front();
176  bool appendUndo = sch_item && sch_item->IsNew();
177  bool placingNewItems = sch_item && sch_item->IsNew();
178 
179  //------------------------------------------------------------------------
180  // Setup a drag or a move
181  //
182  m_dragAdditions.clear();
183  m_specialCaseLabels.clear();
184  internalPoints.clear();
185 
186  for( SCH_ITEM* it : m_frame->GetScreen()->Items() )
187  {
188  it->ClearFlags( TEMP_SELECTED );
189 
190  if( !it->IsSelected() )
191  it->ClearFlags( STARTPOINT | ENDPOINT );
192 
193  if( !selection.IsHover() && it->IsSelected() )
194  it->SetFlags( STARTPOINT | ENDPOINT );
195  }
196 
197  if( m_isDrag )
198  {
199  EDA_ITEMS connectedDragItems;
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, connectedDragItems );
215  }
216 
217  for( EDA_ITEM* item : connectedDragItems )
218  {
219  m_dragAdditions.push_back( item->m_Uuid );
221  }
222  }
223  else
224  {
225  // Mark the edges of the block with dangling flags for a move.
226  for( EDA_ITEM* item : selection )
227  static_cast<SCH_ITEM*>( item )->GetEndPoints( internalPoints );
228 
229  for( EDA_ITEM* item : selection )
230  static_cast<SCH_ITEM*>( item )->UpdateDanglingState( internalPoints );
231  }
232  // Generic setup
233  //
234  for( EDA_ITEM* item : selection )
235  {
236  if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
237  {
238  if( snapLayer == LAYER_GRAPHICS )
239  snapLayer = LAYER_ANY;
240  else
241  snapLayer = LAYER_CONNECTABLE;
242  }
243  else
244  {
245  if( snapLayer == LAYER_CONNECTABLE )
246  snapLayer = LAYER_ANY;
247  else
248  snapLayer = LAYER_GRAPHICS;
249  }
250 
251  if( item->IsNew() )
252  {
253  if( item->HasFlag( TEMP_SELECTED ) && m_isDrag )
254  {
255  // Item was added in getConnectedDragItems
256  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::NEWITEM, appendUndo );
257  appendUndo = true;
258  }
259  else
260  {
261  // Item was added in a previous command (and saved to undo by
262  // that command)
263  }
264  }
265  else if( item->GetParent() && item->GetParent()->IsSelected() )
266  {
267  // Item will be (or has been) saved to undo by parent
268  }
269  else
270  {
271  saveCopyInUndoList( (SCH_ITEM*) item, UNDO_REDO::CHANGED, appendUndo );
272  appendUndo = true;
273  }
274 
275  SCH_ITEM* schItem = (SCH_ITEM*) item;
276  schItem->SetStoredPos( schItem->GetPosition() );
277  }
278 
279  // Set up the starting position and move/drag offset
280  //
281  m_cursor = controls->GetCursorPosition();
282 
283  if( evt->IsAction( &EE_ACTIONS::restartMove ) )
284  {
285  wxASSERT_MSG( m_anchorPos, "Should be already set from previous cmd" );
286  }
287  else if( placingNewItems )
288  {
289  m_anchorPos = selection.GetReferencePoint();
290  }
291 
292  if( m_anchorPos )
293  {
294  VECTOR2I delta = m_cursor - (*m_anchorPos);
295 
296  // Drag items to the current cursor position
297  for( EDA_ITEM* item : selection )
298  {
299  // Don't double move pins, fields, etc.
300  if( item->GetParent() && item->GetParent()->IsSelected() )
301  continue;
302 
303  moveItem( item, delta );
304  updateItem( item, false );
305  }
306 
308  }
309  // For some items, moving the cursor to anchor is not good (for instance large
310  // hierarchical sheets or symbols can have the anchor outside the view)
311  else if( selection.Size() == 1 && !sch_item->IsMovableFromAnchorPoint() )
312  {
315  }
316  else
317  {
318  if( m_frame->GetMoveWarpsCursor() )
319  {
320  // User wants to warp the mouse
321  m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
322  }
323  else
324  {
325  // User does not want to warp the mouse
327  }
328  }
329 
330  controls->SetCursorPosition( m_cursor, false );
332 
333  prevPos = m_cursor;
334  controls->SetAutoPan( true );
335  m_moveInProgress = true;
336  }
337 
338  //------------------------------------------------------------------------
339  // Follow the mouse
340  //
341  m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
342  snapLayer, selection );
343 
344  VECTOR2I delta( m_cursor - prevPos );
346 
347  m_moveOffset += delta;
348  prevPos = m_cursor;
349 
350  for( EDA_ITEM* item : selection )
351  {
352  // Don't double move pins, fields, etc.
353  if( item->GetParent() && item->GetParent()->IsSelected() )
354  continue;
355 
356  moveItem( item, delta );
357  updateItem( item, false );
358  }
359 
361  }
362  //------------------------------------------------------------------------
363  // Handle cancel
364  //
365  else if( evt->IsCancelInteractive() || evt->IsActivate() )
366  {
367  if( m_moveInProgress )
368  {
369  if( evt->IsActivate() )
370  {
371  // Allowing other tools to activate during a move runs the risk of race
372  // conditions in which we try to spool up both event loops at once.
373 
374  if( m_isDrag )
375  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel drag." ) );
376  else
377  m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel move." ) );
378 
379  evt->SetPassEvent( false );
380  continue;
381  }
382 
383  evt->SetPassEvent( false );
384  restore_state = true;
385  }
386 
387  break;
388  }
389  //------------------------------------------------------------------------
390  // Handle TOOL_ACTION special cases
391  //
392  else if( evt->Action() == TA_UNDO_REDO_PRE )
393  {
394  unselect = true;
395  break;
396  }
397  else if( evt->IsAction( &ACTIONS::doDelete ) )
398  {
399  evt->SetPassEvent();
400  // Exit on a delete; there will no longer be anything to drag.
401  break;
402  }
403  else if( evt->IsAction( &ACTIONS::duplicate ) )
404  {
405  if( selection.Front()->IsNew() )
406  {
407  // This doesn't really make sense; we'll just end up dragging a stack of
408  // objects so we ignore the duplicate and just carry on.
409  continue;
410  }
411 
412  // Move original back and exit. The duplicate will run in its own loop.
413  restore_state = true;
414  unselect = false;
415  chain_commands = true;
416  break;
417  }
418  else if( evt->IsAction( &EE_ACTIONS::rotateCW )
419  || evt->IsAction( &EE_ACTIONS::rotateCCW )
420  || evt->IsAction( &EE_ACTIONS::mirrorH )
421  || evt->IsAction( &EE_ACTIONS::mirrorV ) )
422  {
423  if( m_isDrag )
424  {
425  // These are just going to make a mess, so eat them without doing anything.
426  wxBell();
427  }
428  else
429  {
430  // Allow operations while moving
431  evt->SetPassEvent();
432  }
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()->IsJunctionNeeded( it.GetPosition(), true ) )
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, 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( flags[ii] );
838  getConnectedDragItems( line, pts[ii], drag_items );
839  std::set<EDA_ITEM*> unique_items( drag_items.begin(), drag_items.end() );
840 
841  VECTOR2I gridpt = grid.AlignGrid( pts[ii] ) - pts[ii];
842 
843  if( gridpt != VECTOR2I( 0, 0 ) )
844  {
845  for( EDA_ITEM* dragItem : unique_items )
846  {
847  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
848  continue;
849 
850  saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
851  append_undo = true;
852 
853  moveItem( dragItem, gridpt );
854  dragItem->ClearFlags( IS_MOVING );
855  updateItem( dragItem, true );
856  }
857  }
858  }
859  }
860  else if( item->Type() == SCH_FIELD_T )
861  {
862  VECTOR2I gridpt = grid.AlignGrid( item->GetPosition() ) - item->GetPosition();
863 
864  if( gridpt != VECTOR2I( 0, 0 ) )
865  {
866  saveCopyInUndoList( item, UNDO_REDO::CHANGED, append_undo );
867  append_undo = true;
868 
869  moveItem( item, gridpt );
870  updateItem( item, true );
871  item->ClearFlags( IS_MOVING );
872  }
873  }
874  else
875  {
876  std::vector<wxPoint> connections;
877  EDA_ITEMS drag_items{ item };
878  connections = static_cast<SCH_ITEM*>( item )->GetConnectionPoints();
879 
880  for( const wxPoint& point : connections )
881  getConnectedDragItems( static_cast<SCH_ITEM*>( item ), point, drag_items );
882 
883  std::map<VECTOR2I, int> shifts;
884  VECTOR2I most_common( 0, 0 );
885  int max_count = 0;
886 
887  for( const wxPoint& conn : connections )
888  {
889  VECTOR2I gridpt = grid.AlignGrid( conn ) - conn;
890 
891  shifts[gridpt]++;
892 
893  if( shifts[gridpt] > max_count )
894  {
895  most_common = gridpt;
896  max_count = shifts[most_common];
897  }
898  }
899 
900  if( most_common != VECTOR2I( 0, 0 ) )
901  {
902  for( EDA_ITEM* dragItem : drag_items )
903  {
904  if( dragItem->GetParent() && dragItem->GetParent()->IsSelected() )
905  continue;
906 
907  saveCopyInUndoList( dragItem, UNDO_REDO::CHANGED, append_undo );
908  append_undo = true;
909 
910  moveItem( dragItem, most_common );
911  dragItem->ClearFlags( IS_MOVING );
912  updateItem( dragItem, true );
913  }
914  }
915  }
916  }
917 
919  m_toolMgr->RunAction( EE_ACTIONS::addNeededJunctions, true, &selection );
920 
923 
924  m_frame->OnModify();
925  return 0;
926 }
927 
928 
930 {
932  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::move.MakeEvent() );
933  Go( &SCH_MOVE_TOOL::Main, EE_ACTIONS::drag.MakeEvent() );
935 }
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:1471
OPT< int > GetCommandId() const
Definition: tool_event.h:450
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:377
void getConnectedDragItems(SCH_ITEM *aOriginalItem, wxPoint aPoint, EDA_ITEMS &aList)
Set up handlers for various events.
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:59
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:153
TOOL_MENU & GetToolMenu()
bool IsMotion() const
Definition: tool_event.h:295
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:47
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:357
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:199
#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:233
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
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:436
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:336
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:290
wxPoint GetPosition() const override
Definition: sch_text.h:237
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:272
int Modifier(int aMask=MD_MODIFIER_MASK) const
Definition: tool_event.h:331
Definition: seg.h:40
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:455
static bool IsDrawingLineWireOrBus(const SELECTION &aSelection)
bool IsActivate() const
Definition: tool_event.h:310
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()
Definition: sch_screen.h:102
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:390
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Definition: tools_holder.h:139
void Activate()
Run the tool.
const wxPoint & GetTextPos() const
Definition: eda_text.h:254
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:87
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:238
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:236
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:197
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